# HG changeset patch # User ymh # Date 1323393677 -3600 # Node ID ea562f37d7a5a9fa439ec7e69604ae12f43fbcc6 # Parent 80a400379dd3af7414ee5916a1e51f5ace4b6476# Parent 87bf6ec8af90d455f30eea6d1cb4ebcdf76ac353 Merge diff -r 87bf6ec8af90 -r ea562f37d7a5 .hgignore --- a/.hgignore Thu Dec 08 23:48:55 2011 +0100 +++ b/.hgignore Fri Dec 09 02:21:17 2011 +0100 @@ -1,2 +1,3 @@ syntax: regexp ^\.settings$ +.*\.orig$ diff -r 87bf6ec8af90 -r ea562f37d7a5 Controller/WikiTagController.php --- a/Controller/WikiTagController.php Thu Dec 08 23:48:55 2011 +0100 +++ b/Controller/WikiTagController.php Fri Dec 09 02:21:17 2011 +0100 @@ -49,7 +49,7 @@ * @param unknown_type $tags_list * @return \Symfony\Bundle\FrameworkBundle\Controller\Response */ - public function addJavascriptAction($tags_list=false) + public function addJavascriptAction($tags_list=false, $profile_name=null) { $cats = $this->getDoctrine()->getRepository('WikiTagBundle:Category')->findOrderedCategories(); // $cats is {"Label":"Créateur"},{"Label":"Datation"},... @@ -61,7 +61,13 @@ } // ... so we create is json like {"":""},{"Créateur":"Créateur"},{"Datation":"Datation"},... $categories = json_encode($ar); - return $this->render('WikiTagBundle:WikiTag:javascript.html.twig', array('categories' => $categories, 'tags_list' => $tags_list)); + // Management of profiles for the list of displayed columns and reorder tag button + $profile_array = $this->container->getParameter("wiki_tag.document_list_profile"); + $columns_array = null; + if($profile_array!=null && $profile_name!=null && $profile_name!=""){ + $columns_array = $profile_array[$profile_name]; + } + return $this->render('WikiTagBundle:WikiTag:javascript.html.twig', array('categories' => $categories, 'tags_list' => $tags_list, 'columns' => $columns_array)); } /** @@ -100,7 +106,7 @@ } $ordered_tags = $this->getDoctrine()->getRepository('WikiTagBundle:DocumentTag')->findOrderedTagsForDoc($id_doc); - return $this->render('WikiTagBundle:WikiTag:documentTags.html.twig', array('ordered_tags' => $ordered_tags, 'doc_id' => $id_doc, 'profile_name' => $profile_name, 'columns' => $columns_array)); + return $this->render('WikiTagBundle:WikiTag:documentTags.html.twig', array('ordered_tags' => $ordered_tags, 'doc_id' => $id_doc, 'columns' => $columns_array)); } /** @@ -193,7 +199,12 @@ return new Response(json_encode(array('error' => 'duplicate_tag', 'message' => sprintf("Le tag %s existe déjà pour cette fiche.", $tag_label))),400); } // We create the new tag or get the already existing tag. $tag, $revision_id, $created - $ar = $this->getDoctrine()->getRepository('WikiTagBundle:Tag')->getOrCreateTag($tag_label); + try { + $ar = $this->getDoctrine()->getRepository('WikiTagBundle:Tag')->getOrCreateTag($tag_label); + } + catch (\Exception $e){ + return new Response(json_encode(array('error' => 'wikipedia_request_failed', 'message' => $e->getMessage())),400); + } $tag = $ar[0]; $revision_id = $ar[1]; $created = $ar[2]; @@ -275,7 +286,13 @@ return new Response(json_encode(array('error' => 'duplicate_tag', 'message' => sprintf("Le tag %s existe déjà pour cette fiche.", $tag_label))),400); } // returns array($tag, $revision_id, $created) - $ar = $this->getDoctrine()->getRepository('WikiTagBundle:Tag')->getOrCreateTag($tag_label);// tag, revision_id, created = get_or_create_tag(tag_label) + try { + $ar = $this->getDoctrine()->getRepository('WikiTagBundle:Tag')->getOrCreateTag($tag_label); + } + catch (\Exception $e){ + return new Response(json_encode(array('error' => 'wikipedia_request_failed', 'message' => $e->getMessage())),400); + } + $tag = $ar[0]; $revision_id = $ar[1]; $created = $ar[2]; @@ -414,17 +431,11 @@ * Generic render partial template * @param unknown_type $id_doc */ - public function renderDocTags($id_doc, $profile_name) + public function renderDocTags($id_doc) { - // Management of profiles for the list of displayed columns and reorder tag button - $profile_array = $this->container->getParameter("wiki_tag.document_list_profile"); - $columns_array = null; - if($profile_array!=null && $profile_name!=null && $profile_name!=""){ - $columns_array = $profile_array[$profile_name]; - } // Get tags and render the table $ordered_tags = $this->getDoctrine()->getRepository('WikiTagBundle:DocumentTag')->findOrderedTagsForDoc($id_doc); - return $this->render('WikiTagBundle:WikiTag:tagTable.html.twig', array('ordered_tags' => $ordered_tags, 'doc_id' => $id_doc, 'columns' => $columns_array)); + return $this->render('WikiTagBundle:WikiTag:tagTable.html.twig', array('ordered_tags' => $ordered_tags, 'doc_id' => $id_doc)); } @@ -537,8 +548,12 @@ $id_moved_tag = $this->getRequest()->request->get('id'); $moved_tag = $this->getDoctrine()->getRepository('WikiTagBundle:Tag')->findOneBy(array('id' => $id_moved_tag)); // We update the tag label and its wikipedia info with the new label. - $this->updateTagWithNewLabel($moved_tag, $tag_label); - + try { + $this->updateTagWithNewLabel($moved_tag, $tag_label); + } + catch (\Exception $e){ + return new Response(json_encode(array('error' => 'wikipedia_request_failed', 'message' => $e->getMessage())),400); + } // We render the tag list. $num_page = $this->getRequest()->request->get('num_page'); $nb_by_page = $this->getRequest()->request->get('nb_by_page'); @@ -558,7 +573,12 @@ $id_moved_tag = $this->getRequest()->request->get('tag_id'); $moved_tag = $this->getDoctrine()->getRepository('WikiTagBundle:Tag')->findOneBy(array('id' => $id_moved_tag)); // We update the tag label and its wikipedia info with the original label. - return $this->updateTagWithNewLabel($moved_tag, $moved_tag->getOriginalLabel()); + try { + $this->updateTagWithNewLabel($moved_tag, $moved_tag->getOriginalLabel()); + } + catch (\Exception $e){ + return new Response(json_encode(array('error' => 'wikipedia_request_failed', 'message' => $e->getMessage())),400); + } // We render the tag list. $num_page = $this->getRequest()->request->get('num_page'); @@ -578,7 +598,12 @@ if($label!=$tag->getLabel()){ // We get the Wikipedia informations for the sent label $tag_label_normalized = WikiTagUtils::normalizeTag($label); - $wp_response = WikiTagUtils::getWikipediaInfo($tag_label_normalized); + try { + $wp_response = WikiTagUtils::getWikipediaInfo($tag_label_normalized); + } + catch (\Exception $e){ + throw new \Exception($e->getMessage()); + } $tag->setWikipediaInfo($wp_response); // Save datas. $em = $this->getDoctrine()->getEntityManager(); @@ -607,8 +632,6 @@ return $this->render('WikiTagBundle:WikiTag:TagListTable.html.twig', array('tags' => $tags, 'searched' => $searched, 'nb_by_page' => $nb_by_page, 'sort' => $sort, 'num_page' => $num_page, 'reverse_sort' => $reverse_sort, 'route_for_documents_by_tag' => $this->container->getParameter("wiki_tag.route_for_documents_by_tag"))); - - return $this->getAllTags(); } @@ -622,6 +645,7 @@ if($searched==NULL){ $searched = ""; } + $searched = urldecode($searched); // Number of tags per page if($nb_by_page==NULL){ $nb_by_page = 50; @@ -630,7 +654,6 @@ if($num_page==NULL){ $num_page = 1; } - // We build the query. $qb = $this->getDoctrine()->getEntityManager()->createQueryBuilder(); $qb->select('t', 'COUNT( dt.id ) AS nb_docs'); @@ -675,6 +698,7 @@ $qb->addOrderBy('t.normalizedLabel','ASC'); $qb->addOrderBy('t.label','ASC'); $reverse_sort = "labd"; + break; case "nbd": $qb->addOrderBy('nb_docs','DESC'); $qb->addOrderBy('t.popularity','DESC'); diff -r 87bf6ec8af90 -r ea562f37d7a5 Entity/TagRepository.php --- a/Entity/TagRepository.php Thu Dec 08 23:48:55 2011 +0100 +++ b/Entity/TagRepository.php Fri Dec 09 02:21:17 2011 +0100 @@ -83,7 +83,12 @@ if($created==true || $tag->getUrlStatus()===Tag::$TAG_URL_STATUS_DICT['null_result']) { if($wp_request_done==false) { - $wp_response = WikiTagUtils::getWikipediaInfo($tag_label_normalized); + try { + $wp_response = WikiTagUtils::getWikipediaInfo($tag_label_normalized); + } + catch (\Exception $e){ + throw new \Exception($e->getMessage()); + } } $tag->setWikipediaInfo($wp_response); @@ -97,8 +102,12 @@ } else if($tag!=null && $tag->getWikipediaPageId()!=null) { - - $wp_response = WikiTagUtils::getWikipediaInfo(null, $tag->getWikipediaPageId()); + try { + $wp_response = WikiTagUtils::getWikipediaInfo(null, $tag->getWikipediaPageId()); + } + catch (\Exception $e){ + throw new \Exception($e->getMessage()); + } $wikipedia_revision_id = $wp_response['revision_id']; } else { @@ -108,5 +117,4 @@ return array($tag, $wikipedia_revision_id, $created);//, $wpReponse); } - -} \ No newline at end of file +} diff -r 87bf6ec8af90 -r ea562f37d7a5 README.md --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/README.md Fri Dec 09 02:21:17 2011 +0100 @@ -0,0 +1,185 @@ + +# WikiTagBundle + +WikiTagBundle is a php bundle for [Symfony 2](http://symfony.com/) released by the [Institute for research and innovation](http://www.iri.centrepompidou.fr/) (IRI). +It enables to add semantised tags to any kind of document. +By semantised, we mean that a tag has its label, but also a wikipedia link related to this label. Right now, for v1.0, it works with the french wikipedia. +For a simple example, the tag "Asie" is related to the link [http://fr.wikipedia.org/wiki/Asie](http://fr.wikipedia.org/wiki/Asie). +The tag can also be categorised, by any value in a list. +The tag can also have an alias, which is any string value. +When a wikipedia entry is found, the bundle also searches a [dbPedia](http://dbpedia.org/) entry for the english equivalent, for example [http://dbpedia.org/page/Asia](http://dbpedia.org/page/Asia). + +A tag can have 4 kinds of wikipedia links : + +* **match** - Perfect match between the label and the wikipedia entry (i.e. [Asie](http://fr.wikipedia.org/wiki/Asie)). +* **redirection** - The label exists in wikipedia but redirects to an other entry (i.e. Louis XIV to [Louis XIV de France](http://fr.wikipedia.org/wiki/Louis_XIV_de_France)). +* **homonymy** - The label leads to wikipedia homonymy page (i.e. [Abstraction](http://fr.wikipedia.org/wiki/Abstraction)). +* **null result** - The label is not related to any wikipedia entry. So we build a link leading to the search page (i.e. [art multimédia](http://fr.wikipedia.org/w/index.php?search=art+multim%C3%A9dia)). + + + +## Install +WikiTagBundle is a php bundle for [Symfony 2](http://symfony.com/). + +* Install the dependencies : [PagerFanta](https://github.com/whiteoctober/Pagerfanta) and [Mondator](https://github.com/mandango/mondator). +* Download the zipfile from the [downloads](https://github.com/) page and install it. +* Once unzipped, just copy the IRI folder in your vendor/bundles folder. The folder hierarchy will be vendor/bundles/IRI/Bundle/WikiTagBundle. + +## Getting Started +* Install WikiTagBundle +* Register the bundle in AppKernel.php : + + ... new IRI\Bundle\WikiTagBundle\WikiTagBundle(), ... + +* Register the namespace in the autoload.php : + + ... 'IRI\Bundle\WikiTagBundle' => \_\_DIR\_\_.'/../vendor/bundles', ... + +* Register the namespace fallbacks in the autoload.php : + + $loader->registerNamespaceFallbacks(array( + \_\_DIR\_\_.'/../src', + \_\_DIR\_\_.'/cache/dev/wikitag', + \_\_DIR\_\_.'/cache/prod/wikitag', + \_\_DIR\_\_.'/cache/test/wikitag', + \_\_DIR\_\_.'/cache/task/wikitag', + )); + +* Since WikiTagBundle builds its own document class from the host app's document class, you need to tell in config.yml what _text_ fields will be used in this class. +These fields are used for searching and to calculate the tag's weight. Example : + + wiki_tag: + document_class: Company\BaseBundle\Entity\Document + document_id_column: id + fields: + title: + type: string + length: 1024 + accessor: getTitre + weight: 1.0 + description: + type: text + weight: 0.5 + +* Add the WikiTag routes to your routing.yml : + + WikiTagBundle: + resource: "@WikiTagBundle/Resources/config/routing.yml" + prefix: /tag + +* Run the commands : + + php app/console wikitag:generate-document-class (no need to explain) + php app/console wikitag:schema:update (also runs php app/console doctrine:schema:update) + php app/console wikitag:sync-doc (fills the database with the datas from the host document class to the wikitag document class) + +* Your database is ready. You can now include the table of tags in a template. Do not forget css and javascript (and php app/console assets:install). Example : + + {# example of page extending the base template #} + {% extends 'CompanyBaseBundle:Default:index.html.twig' %} + + {% block css_import %} + {{ parent() }} + {% render "WikiTagBundle:WikiTag:addCss" %} + {% endblock %} + + {% block js_declaration %} + {{ parent() }} + {% render "WikiTagBundle:WikiTag:addJavascript" %} + {% endblock %} + + {% block content %} + + {% render "WikiTagBundle:WikiTag:documentTags" with {'id_doc': doc.id} %} + + {% endblock %} + +* Great ! You can now add/remove/change semantised tags to your document ! The WikiTag template includes an autocomplete search field to add simply and fastly any wikipedia semantised tag. + + +## The list of all tags +If you want to, you can add into a page the list of all tags. WikiTagBundle manages a list of all tags with the paginator PagerFanta. By default, it displays 50 tags per page. +The template includes links to quick search via a list of tags's first letter. A search field is also included. It works with the star character (\*) as a delimiter for searching. +For example "\*Peter" will return tags ending by Peter, "Peter\*" tags beginning by Peter, and "\*Peter\*" all tags including Peter. +The list can be sorted in ascending or descending label, number of documents or popularity (integer value). +WikiTagBundle manages pagination, search, and sort with url parameters. + +Example : http://mysite.com/route\_to\_list?searched=Peter\*&num\_page=1&nb\_by\_page=50&sort=popd + +Including the tag list template looks like : + + {# example of template including the all tags list #} + {% extends 'CompanyBaseBundle:Default:index.html.twig' %} + + {% block css_import %} + {{ parent() }} + {% render "WikiTagBundle:WikiTag:addCss" %} + {% endblock %} + + {% block js_declaration %} + {{ parent() }} + {% render "WikiTagBundle:WikiTag:addJavascript" with {'tags_list': true} %} + {% endblock %} + + {% block content %} + + {% render "WikiTagBundle:WikiTag:allTags" %} + + {% endblock %} + +*IMPORTANT* : This template needs a route to be defined in configuration file. Usually, this host site's route leads the a page/document concerned by the clicked tag. +This route is used by the list to create a link on the "nb of documents" column. config.yml looks like : + + wiki_tag: + route_for_documents_by_tag: a_route_from_host_site + document_class: Company\BaseBundle\Entity\Document + ... + + +## More configuration 1 : tag list profile for a document +Via config.yml, you can configure which columns are displayed by default for a given user. It also concerns the button "sort tags" +(This function orders tags depending of their presence in the text fields set in config.yml). +Is it is very simple, you define the list of columns the user profile will see by default. In the following example, the list values are the ones to use : + + wiki_tag: + ... + document_list_profile: + all: [ 'sort_tag', 'order', 'id', 'move_up_down', 'label', 'wikipedia_link', 'wikipedia_permalink', 'dbpedia_link', 'category', 'remove_wikipedia_link', 'alias', 'remove_tag_from_list', 'alternative_label', 'alternative_wikipedia_url' ] + editor : [ 'order', 'id', 'move_up_down', 'label', 'wikipedia_link', 'wikipedia_permalink', 'dbpedia_link', 'category', 'remove_wikipedia_link', 'remove_tag_from_list' ] + contributor: [ 'sort_tag', 'order', 'label', 'wikipedia_link', 'wikipedia_permalink', 'delete_wikipedia_link', 'remove_tag_from_list' ] + +In these values, "sort_tag" means the sort tag button. All the other values are the available columns in the tag table. +Once this configuration set, call the profile in your template. The profile has to be called in the javascript call AND in the html call. So your template will look like this : + + ... + {% render "WikiTagBundle:WikiTag:addJavascript" with {'profile_name': 'editor'} %} + ... + {% render "WikiTagBundle:WikiTag:documentTags" with {'id_doc': doc.id, 'profile_name': 'editor'} %} + ... + + +## More configuration 2 : add context seach to any text part of your page +Via config.yml, you can configure a list of jquery selectors meant to let appear tag context search by selecting text. +Once some text selected, a little div appears and displays several wikipedia entries with the entry title and a snippet. The results are the same than in an opensearch page +(example with [découvrir](http://fr.wikipedia.org/w/index.php?search=d%C3%A9couvrir)). If you want this functionality, config.yml will look like this : + + wiki_tag: + ... + reactive_selectors: + some_divs: [ '.left_sheet', '#p_title .sheet_title', '#p_description' ] + only_p: [ '#p_title .sheet_title', '#p_description' ] + whole_page: [ 'document' ] + +If you want every text on your page to be reactive, the list has to be [ 'document' ]. +In the templates, you have to call a specific javascript with the wanted parameter. Your javascript calls may look like this : + + {% block js_declaration %} + {{ parent() }} + {% render "WikiTagBundle:WikiTag:addJavascript" with {'profile_name': 'editor'} %} + {% render "WikiTagBundle:WikiTag:addJavascriptForContextSearch" with {'context_name': 'some_divs'} %} + {% endblock %} + + + + + diff -r 87bf6ec8af90 -r ea562f37d7a5 Resources/public/css/clickmenu.css --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Resources/public/css/clickmenu.css Fri Dec 09 02:21:17 2011 +0100 @@ -0,0 +1,115 @@ +div.cmDiv +{ + border: 1px solid black; + background-color: #eee; +} +.clickMenu +{ + margin: 0; + padding: 0; + cursor: default; +} +.clickMenu, .clickMenu ul +{ + list-style: none; +} +.clickMenu ul +{ + margin: 0; + padding: 2px; + border: 1px solid black; + background-color: #eee; + min-width: 100px; /* ie doesnt know this :/ */ + //float: left;/*ie again*/ + //width: 100%;/*and again*/ +} +.clickMenu div.outerbox +{ + display: none; + min-width: 106px; /* firefox produces animation-flickering when the box is bigger than this :/ */ +} +.clickMenu div.inner +{ + //top: 20px; /* for ie */ + left: 0; + margin: 0; +} +.clickMenu div.inner div.outerbox +{ + margin: 0; + left: 98px; + //left: 90px; /* for ie */ + top: -3px; +} +.clickMenu li +{ + position: relative; + padding: 0 20px 0 2px; + /*white-space: nowrap; does not really work in ie */ +} +.clickMenu li.main +{ + float: left; + padding: 0 10px; + background-color: #eee; + //height: 20px; /* for ie */ +} +.clickMenu li.main li +{ + z-index: 2; + min-width: 78px; +} +.clickMenu li.hover +{ + z-index: 1 !important; /* the hover z-index has to be below the normal one the hovering items may be drawn over a submenu */ + background-color: #aaa; +} +.clickMenu img.liArrow +{ + position: absolute; + right: 5px; + top: 0.41em; +} +.clickMenu a +{ + text-decoration: none; + color: black; + cursor: default; +} +/* thats for the shadowbox */ +html>body div.outerbox +{ + padding: 0 5px 5px 0; +} +html>body div.shadowbox1 +{ + position: absolute; + right: 0; + bottom: 5px; + width: 5px; + height: 100%; + background: url(../images/myshadow.png) no-repeat right top; +} +html>body div.shadowbox2 +{ + position: absolute; + bottom: 0; + right: 5px; + height: 5px; + width: 100%; + background: url(../images/myshadow.png) left bottom; +} +html>body div.shadowbox3 +{ + position: absolute; + bottom: 0; + right: 0; + height: 5px; + width: 5px; + background: url(../images/myshadow.png) no-repeat right bottom; +} +html>body .innerbox +{ + margin: 0; + display: inherit; +} \ No newline at end of file diff -r 87bf6ec8af90 -r ea562f37d7a5 Resources/public/css/wikiTag.css --- a/Resources/public/css/wikiTag.css Thu Dec 08 23:48:55 2011 +0100 +++ b/Resources/public/css/wikiTag.css Fri Dec 09 02:21:17 2011 +0100 @@ -1,6 +1,6 @@ @charset "UTF-8"; -.wikitag_hand_cursor, .wikitag_remove_wp_link, .wikitag_remove_tag_from_list, .wikitag_reset_wp_info { +.wikitag_hand_cursor, .wikitag_reset_wp_info { cursor: pointer; } #wikitag_actions span { @@ -13,6 +13,9 @@ #wikitag_table td, th { padding: 3px; } +#wikitag_table tr, #wikitag_alltags_table tr { + height: 25px; +} .wikitag_text_centered { text-align: center; } @@ -28,14 +31,113 @@ tr.wikitag_oddline:hover, tr.wikitag_evenline:hover { background: lightblue; } -.wikitag_updown_td { - width: 12px; - cursor: n-resize; -} tr.wikitag_dragged_row td { color: yellow; background-color: black; } +.wikitag_hidden { + display: none; +} + +/* Styles for images in tables */ +.wikitag_a_icon { + width: 16px; + height: 16px; + display: block; + margin: 0 auto; +} +.wikitag_wp_link { + background-image: url('../images/arrow_right.png'); +} +.wikitag_wp_nolink { + background-image: url('../images/wikipedia_search.png'); +} +.wikitag_wp_permalink { + background-image: url('../images/clock_arrow.png'); +} +.wikitag_wp_dbplink { + background-image: url('../images/arrow_green_right.png'); +} +.wikitag_wp_redirlink { + background-image: url('../images/arrow_redir.png'); +} +.wikitag_td_icon { + width: 16px; + height: 16px; + margin: 0 auto; + background-repeat: no-repeat; + background-position: center; +} +.wikitag_updown_td { + cursor: n-resize; + background-image: url('../images/arrow_up_down.png'); +} +.wikitag_updown_td span { + display: none; +} +.wikitag_remove_wp_link { + cursor: pointer; + background-image: url('../images/red_cross.png'); +} +.wikitag_remove_tag_from_list { + cursor: pointer; + background-image: url('../images/tag_remove.png'); +} + +/* Styles for the list of columns in document tag list*/ +#wikitag_ul_target +{ + list-style: none; + background-color: #eee; +} +#wikitag_ul_target li +{ + background-repeat: no-repeat; + padding-left: 20px; +} +#wikitag_thSelectColumn div.cmDiv +{ + display: inline; + background: none; + border: 0; +} +#wikitag_thSelectColumn li.main +{ + padding: 0; + background: none; + width: 100%; + text-align: right; +} +#wikitag_thSelectColumn li.main li +{ + text-align: left; + padding-right: 2px; +} +#wikitag_thSelectColumn +{ + padding-right: 2px; + width: 10px; +} +#wikitag_ulSelectColumn +{ + vertical-align: middle; +} +li.wikitag_advon +{ + background: url(../images/tick.png) 0 50%; +} +li.wikitag_advoff +{ + background: url(../images/cross.png) 0 50%; +} +li.wikitag_reinit +{ + background: url(../images/arrow_rotate_clockwise.png) 0 50%; +} +.clickMenu div.inner { + left: -140px !important; +} + /* styles for the 4 types of STATUS */ .wikitag_null_result { diff -r 87bf6ec8af90 -r ea562f37d7a5 Resources/public/images/arrow_redir.png Binary file Resources/public/images/arrow_redir.png has changed diff -r 87bf6ec8af90 -r ea562f37d7a5 Resources/public/images/arrow_rotate_clockwise.png Binary file Resources/public/images/arrow_rotate_clockwise.png has changed diff -r 87bf6ec8af90 -r ea562f37d7a5 Resources/public/images/cross.png Binary file Resources/public/images/cross.png has changed diff -r 87bf6ec8af90 -r ea562f37d7a5 Resources/public/images/myshadow.png Binary file Resources/public/images/myshadow.png has changed diff -r 87bf6ec8af90 -r ea562f37d7a5 Resources/public/images/tick.png Binary file Resources/public/images/tick.png has changed diff -r 87bf6ec8af90 -r ea562f37d7a5 Resources/public/js/jquery.clickmenu.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Resources/public/js/jquery.clickmenu.js Fri Dec 09 02:21:17 2011 +0100 @@ -0,0 +1,515 @@ +/* clickMenu - v0.1.6 + * Copyright (c) 2007 Roman Weich + * http://p.sohei.org + * + * Changelog: + * v 0.1.6 - 2007-09-06 + * -fix: having a link in the top-level menu would not open the menu but call the link instead + * v 0.1.5 - 2007-07-07 + * -change/fix: menu opening/closing now through simple show() and hide() calls - before fadeIn and fadeOut were used for which extra functions to stop a already running animation were created -> they were + * buggy (not working with the interface plugin in jquery1.1.2 and not working with jquery1.1.3 at all) and now removed + * -change: removed option: fadeTime + * -change: now using the bgiframe plugin for adding iframes in ie6 when available + * v 0.1.4 - 2007-03-20 + * -fix: the default options were overwritten by the context related options + * -fix: hiding a submenu all hover- and click-events were unbound, even the ones not defined in this plugin - unbinding should work now + * v 0.1.3 - 2007-03-13 + * -fix: some display problems ie had when no width was set on the submenu, so on ie the width for each submenu will be explicitely set + * -fix: the fix to the ie-width-problem is a fix to the "ie does not support css min-width stuff" problem too which displayed some submenus too narrow (it looked just not right) + * -fix: some bugs, when user the was too fast with the mouse + * v 0.1.2 - 2007-03-11 + * -change: made a lot changes in the traversing routines to speed things up (having better memory usage now as well) + * -change: added $.fn.clickMenu.setDefaults() for setting global defaults + * -fix: hoverbug when a main menu item had no submenu + * -fix: some bugs i found while rewriting most of the stuff + * v 0.1.1 - 2007-03-04 + * -change: the width of the submenus is no longer fixed, its set in the plugin now + * -change: the submenu-arrow is now an img, not the background-img of the list element - that allows better positioning, and background-changes on hover (you have to set the image through the arrowSrc option) + * -fix: clicking on a clickMenu while another was already open, didn't close the open one + * -change: clicking on the open main menu item will close it + * -fix: on an open menu moving the mouse to a main menu item and moving it fastly elsewere hid the whole menu + * v 0.1.0 - 2007-03-03 + */ + +(function($) +{ + var defaults = { + onClick: function(){ + $(this).find('>a').each(function(){ + if ( this.href ) + { + window.location = this.href; + } + }); + }, + arrowSrc: '', + subDelay: 300, + mainDelay: 10 + }; + + $.fn.clickMenu = function(options) + { + var shown = false; + var liOffset = ( ($.browser.msie) ? 4 : 2 ); + + var settings = $.extend({}, defaults, options); + + var hideDIV = function(div, delay) + { + //a timer running to show the div? + if ( div.timer && !div.isVisible ) + { + clearTimeout(div.timer); + } + else if (div.timer) + { + return; //hide-timer already running + } + if ( div.isVisible ) + { + div.timer = setTimeout(function() + { + //remove events + $(getAllChilds(getOneChild(div, 'UL'), 'LI')).unbind('mouseover', liHoverIn).unbind('mouseout', liHoverOut).unbind('click', settings.onClick); + //hide it + $(div).hide(); + div.isVisible = false; + div.timer = null; + }, delay); + } + }; + + var showDIV = function(div, delay) + { + if ( div.timer ) + { + clearTimeout(div.timer); + } + if ( !div.isVisible ) + { + div.timer = setTimeout(function() + { + //check if the mouse is still over the parent item - if not dont show the submenu + if ( !checkClass(div.parentNode, 'hover') ) + { + return; + } + //assign events to all div>ul>li-elements + $(getAllChilds(getOneChild(div, 'UL'), 'LI')).mouseover(liHoverIn).mouseout(liHoverOut).click(settings.onClick); + //positioning + if ( !checkClass(div.parentNode, 'main') ) + { + $(div).css('left', div.parentNode.offsetWidth - liOffset); + } + //show it + div.isVisible = true; //we use this over :visible to speed up traversing + $(div).show(); + if ( $.browser.msie ) //fixing a display-bug in ie6 and adding min-width + { + var cW = $(getOneChild(div, 'UL')).width(); + if ( cW < 100 ) + { + cW = 100; + } + $(div).css('width', cW); + } + div.timer = null; + }, delay); + } + }; + + //same as hover.handlehover in jquery - just can't use hover() directly - need the ability to unbind only the one hover event + var testHandleHover = function(e) + { + // Check if mouse(over|out) are still within the same parent element + var p = (e.type == "mouseover" ? e.fromElement : e.toElement) || e.relatedTarget; + // Traverse up the tree + while ( p && p != this ) + { + try + { + p = p.parentNode; + } + catch(e) + { + p = this; + } + } + // If we actually just moused on to a sub-element, ignore it + if ( p == this ) + { + return false; + } + return true; + }; + + var mainHoverIn = function(e) + { + //no need to test e.target==this, as no child has the same event binded + //its possible, that a main menu item still has hover (if it has no submenu) - thus remove it + var lis = getAllChilds(this.parentNode, 'LI'); + var pattern = new RegExp("(^|\\s)hover(\\s|$)"); + for (var i = 0; i < lis.length; i++) + { + if ( pattern.test(lis[i].className) ) + { + $(lis[i]).removeClass('hover'); + } + } + $(this).addClass('hover'); + if ( shown ) + { + hoverIn(this, settings.mainDelay); + } + }; + + var liHoverIn = function(e) + { + if ( !testHandleHover(e) ) + { + return false; + } + if ( e.target != this ) + { + //look whether the target is a direct child of this (maybe an image) + if ( !isChild(this, e.target) ) + { + return; + } + } + hoverIn(this, settings.subDelay); + }; + + var hoverIn = function(li, delay) + { + var innerDiv = getOneChild(li, 'DIV'); + //stop running timers from the other menus on the same level - a little faster than $('>*>div', li.parentNode) + var n = li.parentNode.firstChild; + for ( ; n; n = n.nextSibling ) + { + if ( n.nodeType == 1 && n.nodeName.toUpperCase() == 'LI' ) + { + var div = getOneChild(n, 'DIV'); + if ( div && div.timer && !div.isVisible ) //clear show-div timer + { + clearTimeout(div.timer); + div.timer = null; + } + } + } + //is there a timer running to hide one of the parent divs? stop it + var pNode = li.parentNode; + for ( ; pNode; pNode = pNode.parentNode ) + { + if ( pNode.nodeType == 1 && pNode.nodeName.toUpperCase() == 'DIV' ) + { + if (pNode.timer) + { + clearTimeout(pNode.timer); + pNode.timer = null; + $(pNode.parentNode).addClass('hover'); + } + } + } + //highlight the current element + $(li).addClass('hover'); + //is the submenu already visible? + if ( innerDiv && innerDiv.isVisible ) + { + //hide-timer running? + if ( innerDiv.timer ) + { + clearTimeout(innerDiv.timer); + innerDiv.timer = null; + } + else + { + return; + } + } + //hide all open menus on the same level and below and unhighlight the li item (but not the current submenu!) + $(li.parentNode.getElementsByTagName('DIV')).each(function(){ + if ( this != innerDiv && this.isVisible ) + { + hideDIV(this, delay); + $(this.parentNode).removeClass('hover'); + } + }); + //show the submenu, if there is one + if ( innerDiv ) + { + showDIV(innerDiv, delay); + } + }; + + var liHoverOut = function(e) + { + if ( !testHandleHover(e) ) + { + return false; + } + if ( e.target != this ) + { + if ( !isChild(this, e.target) ) //return only if the target is no direct child of this + { + return; + } + } + //remove the hover from the submenu item, if the mouse is hovering out of the menu (this is only for the last open (levelwise) (sub-)menu) + var div = getOneChild(this, 'DIV'); + if ( !div ) + { + $(this).removeClass('hover'); + } + else + { + if ( !div.isVisible ) + { + $(this).removeClass('hover'); + } + } + }; + + var mainHoverOut = function(e) + { + //no need to test e.target==this, as no child has the same event binded + //remove hover + var div = getOneChild(this, 'DIV'); + var relTarget = e.relatedTarget || e.toElement; //this is undefined sometimes (e.g. when the mouse moves out of the window), so dont remove hover then + var p; + if ( !shown ) + { + $(this).removeClass('hover'); + } + else if ( !div && relTarget ) //menuitem has no submenu, so dont remove the hover if the mouse goes outside the menu + { + p = findParentWithClass(e.target, 'UL', 'clickMenu'); + if ( p.contains(relTarget)) + { + $(this).removeClass('hover'); + } + } + else if ( relTarget ) + { + //remove hover only when moving to anywhere inside the clickmenu + p = findParentWithClass(e.target, 'UL', 'clickMenu'); + if ( !div.isVisible && (p.contains(relTarget)) ) + { + $(this).removeClass('hover'); + } + } + }; + + var mainClick = function() + { + var div = getOneChild(this, 'DIV'); + if ( div && div.isVisible ) //clicked on an open main-menu-item + { + clean(); + $(this).addClass('hover'); + } + else + { + hoverIn(this, settings.mainDelay); + shown = true; + $(document).bind('mousedown', checkMouse); + } + return false; + }; + + var checkMouse = function(e) + { + //is the mouse inside a clickmenu? if yes, is it an open (the current) one? + var vis = false; + var cm = findParentWithClass(e.target, 'UL', 'clickMenu'); + if ( cm ) + { + $(cm.getElementsByTagName('DIV')).each(function(){ + if ( this.isVisible ) + { + vis = true; + } + }); + } + if ( !vis ) + { + clean(); + } + }; + + var clean = function() + { + //remove timeout and hide the divs + $('ul.clickMenu div.outerbox').each(function(){ + if ( this.timer ) + { + clearTimeout(this.timer); + this.timer = null; + } + if ( this.isVisible ) + { + $(this).hide(); + this.isVisible = false; + } + }); + $('ul.clickMenu li').removeClass('hover'); + //remove events + $('ul.clickMenu>li li').unbind('mouseover', liHoverIn).unbind('mouseout', liHoverOut).unbind('click', settings.onClick); + $(document).unbind('mousedown', checkMouse); + shown = false; + }; + + var getOneChild = function(elem, name) + { + if ( !elem ) + { + return null; + } + var n = elem.firstChild; + for ( ; n; n = n.nextSibling ) + { + if ( n.nodeType == 1 && n.nodeName.toUpperCase() == name ) + { + return n; + } + } + return null; + }; + + var getAllChilds = function(elem, name) + { + if ( !elem ) + { + return []; + } + var r = []; + var n = elem.firstChild; + for ( ; n; n = n.nextSibling ) + { + if ( n.nodeType == 1 && n.nodeName.toUpperCase() == name ) + { + r[r.length] = n; + } + } + return r; + }; + + var findParentWithClass = function(elem, searchTag, searchClass) + { + var pNode = elem.parentNode; + var pattern = new RegExp("(^|\\s)" + searchClass + "(\\s|$)"); + for ( ; pNode; pNode = pNode.parentNode ) + { + if ( pNode.nodeType == 1 && pNode.nodeName.toUpperCase() == searchTag && pattern.test(pNode.className) ) + { + return pNode; + } + } + return null; + }; + + var checkClass = function(elem, searchClass) + { + var pattern = new RegExp("(^|\\s)" + searchClass + "(\\s|$)"); + if ( pattern.test(elem.className) ) + { + return true; + } + return false; + }; + + var isChild = function(elem, childElem) + { + var n = elem.firstChild; + for ( ; n; n = n.nextSibling ) + { + if ( n == childElem ) + { + return true; + } + } + return false; + }; + + return this.each(function() + { + //add .contains() to mozilla - http://www.quirksmode.org/blog/archives/2006/01/contains_for_mo.html + if (window.Node && Node.prototype && !Node.prototype.contains) + { + Node.prototype.contains = function(arg) + { + return !!(this.compareDocumentPosition(arg) & 16); + }; + } + //add class + if ( !checkClass(this, 'clickMenu') ) + { + $(this).addClass('clickMenu'); + } + //add shadows + $('ul', this).shadowBox(); + //ie6? - add iframes + if ( $.browser.msie && (!$.browser.version || parseInt($.browser.version) <= 6) ) + { + if ( $.fn.bgiframe ) + { + $('div.outerbox', this).bgiframe(); + } + else + { + /* thanks to Mark Gibson - http://www.nabble.com/forum/ViewPost.jtp?post=6504414&framed=y */ + $('div.outerbox', this).append('