diff --git a/html/modules/cck/content.js b/html/modules/cck/content.js index cb6fca7..4b1501c 100755 --- a/html/modules/cck/content.js +++ b/html/modules/cck/content.js @@ -77,4 +77,4 @@ jQuery.fn.contentPopulateOptions = function(options, selected) { .html(html) .attr('disabled', disabled ? 'disabled' : ''); }); -} \ No newline at end of file +} diff --git a/html/modules/node_images/LICENSE.txt b/html/modules/node_images/LICENSE.txt new file mode 100644 index 0000000..2c095c8 --- /dev/null +++ b/html/modules/node_images/LICENSE.txt @@ -0,0 +1,274 @@ +GNU GENERAL PUBLIC LICENSE + + Version 2, June 1991 + +Copyright (C) 1989, 1991 Free Software Foundation, Inc. 675 Mass Ave, +Cambridge, MA 02139, USA. Everyone is permitted to copy and distribute +verbatim copies of this license document, but changing it is not allowed. + + Preamble + +The licenses for most software are designed to take away your freedom to +share and change it. By contrast, the GNU General Public License is +intended to guarantee your freedom to share and change free software--to +make sure the software is free for all its users. This General Public License +applies to most of the Free Software Foundation's software and to any other +program whose authors commit to using it. (Some other Free Software +Foundation software is covered by the GNU Library General Public License +instead.) You can apply it to your programs, too. + +When we speak of free software, we are referring to freedom, not price. Our +General Public Licenses are designed to make sure that you have the +freedom to distribute copies of free software (and charge for this service if +you wish), that you receive source code or can get it if you want it, that you +can change the software or use pieces of it in new free programs; and that +you know you can do these things. + +To protect your rights, we need to make restrictions that forbid anyone to +deny you these rights or to ask you to surrender the rights. These restrictions +translate to certain responsibilities for you if you distribute copies of the +software, or if you modify it. + +For example, if you distribute copies of such a program, whether gratis or for +a fee, you must give the recipients all the rights that you have. You must make +sure that they, too, receive or can get the source code. And you must show +them these terms so they know their rights. + +We protect your rights with two steps: (1) copyright the software, and (2) +offer you this license which gives you legal permission to copy, distribute +and/or modify the software. + +Also, for each author's protection and ours, we want to make certain that +everyone understands that there is no warranty for this free software. If the +software is modified by someone else and passed on, we want its recipients +to know that what they have is not the original, so that any problems +introduced by others will not reflect on the original authors' reputations. + +Finally, any free program is threatened constantly by software patents. We +wish to avoid the danger that redistributors of a free program will individually +obtain patent licenses, in effect making the program proprietary. To prevent +this, we have made it clear that any patent must be licensed for everyone's +free use or not licensed at all. + +The precise terms and conditions for copying, distribution and modification +follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND + MODIFICATION + +0. This License applies to any program or other work which contains a notice +placed by the copyright holder saying it may be distributed under the terms +of this General Public License. The "Program", below, refers to any such +program or work, and a "work based on the Program" means either the +Program or any derivative work under copyright law: that is to say, a work +containing the Program or a portion of it, either verbatim or with +modifications and/or translated into another language. (Hereinafter, translation +is included without limitation in the term "modification".) Each licensee is +addressed as "you". + +Activities other than copying, distribution and modification are not covered +by this License; they are outside its scope. The act of running the Program is +not restricted, and the output from the Program is covered only if its contents +constitute a work based on the Program (independent of having been made +by running the Program). Whether that is true depends on what the Program +does. + +1. You may copy and distribute verbatim copies of the Program's source +code as you receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice and +disclaimer of warranty; keep intact all the notices that refer to this License +and to the absence of any warranty; and give any other recipients of the +Program a copy of this License along with the Program. + +You may charge a fee for the physical act of transferring a copy, and you +may at your option offer warranty protection in exchange for a fee. + +2. You may modify your copy or copies of the Program or any portion of it, +thus forming a work based on the Program, and copy and distribute such +modifications or work under the terms of Section 1 above, provided that you +also meet all of these conditions: + +a) You must cause the modified files to carry prominent notices stating that +you changed the files and the date of any change. + +b) You must cause any work that you distribute or publish, that in whole or in +part contains or is derived from the Program or any part thereof, to be +licensed as a whole at no charge to all third parties under the terms of this +License. + +c) If the modified program normally reads commands interactively when run, +you must cause it, when started running for such interactive use in the most +ordinary way, to print or display an announcement including an appropriate +copyright notice and a notice that there is no warranty (or else, saying that +you provide a warranty) and that users may redistribute the program under +these conditions, and telling the user how to view a copy of this License. +(Exception: if the Program itself is interactive but does not normally print such +an announcement, your work based on the Program is not required to print +an announcement.) + +These requirements apply to the modified work as a whole. If identifiable +sections of that work are not derived from the Program, and can be +reasonably considered independent and separate works in themselves, then +this License, and its terms, do not apply to those sections when you distribute +them as separate works. But when you distribute the same sections as part +of a whole which is a work based on the Program, the distribution of the +whole must be on the terms of this License, whose permissions for other +licensees extend to the entire whole, and thus to each and every part +regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest your rights to +work written entirely by you; rather, the intent is to exercise the right to +control the distribution of derivative or collective works based on the +Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of a +storage or distribution medium does not bring the other work under the scope +of this License. + +3. You may copy and distribute the Program (or a work based on it, under +Section 2) in object code or executable form under the terms of Sections 1 +and 2 above provided that you also do one of the following: + +a) Accompany it with the complete corresponding machine-readable source +code, which must be distributed under the terms of Sections 1 and 2 above +on a medium customarily used for software interchange; or, + +b) Accompany it with a written offer, valid for at least three years, to give +any third party, for a charge no more than your cost of physically performing +source distribution, a complete machine-readable copy of the corresponding +source code, to be distributed under the terms of Sections 1 and 2 above on +a medium customarily used for software interchange; or, + +c) Accompany it with the information you received as to the offer to distribute +corresponding source code. (This alternative is allowed only for +noncommercial distribution and only if you received the program in object +code or executable form with such an offer, in accord with Subsection b +above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source code +means all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation and +installation of the executable. However, as a special exception, the source +code distributed need not include anything that is normally distributed (in +either source or binary form) with the major components (compiler, kernel, +and so on) of the operating system on which the executable runs, unless that +component itself accompanies the executable. + +If distribution of executable or object code is made by offering access to +copy from a designated place, then offering equivalent access to copy the +source code from the same place counts as distribution of the source code, +even though third parties are not compelled to copy the source along with the +object code. + +4. You may not copy, modify, sublicense, or distribute the Program except as +expressly provided under this License. Any attempt otherwise to copy, +modify, sublicense or distribute the Program is void, and will automatically +terminate your rights under this License. However, parties who have received +copies, or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + +5. You are not required to accept this License, since you have not signed it. +However, nothing else grants you permission to modify or distribute the +Program or its derivative works. These actions are prohibited by law if you +do not accept this License. Therefore, by modifying or distributing the +Program (or any work based on the Program), you indicate your acceptance +of this License to do so, and all its terms and conditions for copying, +distributing or modifying the Program or works based on it. + +6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the original +licensor to copy, distribute or modify the Program subject to these terms and +conditions. You may not impose any further restrictions on the recipients' +exercise of the rights granted herein. You are not responsible for enforcing +compliance by third parties to this License. + +7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), conditions +are imposed on you (whether by court order, agreement or otherwise) that +contradict the conditions of this License, they do not excuse you from the +conditions of this License. If you cannot distribute so as to satisfy +simultaneously your obligations under this License and any other pertinent +obligations, then as a consequence you may not distribute the Program at all. +For example, if a patent license would not permit royalty-free redistribution +of the Program by all those who receive copies directly or indirectly through +you, then the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply and +the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any patents or +other property right claims or to contest validity of any such claims; this +section has the sole purpose of protecting the integrity of the free software +distribution system, which is implemented by public license practices. Many +people have made generous contributions to the wide range of software +distributed through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing to +distribute software through any other system and a licensee cannot impose +that choice. + +This section is intended to make thoroughly clear what is believed to be a +consequence of the rest of this License. + +8. If the distribution and/or use of the Program is restricted in certain +countries either by patents or by copyrighted interfaces, the original copyright +holder who places the Program under this License may add an explicit +geographical distribution limitation excluding those countries, so that +distribution is permitted only in or among countries not thus excluded. In such +case, this License incorporates the limitation as if written in the body of this +License. + +9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will be +similar in spirit to the present version, but may differ in detail to address new +problems or concerns. + +Each version is given a distinguishing version number. If the Program specifies +a version number of this License which applies to it and "any later version", +you have the option of following the terms and conditions either of that +version or of any later version published by the Free Software Foundation. If +the Program does not specify a version number of this License, you may +choose any version ever published by the Free Software Foundation. + +10. If you wish to incorporate parts of the Program into other free programs +whose distribution conditions are different, write to the author to ask for +permission. For software which is copyrighted by the Free Software +Foundation, write to the Free Software Foundation; we sometimes make +exceptions for this. Our decision will be guided by the two goals of +preserving the free status of all derivatives of our free software and of +promoting the sharing and reuse of software generally. + + NO WARRANTY + +11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, +THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT +PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE +STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT +WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND +PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL +NECESSARY SERVICING, REPAIR OR CORRECTION. + +12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR +AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR +ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE +LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, +SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES +ARISING OUT OF THE USE OR INABILITY TO USE THE +PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA +OR DATA BEING RENDERED INACCURATE OR LOSSES +SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE +PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN +IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF +THE POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS diff --git a/html/modules/node_images/node-example.tpl.php b/html/modules/node_images/node-example.tpl.php new file mode 100644 index 0000000..212000f --- /dev/null +++ b/html/modules/node_images/node-example.tpl.php @@ -0,0 +1,32 @@ +
"> + +

+ + + +
+ + + + node_images) { ?> + + + + + +
+ + + node_images ?> +
+ Gallery +
+
+
+ + + + +
\ No newline at end of file diff --git a/html/modules/node_images/node_images.admin.inc b/html/modules/node_images/node_images.admin.inc new file mode 100644 index 0000000..b4d8eee --- /dev/null +++ b/html/modules/node_images/node_images.admin.inc @@ -0,0 +1,71 @@ + 'textfield', + '#title' => t('Node images path'), + '#default_value' => variable_get('node_images_path', 'node_images'), + '#maxlength' => 255, + '#description' => t('Subdirectory in the directory %dir where the node images will be stored. This directory has to exist and be writable by Drupal. You can use the following variables: %uid, %username, %nid', array('%dir' => file_directory_path() .'/')), + '#after_build' => array('_node_images_check_directory'), + ); + $form['node_images_large_resolution'] = array( + '#type' => 'textfield', + '#title' => t('Resolution for large images'), + '#default_value' => variable_get('node_images_large_resolution', '400x300'), + '#size' => 19, + '#maxlength' => 9, + '#description' => t('The size for large images in the node gallery, expressed as WIDTHxHEIGHT (e.g. 400x300).'), + ); + $form['node_images_thumb_resolution'] = array( + '#type' => 'textfield', + '#title' => t('Resolution for thumbnails'), + '#default_value' => variable_get('node_images_thumb_resolution', '100x100'), + '#size' => 15, + '#maxlength' => 7, + '#description' => t('The thumbnail size expressed as WIDTHxHEIGHT (e.g. 100x75).'), + ); + $form['node_images_uploadsize_default'] = array( + '#type' => 'textfield', + '#title' => t('Default maximum file size per upload'), + '#default_value' => variable_get('node_images_uploadsize_default', 1), + '#size' => 5, + '#maxlength' => 5, + '#description' => t('The default maximum file size a user can upload. If a maximum resolution is set, the size will be checked after the file has been resized.'), + '#field_suffix' => t('MB'), + ); + $form['node_images_extensions'] = array( + '#type' => 'textfield', + '#title' => t('Default permitted image extensions'), + '#default_value' => variable_get('node_images_extensions', 'jpg jpeg gif png'), + '#maxlength' => 255, + '#description' => t('Default image extensions that users can upload. Separate extensions with a space and do not include the leading dot.'), + ); + $form['node_images_max_images'] = array( + '#type' => 'textfield', + '#title' => t('Maximum number of images'), + '#default_value' => variable_get('node_images_max_images', 4), + '#size' => 6, + '#maxlength' => 2, + '#description' => t('The maximum number of images a user can upload for each node.'), + ); + $form['node_images_md5name'] = array( + '#type' => 'checkbox', + '#title'=> t('MD5 filenames'), + '#description' => t('Override uploaded filenames with the MD5 hash of the file.'), + '#default_value' => variable_get('node_images_md5name', FALSE), + ); + + return system_settings_form($form); +} \ No newline at end of file diff --git a/html/modules/node_images/node_images.css b/html/modules/node_images/node_images.css new file mode 100644 index 0000000..787ea3d --- /dev/null +++ b/html/modules/node_images/node_images.css @@ -0,0 +1,51 @@ +.node_images_slideshow { + margin: 0; + border: 0; +} +.node_images_slideshow .large { + text-align: center; + float: left; + width: 450px; +} +.node_images_slideshow .header { + margin-bottom: 5px; +} +.node_images_slideshow p.description { + margin-top: 5px; + font-weight: bold; +} +.node_images_slideshow .thumbs { + float: left; + margin: 0 0 0 10px; + min-width: 150px; + height: 400px; + overflow-x: hidden; + overflow-y: auto; +} +.node_images_slideshow .thumbs .thumb { + text-align: center; + margin: 0 0 10px 0; + float: left; + min-height: 145px; +} +.node_images_slideshow .thumbs img.slideshow-thumb { + border: 0; + padding: 1px; + border: 2px solid #ddd; +} +.node_images_slideshow .thumbs img.active { + border: 2px solid #888; +} +.node_images_slideshow .thumbs .thumb-description { + width: 120px; + white-space: wrap; + margin-bottom: 5px; + line-height: 1.2em; + font-size: .75em; +} +.polaroid { + padding: 15px 15px 55px 15px; + border: 1px solid #000; + background: #fff; + text-align: center; +} \ No newline at end of file diff --git a/html/modules/node_images/node_images.info b/html/modules/node_images/node_images.info new file mode 100644 index 0000000..301fc61 --- /dev/null +++ b/html/modules/node_images/node_images.info @@ -0,0 +1,12 @@ +name = Node Images +description = Allows users to upload images and associate them to nodes. +package = Image +version = VERSION +core = 6.x +version = "6.x-1.0" +; Information added by drupal.org packaging script on 2009-02-03 +version = "6.x-2.1" +core = "6.x" +project = "node_images" +datestamp = "1233669934" + diff --git a/html/modules/node_images/node_images.install b/html/modules/node_images/node_images.install new file mode 100644 index 0000000..7f4d710 --- /dev/null +++ b/html/modules/node_images/node_images.install @@ -0,0 +1,82 @@ + $name) { + variable_del('node_images_position_'. $key); + variable_del('node_images_teaser_images_'. $key); + variable_del('node_images_body_images_'. $key); + variable_del('node_images_teaser_format_'. $key); + variable_del('node_images_body_format_'. $key); + variable_del('node_images_roles_add_'. $key); + } +} + +function node_images_schema(){ + $schema['node_images'] = array( + 'fields' => array( + 'id' => array('type' =>'serial', 'unsigned' => TRUE, 'not null' => TRUE), + 'nid' => array('type' =>'int', 'unsigned' => TRUE, 'not null' => TRUE, 'default' => 0), + 'uid' => array('type' =>'int', 'unsigned' => TRUE, 'not null' => TRUE, 'default' => 0), + 'filename' => array('type' => 'varchar', 'length' => 255, 'not null' => TRUE, 'default' => ''), + 'filepath' => array('type' => 'varchar', 'length' => 255, 'not null' => TRUE, 'default' => ''), + 'filemime' => array('type' => 'varchar', 'length' => 255, 'not null' => TRUE, 'default' => ''), + 'filesize' => array('type' =>'int', 'unsigned' => TRUE, 'not null' => TRUE, 'default' => 0), + 'thumbpath' => array('type' => 'varchar', 'length' => 255, 'not null' => TRUE, 'default' => ''), + 'thumbsize' => array('type' =>'int', 'unsigned' => TRUE, 'not null' => TRUE, 'default' => 0), + 'status' => array('type' =>'int', 'size' => 'small', 'unsigned' => TRUE, 'not null' => TRUE, 'default' => 1), + 'weight' => array('type' =>'int', 'size' => 'small', 'unsigned' => FALSE, 'not null' => TRUE, 'default' => 0), + 'description' => array('type' => 'varchar', 'length' => 255, 'not null' => TRUE, 'default' => ''), + 'timestamp' => array('type' => 'int', 'unsigned' => TRUE, 'default' => 0), + 'list' => array('type' => 'int', 'size' => 'tiny', 'not null' => TRUE, 'unsigned' => TRUE, 'default' => 1), + ), + 'primary key' => array('id'), + 'indexes' => array( + 'uid' =>array('uid'), + 'nid_status' => array('nid', 'status'), + ) + ); + return $schema; +} + +function node_images_update_1() { + $names = node_get_types('names'); + foreach ($names as $key => $name) { + if ($result = variable_get('node_images_'. $key, '')) { + variable_set('node_images_position_'. $key, $result); + variable_del('node_images_'. $key); + } + } + return array(); +} + +function node_images_update_2() { + $ret = array(); + switch ($GLOBALS['db_type']) { + case 'mysql': + case 'mysqli': + $ret[] = update_sql('ALTER TABLE {node_images} ADD timestamp INT(11) NOT NULL default 0'); + $ret[] = update_sql('ALTER TABLE {node_images} ADD list TINYINT(1) unsigned NOT NULL default 1'); + break; + } + return $ret; +} \ No newline at end of file diff --git a/html/modules/node_images/node_images.js b/html/modules/node_images/node_images.js new file mode 100644 index 0000000..1cd4854 --- /dev/null +++ b/html/modules/node_images/node_images.js @@ -0,0 +1,57 @@ +// $Id: node_images.js,v 1.2.2.1 2008/08/23 08:53:35 stefano73 Exp $ + +Drupal.behaviors.node_images_slideshow = function() { + for (var node in Drupal.settings.node_images_slideshow) { + var slideshow = Drupal.settings.node_images_slideshow[node], container = $('#slideshow-' + node); + + function preloadImages(i) { + var n = slideshow.images[++i]; + if (slideshow.current != i) { + if (n) { + n.image = $('').attr('src', n.src).load(function() { preloadImages(i); }); + } else preloadImages(0); + } + } + + function updateSlideshow(previous) { + if (slideshow.current != previous) { + $('#thumb-' + previous).removeClass('active'); + $('#thumb-' + slideshow.current).addClass('active'); + } + + var current = slideshow.images[slideshow.current]; + current.title = current.title || ''; + current.description = current.description || ''; + current.href = current.href || '#'; + + container. + find('.polaroid').attr('src', current.src).css({ opacity: 0.8 }).animate({ opacity: 1, width: current.width, height: current.height }).end(). + find('.title').html(current.title).end().find('.description').html(current.description).end(). + find('.link').attr('href', current.href).end().find('.current').html(slideshow.current).end(); + return false; + } + + container.find('.previous').click(function() { + var current = slideshow.current; + if(!(slideshow.images[--slideshow.current])) slideshow.current = slideshow.total; + return updateSlideshow(current); + }).end(). + + find('.next').click(function() { + var current = slideshow.current; + if(!(slideshow.images[++slideshow.current])) slideshow.current = 1; + return updateSlideshow(current); + }).end(); + + $('.slideshow-thumb').each(function () { + $(this).click(function() { + var previous = slideshow.current; + slideshow.current = this.id.substr(6, this.id.length-6); + return updateSlideshow(previous); + }); + }); + + preloadImages(slideshow.current); + $('#thumb-' + slideshow.current).addClass('active'); + } +} \ No newline at end of file diff --git a/html/modules/node_images/node_images.module b/html/modules/node_images/node_images.module new file mode 100644 index 0000000..85d7ae9 --- /dev/null +++ b/html/modules/node_images/node_images.module @@ -0,0 +1,986 @@ +Internationalization package).', array('@i18n' => url('http://drupal.org/project/i18n'))); + } + $output = '

'.$output.'

'; + break; + } + return $output; +} + +/** + * Implementation of hook_perm(). + */ +function node_images_perm() { + $perms = array('administer node images', 'edit own node images', 'edit any node image', 'delete own node images', 'delete any node image'); + + foreach (node_get_types() as $type) { + $name = strtolower(check_plain($type->type)); + $perms[] = 'add node images to own '. $name .' content'; + $perms[] = 'add node images to any '. $name .' content'; + } + + return $perms; +} + +/** + * Implementation of hook_menu(). + */ +function node_images_menu() { + $items = array(); + + $items['node_images/js'] = array( + 'page callback' => '_node_images_js', + 'access callback' => TRUE, + 'type' => MENU_CALLBACK, + 'file' => 'node_images.pages.inc', + ); + $items['admin/settings/node_images'] = array( + 'title' => 'Node images', + 'description' => 'Control how to upload node images.', + 'page callback' => 'drupal_get_form', + 'page arguments' => array('node_images_admin_settings'), + 'access arguments' => array('administer site configuration'), + 'file' => 'node_images.admin.inc', + ); + $items['node/%node/images'] = array( + 'title' => 'Images', + 'page callback' => 'drupal_get_form', + 'page arguments' => array('_node_images_edit_page', 1), + 'access callback' => '_node_images_access', + 'access arguments' => array('create', 1), + 'weight' => 2, + 'file' => 'node_images.pages.inc', + 'type' => MENU_LOCAL_TASK, + ); + $items['node/%node/image_gallery'] = array( + 'title' => 'Gallery', + 'page callback' => '_node_images_gallery', + 'page arguments' => array(1), + 'access callback' => 'node_access', + 'access arguments' => array('view', 1), + 'file' => 'node_images.pages.inc', + 'type' => MENU_CALLBACK, + ); + + return $items; +} + +/** + * implementation of hook_form_alter() + */ +function node_images_form_alter(&$form, $form_state, $form_id) { + if ($form_id == 'node_type_form' && isset($form['identity']['type'])) { + $type = $form['#node_type']->type; + // radio button in the node's content type configuration page + _node_images_check_settings(); + $form['node_images'] = array( + '#type' => 'fieldset', + '#title' => t('Node images'), + '#collapsible' => TRUE, + '#collapsed' => TRUE, + ); + $form['node_images']['node_images_position'] = array( + '#type' => 'radios', + '#title' => t('Position'), + '#default_value' => variable_get('node_images_position_'.$type, 'hide'), + '#options' => array('hide' => t('Do not show'), 'above' => t('Show above node body'), 'below' => t('Show below node body'), + 'node_template' => t('Manually set in node template by variable $node->node_images')), + '#description' => t('The position of images in the node view.'), + ); + $form['node_images']['node_images_gallery_link'] = array( + '#type' => 'radios', + '#title' => t('Link to image gallery'), + '#default_value' => variable_get('node_images_gallery_link_'.$type, TRUE), + '#options' => array(t('Do not show'), t('Show')), + '#description' => t('Choose whether to show or not the link to the image gallery.'), + ); + $form['node_images']['node_images_teaser_images'] = array( + '#type' => 'textfield', + '#title' => t('Number of images in node teaser'), + '#default_value' => variable_get('node_images_teaser_images_'.$type, 2), + '#size' => 5, + '#maxlength' => 2, + '#description' => t('The maximum number of images to show in the node teaser. 0 will not show images. Leave blank to show all images.'), + ); + $form['node_images']['node_images_body_images'] = array( + '#type' => 'textfield', + '#title' => t('Number of images in node body'), + '#size' => 5, + '#maxlength' => 2, + '#default_value' => variable_get('node_images_body_images_'.$type, NULL), + '#description' => t('The maximum number of images to show in the node body. 0 will not show images. Leave blank to show all images.'), + ); + $form['node_images']['node_images_teaser_format'] = array( + '#type' => 'radios', + '#title' => t('Image format in node teaser'), + '#default_value' => variable_get('node_images_teaser_format_'.$type, 'thumbs'), + '#options' => array('thumbs' => t('Thumbnails'), 'fullsize' => t('Full size images')), + '#description' => t('Image format in node teaser.'), + ); + $form['node_images']['node_images_body_format'] = array( + '#type' => 'radios', + '#title' => t('Image format in node body'), + '#default_value' => variable_get('node_images_body_format_'.$type, 'thumbs'), + '#options' => array('thumbs' => t('Thumbnails'), 'fullsize' => t('Full size images')), + '#description' => t('Image format in node body.'), + ); + } + + if (isset($form['type']) && isset($form['#node'])) { + $node = $form['#node']; + if (!isset($node->nid) && $form['type']['#value'] .'_node_form' == $form_id && _node_images_access('create', $node)) { + // Attachments fieldset + $form['node_images'] = array( + '#type' => 'fieldset', + '#title' => t('Node images'), + '#collapsible' => TRUE, + '#collapsed' => empty($node->node_images), + '#description' => t('Changes made to the node images are not permanent until you save this post.'), + '#weight' => 30, + ); + + require_once(drupal_get_path('module', 'node_images').'/node_images.pages.inc'); + $form = _node_images_edit_page($form_state, $node, $form); + } + } +} + +/** + * Implementation of hook_nodeapi(). + */ +function node_images_nodeapi(&$node, $op, $teaser, $page) { + // test if images are allowed for this node + if ($op == 'load' || $op == 'view') { + if (variable_get('node_images_position_'.$node->type, 'hide') == 'hide') return; + // fire an additional hook for the specific node type + // i.e. node images might be a pro feature, so only subscribed users can view them + // if the hook is not present in the node type module, images are loaded by default + $show = module_invoke($node->type, 'node_images', $op, $node); + if ($show === FALSE) return; + } + + switch ($op) { + case 'insert': + if (_node_images_access('create', $node)) { + _node_images_save_attachments($node); + if (variable_get('node_images_position_'.$node->type, 'hide') != 'hide') { + drupal_set_message(t('Click the !images tab to edit node images.', + array('!images' => l(t('Images'), 'node/'.$node->nid.'/images')))); + } + } + break; + + case 'delete': + // Delete image and thumbnail files + $sql = db_query('SELECT id, filepath, thumbpath FROM {node_images} WHERE nid=%d', $node->nid); + while ($r = db_fetch_object($sql)) { + file_delete($r->filepath); + file_delete($r->thumbpath); + module_invoke('i18nstrings', 'remove_string', _node_images_build_i18nstrings_context($r)); + } + // Delete all images associated with the node + db_query('DELETE FROM {node_images} WHERE nid=%d', $node->nid); + break; + + case 'prepare': + // Initialize $_SESSION['node_images'] if no post occurred. + // This clears the variable from old forms and makes sure it + // is an array to prevent notices and errors in other parts + // of node_images.module. + if (!$_POST) { + $_SESSION['node_images'] = array(); + } + break; + + case 'view': + $node->node_images = node_images_load($node, $teaser, $page); + if (empty($node->node_images)) return; + + // search for a themed view for the current node type + $nodetype_function = 'theme_'.$node->type.'_node_images_view'; + if (function_exists($nodetype_function)) { + $output = $nodetype_function($node, $teaser, $page); + } + else { + // use the default theme function + $output = theme('node_images_view', $node, $teaser, $page); + } + $node->node_images = $output; + + $output = '
'.$output.'
'; + $position = variable_get('node_images_position_'.$node->type, 'hide'); + switch ($position) { + case 'node_template': + case 'hide': + break; + + case 'above': + $node->content['node_images'] = array('#value' => $output, '#weight' => -1); + break; + + default: + $node->content['node_images'] = array('#value' => $output, '#weight' => 1); + break; + } + return $node; + } +} + +/** + * Implementation of hook_theme() + */ +function node_images_theme() { + return array( + 'node_images_form_list' => array( + 'arguments' => array('form' => NULL), + ), + 'node_images_form_upload' => array( + 'arguments' => array('form' => NULL), + ), + 'node_images_attach_list' => array( + 'arguments' => array('form' => NULL), + ), + 'node_images_view' => array( + 'arguments' => array('node' => NULL, 'teaser' => NULL, 'page' => NULL), + ), + 'node_images_gallery' => array( + 'arguments' => array('element' => NULL), + ), + 'node_images_gallery_thumbs' => array( + 'arguments' => array('thumbs' => NULL, 'cols' => 2), + ), + ); +} + +/** + * Implementation of hook_link(). + */ +function node_images_link($type, $node = null, $teaser = false) { + $links = array(); + + if ($type == 'node' && $node->nid && variable_get('node_images_position_'.$node->type, 'hide') != 'hide') { + if (node_access('update', $node) && user_access('create node images')) { + $links['node_images_edit'] = array( + 'title' => t('Edit node images'), + 'href' => "node/$node->nid/images", + ); + } + if (count($node->node_images) && variable_get('node_images_gallery_link_'.$node->type, TRUE)) { + $links['node_images_gallery'] = array( + 'title' => t('Open the image gallery'), + 'href' => "node/$node->nid/image_gallery", + ); + } + } + + return $links; +} + +/** + * Implementation of hook_user(). + */ +function node_images_user($type, &$edit, &$user, $category = NULL) { + switch ($type) { + case 'delete': + // Set uid=0 for images uploaded by the deleted user + db_query('UPDATE {node_images} SET uid=0 WHERE uid=%d', $user->uid); + } +} + +/** + * Implementation of hook_file_download(). + * Find out if an image is accessible when download method is set to private + */ +function node_images_file_download($file) { + $path = file_create_path($file); + $result = db_query("SELECT * FROM {node_images} WHERE filepath='%s' OR thumbpath='%s'", $path, $path); + if ($file = db_fetch_object($result)) { + $node = node_load($file->nid); + if (node_access('view', $node)) { + if ($path == $file->thumbpath) { + // update header info if thumb is requested + $name = mime_header_encode(basename($file->thumbpath)); + $size = $file->thumbsize; + } + else { + $name = mime_header_encode($file->filename); + $size = $file->filesize; + } + $type = mime_header_encode($file->filemime); + return array( + 'Content-Type: '. $type .'; name='. $name, + 'Content-Length: '. $size, + 'Cache-Control: private' + ); + } + else { + return -1; + } + } +} + +/** + * Implementation of hook_locale(). + */ +function node_images_locale($op = 'groups', $group = NULL) { + switch ($op) { + case 'groups': + return array('node_images' => t('Node images')); + } +} + + +/************************************************************ + * Upload functions + ************************************************************/ + +function _node_images_attach_form($form_state, $node) { + global $user; + + $form = array( + '#cache' => TRUE, + ); + $form['node_images'] = _node_images_attach_list($node); + + if (_node_images_access('create', $node)) { + $limits = _node_images_file_limits($user); + $form['node_images_file'] = array( + '#type' => 'file', + '#title' => t('Attach new image'), + '#size' => 40, + '#description' => ($limits['resolution'] ? t('Images larger than %resolution will be resized. ', array('%resolution' => $limits['resolution'])) : '') . t('The maximum upload size is %filesize. Only files with the following extensions may be uploaded: %extensions. ', array('%extensions' => $limits['extensions'], '%filesize' => format_size($limits['file_size']))), + '#weight' => 30, + ); + $form['node_images_attach'] = array( + '#type' => 'submit', + '#value' => t('Attach'), + '#name' => 'attach', + '#ahah' => array( + 'path' => 'node_images/js', + 'wrapper' => 'node_images-attach-wrapper', + 'progress' => array('type' => 'bar', 'message' => t('Please wait...')), + ), + '#submit' => array('node_form_submit_build_node'), + '#weight' => 31, + ); + } + + return $form; +} + +function _node_images_attach_list($node) { + $form = array( + '#theme' => 'node_images_attach_list', + '#tree' => TRUE, + ); + + if (!empty($node->node_images) && is_array($node->node_images)) { + $delta = max(count($node->node_images), 10); + foreach ($node->node_images as $key => $file) { + $file = (object)$file; + $thumb = file_create_url($file->filepath); + $row = array(); + $row['fid'] = array('#type' => 'value', '#value' => $file->fid); + $row['filepath'] = array('#type' => 'value', '#value' => $file->filepath); + $row['delete'] = array('#type' => 'checkbox'); + $row['list'] = array('#type' => 'checkbox', '#default_value' => $file->list); + $row['thumbnail'] = array('#value' => ''); + $row['description'] = array('#type' => 'textfield', '#default_value' => $file->description, '#maxlength' => 255, '#size' => 40); + $row['filename'] = array('#type' => 'value', '#value' => $file->filename); + $row['filemime'] = array('#type' => 'value', '#value' => $file->filemime); + $row['filesize'] = array('#value' => format_size($file->filesize)); + $row['weight'] = array('#type' => 'weight', '#delta' => $delta, '#default_value' => $file->weight); + $form[$key] = $row; + } + } + return $form; +} + +/** + * Save the current uploaded file. + * + * @return + * An object containing the file information, or FALSE in the event of an error. + */ +function _node_images_file_upload($form, &$form_state, $node = NULL) { + global $user; + + $source = 'node_images_file'; + if (!$_FILES['files']['name'][$source]) return; + + // Error handling in file.inc doesn't work if temp file is not created + if (!is_uploaded_file($_FILES['files']['tmp_name'][$source])) { + drupal_set_message(t('File upload error.'), 'error'); + return FALSE; + } + + $limits = _node_images_file_limits($user); + $validators = array( + 'file_validate_is_image' => array(), + 'file_validate_image_resolution' => array($limits['resolution']), + 'file_validate_size' => array($limits['file_size']), + '_node_images_file_validate_name' => array(), + ); + + // Save new file upload. + $dest = ($node ? _node_images_get_directory('', $user, $node) : file_directory_path()); + if ($file = file_save_upload($source, $validators, $dest)) { + // remove uploaded file from the files table, so it won't be deleted by system_cron() + db_query('DELETE FROM {files} WHERE fid = %d', $file->fid); + + return $file; + } + + return FALSE; +} + +/** + * Save new uploads and store them in the session to be associated to the node + * on _node_images_save_attachments. + * + * @param $node + * A node object to associate with uploaded images. + */ +function node_images_node_form_submit($form, &$form_state, $node = NULL) { + global $user; + + // Save new file upload. + $file = _node_images_file_upload($form, $form_state); + if (!$file) return; + + $file->weight = 0; + $file->list = 1; + $parts = pathinfo($file->filename); + $file->description = (isset($parts['filename']) ? $parts['filename'] : substr($file->filename, 0, strrpos($file->filename, '.'))); + + $_SESSION['node_images'][$file->fid] = $file; + + // Attach session files to node. + if (!empty($_SESSION['node_images'])) { + foreach ($_SESSION['node_images'] as $fid => $file) { + if (!isset($form_state['values']['node_images'][$fid]['filepath'])) { + $form_state['values']['node_images'][$fid] = (array)$file; + } + } + } + + // Order the form according to the set file weight values. + if (!empty($form_state['values']['node_images'])) { + $microweight = 0.001; + foreach ($form_state['values']['node_images'] as $fid => $file) { + if (is_numeric($fid)) { + $form_state['values']['node_images'][$fid]['#weight'] = $file['weight'] + $microweight; + $microweight += 0.001; + } + } + uasort($form_state['values']['node_images'], 'element_sort'); + } +} + +function _node_images_save_attachments(&$node) { + global $user; + + if (empty($node->node_images) || !is_array($node->node_images)) { + return; + } + + $result = _node_images_check_directory(NULL, $user, $node); + if (!$result) return FALSE; + $dest = _node_images_get_directory('', $user, $node); + + foreach ($node->node_images as $fid => $file) { + // Convert file to object for compatibility + $file = (object)$file; + if (!$file->delete) { + $result = file_move($file->filepath, $dest); + if (!$result) { + drupal_set_message(t('Unable to copy image "%image".', + array('%image' => $file->filename)), 'error'); + } + + // If we made it this far it's safe to record this file in the database. + $file->filepath = $dest .'/'. basename($file->filepath); + $thumb = _node_images_create_thumbnail($file->filepath); + $file->uid = $user->uid; + $file->nid = $node->nid; + $file->timestamp = time(); + $file->filesize = filesize($file->filepath); + $file->thumbpath = $thumb->filepath; + $file->thumbsize = $thumb->filesize; + $file->status = 1; + drupal_write_record('node_images', $file); + } + else { + file_delete($file->filepath); + db_query('DELETE FROM {files} WHERE fid = %d', $fid); + + // Remove it from the session in the case of new uploads, + // that you want to disassociate before node submission. + unset($_SESSION['node_images'][$fid]); + } + } + + // Empty the session storage after save. We use this variable to track files + // that haven't been related to the node yet. + unset($_SESSION['node_images']); +} + +/** + * Menu-callback for JavaScript-based uploads. + */ +function _node_images_js() { + global $user; + $output = ''; + + // Load the form from the Form API cache. + $cache = cache_get('form_'. $_POST['form_build_id'], 'cache_form'); + $form = $cache->data; + $form_state = array('values' => $_POST); + + $node = (object)$_POST; + $type = (isset($cache->data['#node'])? $cache->data['#node']->type : NULL); + + if ($type && $_POST['form_id'] == $type.'_node_form') { + // upload from node creation page + + $node->type = $type; + $node->uid = $user->uid; + + // Handle new uploads, and merge tmp files into node_images. + node_images_node_form_submit($form, $form_state); + if (!empty($form_state['values']['node_images'])) { + foreach ($form_state['values']['node_images'] as $fid => $file) { + if (is_numeric($fid)) { + $node->node_images[$fid] = $file; + if (0 && !isset($file['filepath'])) { + $node->node_images[$fid] = $node_files[$fid]; + } + } + } + } + + $form = _node_images_attach_form($form_state, $node); + + // Update the default values changed in the $_POST array. + $files = isset($_POST['node_images']) ? $_POST['node_images'] : array(); + foreach ($files as $fid => $file) { + if (is_numeric($fid)) { + $form['node_images'][$fid]['description']['#default_value'] = $file['description']; + $form['node_images'][$fid]['list']['#default_value'] = isset($file['list']) ? 1 : 0; + $form['node_images'][$fid]['remove']['#default_value'] = isset($file['remove']) ? 1 : 0; + $form['node_images'][$fid]['weight']['#default_value'] = $file['weight']; + $form['node_images'][$fid]['filemime']['#value'] = $file['filemime']; + } + } + } + elseif ($nid = $_POST['nid']) { + // upload from node images page + if ($node = node_load($nid)) { + $form = array(); + $file = _node_images_upload_submit($form, $form_state, $node); + $form = _node_images_edit_form($form_state, $node); + } + } + + // Add the new element to the stored form state and resave. + $cache->data['node_images']['wrapper'] = array_merge($cache->data['node_images']['wrapper'], $form); + cache_set('form_'. $_POST['form_build_id'], $cache->data, 'cache_form', $cache->expire); + + // Render the form for output. + $form += array( + '#post' => $_POST, + '#programmed' => FALSE, + '#tree' => FALSE, + '#parents' => array(), + ); + drupal_alter('form', $form, array(), 'node_images_js'); + + $form_state = array('submitted' => FALSE, 'values' => $_POST); + $form = form_builder('_node_images_js', $form, $form_state); + $form['#sorted'] = TRUE; + + $output .= theme('status_messages') . drupal_render($form); + + // We send the updated node images form. + print drupal_to_js(array('status' => TRUE, 'data' => $output)); + exit(); +} + +/** + * Upload a new image and associate it to the current node. + * + * @param $form + * The form structure. + * @param $form_state + * An associative array containing the current state of the form. + * @param $node + * A node object to associate with uploaded image. + * @return + * the uploaded image file, or FALSE if upload fails + */ +function _node_images_upload_submit($form, &$form_state, $node = NULL) { + global $user; + + if (!$node && is_numeric(arg(1))) { + $node = node_load(arg(1)); + } + else { + } + if (!$node || !isset($node->nid)) { + drupal_set_message(t('Node not valid.'), 'error'); + } + + if (!_node_images_access('create', $node)) { + drupal_set_message(t('You are not allowed to upload node images.'), 'error'); + return FALSE; + } + + if ($file = _node_images_file_upload($form, $form_state, $node)) { + $description = $form_state['values']['description']; + if (trim($description) == '') { + $parts = pathinfo($file->filename); + $description = (isset($parts['filename']) ? $parts['filename'] : substr($file->filename, 0, strrpos($file->filename, '.'))); + } + $thumb = _node_images_create_thumbnail($file->filepath); + + // If we made it this far it's safe to record this file in the database. + $file->uid = $user->uid; + $file->nid = $node->nid; + $file->timestamp = time(); + $file->thumbpath = $thumb->filepath; + $file->thumbsize = $thumb->filesize; + $file->description = $description; + $file->status = 1; + $file->weight = $form_state['values']['weight']; + $file->list = (isset($form_state['values']['list']) ? $form_state['values']['list'] : 1); + + $result = drupal_write_record('node_images', $file); + if ($result == SAVED_NEW) { + drupal_set_message(t('The image has been successfully uploaded.')); + } + return $file; + } + + return FALSE; +} + + +/************************************************************ + * Theme functions + ************************************************************/ + +/** + * Theme the list of attached images. + * + * @ingroup themeable + */ +function theme_node_images_attach_list(&$form) { + $header = array('', t('Delete'), t('List'), t('Thumbnail'), t('Description'), t('Weight'), t('Size')); + drupal_add_tabledrag('node_images_attach_list', 'order', 'sibling', 'node_images-weight'); + + foreach (element_children($form) as $key) { + if (!$form[$key]['thumbnail']) continue; + + // Add class to group weight fields for drag and drop. + $form[$key]['weight']['#attributes']['class'] = 'node_images-weight'; + + $row = array(''); + $row[] = drupal_render($form[$key]['delete']); + $row[] = drupal_render($form[$key]['list']); + $row[] = drupal_render($form[$key]['thumbnail']); + $row[] = array('data' => drupal_render($form[$key]['description']), 'width' => '100%'); + $row[] = drupal_render($form[$key]['weight']); + $row[] = array('data' => drupal_render($form[$key]['filesize']), 'class' => 'nowrap'); + $rows[] = array('data' => $row, 'class' => 'draggable'); + } + + $output = ''; + if (!empty($rows)) $output .= theme('table', $header, $rows, array('id' => 'node_images_attach_list')); + $output .= drupal_render($form); + return $output; +} + +/** + * Show node images in the node view. + */ +function theme_node_images_view($node, $teaser, $page, $block = FALSE, $count = NULL, $format = NULL) { + if (arg(2) == 'image_gallery' || empty($node->node_images)) return; + + $output = ''; + $i = 0; + + // set maximum number of images for teaser/body + $view = ($teaser ? 'teaser' : 'body'); + if (!$count) { + $count = variable_get('node_images_'.$view.'_images_'.$node->type, 2); + } + if (isset($count) && $count == '0') return; + + // set image format + if (!$format) { + $format = variable_get('node_images_'.$view.'_format_'.$node->type, 'thumbs'); + } + + foreach((array)$node->node_images as $id => $image) { + $description = check_plain($image->description); + $pattern = '%description'; + $thumb = strtr($pattern, array('%path'=>file_create_url($image->thumbpath), '%description'=>$description)); + $fullsize = strtr($pattern, array('%path'=>file_create_url($image->filepath), '%description'=>$description)); + + if ($info = @getimagesize($image->filepath)) { + $width = $info[0] + 36; + $height = $info[1] + 36; + } + else { + $width = 420; + $height = 315; + } + + if ($format == 'thumbs') { + $output .= ''.$thumb.' '; + } + else { + $output .= $fullsize.' '; + } + if ($count>0 && ++$i >= $count) break; + } + + if ($block && $count < count($node->node_images)) { + $output .= ''; + } + + return $output; +} + + +/************************************************************ + * Helper functions - other + ************************************************************/ + +function node_images_load($node, $teaser = 0, $page = 0) { + // show images only on teaser and page views + if ($teaser === FALSE && $page === FALSE) return; + + // load node images for the current node + $nid = ($node->tnid && $node->tnid != $node->nid ? $node->tnid : $node->nid); + $images = array(); + $where = 'WHERE nid=%d'; + if ($teaser || $page) $where .= ' AND status=1 AND list=1'; + $sql = db_query('SELECT * FROM {node_images} ' .$where. ' ORDER BY weight', $nid); + while ($r = db_fetch_object($sql)) { + $images[$r->id] = $r; + } + return $images; +} + +/** + * Check access to add, edit or delete node images. + */ +function _node_images_access($op, $node, $file = NULL) { + global $user; + + $type = node_get_types('type', $node); + if (!isset($type->type)) return FALSE; + + $type = strtolower($type->type); + if ($op == 'create') { + return (user_access('add node images to any '. $type .' content') || (user_access('add node images to own '. $type .' content') && ($user->uid == $node->uid)) || user_access('edit any node image')); + } + if ($op == 'update') { + return (user_access('edit any node image') || (user_access('edit own node images') && ($user->uid == $file->uid))); + } + if ($op == 'delete') { + return (user_access('delete any node image') || (user_access('delete own node images') && ($user->uid == $file->uid))); + } + + return FALSE; +} + +/** + * Checks if the upload.module is enabled, and the existence of an image toolkit. + * + * @param $form_element + * The form element containing the name of the directory to check. + */ +function _node_images_check_settings() { + // Make sure we've got a working toolkit + if (!image_get_toolkit()) { + drupal_set_message(t('Make sure you have a working image toolkit installed and enabled, for more information see: %settings', array('%settings' => l(t('the settings page'), 'admin/settings/image-toolkit'))), 'error'); + } +} + +/** + * Checks the existence of the destination directory. + * The directory can be specified either in $form_element (when called from the admin settings) + * or in $path (when called from the node images edit page). + * + * @param $form_element + * The form element containing the name of the directory to check. + */ +function _node_images_check_directory($form_element = NULL, $u = NULL, $node = NULL) { + $dir = (isset($form_element['#value']) ? $form_element['#value'] : ''); + $path = _node_images_get_directory($dir, $u, $node); + $form_item = isset($form_element['#parents'][0]) ? $form_element['#parents'][0] : NULL; + $result = _node_images_mkdir_recursive($path, FILE_CREATE_DIRECTORY, $form_element['#parents'][0]); + if ($form_element) return $form_element; + return $result; +} + +function _node_images_mkdir_recursive($path, $mode, $form_item) { + if (file_check_directory($path, $mode, $form_item)) return TRUE; + + $folders = explode('/', $path); + $dirs = array(); + foreach ($folders as $folder) { + $dirs[] = $folder; + $dir = implode('/', $dirs); + if (!file_check_directory($dir, $mode, $form_item)) { + return FALSE; + } + } + return TRUE; +} + +/** + * Return the destination directory. + */ +function _node_images_get_directory($dir = '', $u = NULL, $node = NULL) { + global $user; + + if (!$u) $u = $user; + $variables = array('%uid' => $u->uid, '%username' => $u->name); + $variables['%nid'] = (isset($node->nid) ? $node->nid : 0); + $variables['%type'] = ($node ? $node->type: 'node'); + $path = file_directory_path().'/'; + $path .= ($dir ? trim($dir, '/') : variable_get('node_images_path', 'node_images')); + return strtr($path, $variables); +} + +/** + * Validate, apply md5 and normalize filename. + * + * @param $file + * A Drupal file object. + * @return + * An array. If the file name is not valid, it will contain an error message. + */ +function _node_images_file_validate_name(&$file) { + $errors = array(); + + if (variable_get('node_images_md5name', FALSE)) { + $parts = pathinfo($file->destination); + $file->destination = $parts['dirname'].'/'.substr(md5($dest.'/'.$file->filepath), 0, 16).'.'.$parts['extension']; + } + $file->filepath = preg_replace(array('/[ \']+/', '/["]+/'), array('_', ''), $file->filepath); + + if (strlen($file->filename) > 255) { + $errors[] = t('Its name exceeds the 255 characters limit. Please rename the file and try again.'); + } + return $errors; +} + +/** + * Create the thumbnail for the uploaded image. + */ +function _node_images_create_thumbnail($source, $suffix = '_tn') { + $size = variable_get('node_images_thumb_resolution', '100x100'); + list($width, $height) = explode('x', $size); + $dest = preg_replace('!(\.[^/.]+?)?$!', "$suffix\\1", $source, 1); + + if ($size = @getimagesize($source)) { + if (!image_scale($source, $dest, $width, $height)) { + file_copy($source, $dest, FILE_EXISTS_REPLACE); + } + $info = image_get_info($dest); + $thumb = new stdClass(); + $thumb->filename = basename($dest); + $thumb->filepath = $dest; + $thumb->filesize = $info['file_size']; + $thumb->filemime = $info['mime_type']; + return $thumb; + } + return NULL; +} + +/** + * Determine the limitations on files that a given user may upload. The user + * may be in multiple roles so we select the most permissive limitations from + * all of their roles. + * + * @param $user + * A Drupal user object. + * @return + * An associative array with the following keys: + * 'extensions' + * A white space separated string containing the allowed extensions. + * 'file_size' + * The maximum size of a file upload in bytes. + * 'resolution' + * A string specifying the maximum resolution of images. + */ +function _node_images_file_limits($user) { + return array( + 'extensions' => variable_get('node_images_extensions', 'jpg jpeg gif png'), + 'file_size' => variable_get('node_images_file_limit', 1) * 1024 * 1024, + 'resolution' => variable_get('node_images_large_resolution', 0), + ); +} + +function _node_images_is_translation_source($node) { + global $language; + if ($node->tnid) return $node->nid == $node->tnid; + + if (module_exists('i18n')) { + $current = i18n_get_lang(); + $default = i18n_default_language(); + return $current == $default; + } + + return ($language->language == language_default('language')); + + $lang = ($node->language ? $node->language : language_default('language')); + return $lang == $language->language; +} + +function _node_images_build_i18nstrings_context($file) { + return 'node_images:node_image:'.$file->id.':description'; +} + +function _node_images_translate(&$file, $update = FALSE) { + global $language; + + //$current_language = $language->language; + //$default_language = language_default('language'); + $context = _node_images_build_i18nstrings_context($file); + if (module_exists('i18nstrings') && function_exists('tt')) { + $file->description = tt($context, $file->description, NULL, $update); + } +} \ No newline at end of file diff --git a/html/modules/node_images/node_images.pages.inc b/html/modules/node_images/node_images.pages.inc new file mode 100644 index 0000000..eeeb76e --- /dev/null +++ b/html/modules/node_images/node_images.pages.inc @@ -0,0 +1,393 @@ + '
', + '#suffix' => '
', + ); + + if (!_node_images_check_directory(NULL, $user, $node)) { + $form['node_images']['#description'] = t('Node images are disabled. The file directories have not been properly configured.'); + if (user_access('administer site configuration')) { + $form['node_images']['#description'] .= ' '. t('Please visit the node_images configuration page.', array('@admin-node-images' => url('admin/settings/node_images'))); + } + else { + $form['node_images']['#description'] .= ' '. t('Please contact the site administrator.'); + } + if ($page) drupal_set_message($form['node_images']['#description'], 'error'); + } + else { + if ($page) { + $form['node_images']['wrapper'] += _node_images_edit_form($form_state, $node); + if (variable_get('node_images_position_'.$node->type, 'hide') == 'hide') { + drupal_set_message(t('Node images are disabled in the view page for the current node type.'), 'warning'); + } + $form['nid'] = array('#type' => 'value', '#value' => $node->nid); + } + else { + $form['node_images']['wrapper'] += _node_images_attach_form($form_state, $node); + $form['#submit'][] = 'node_images_node_form_submit'; + } + + $form['#attributes']['enctype'] = 'multipart/form-data'; + } + + return $form; +} + +function _node_images_edit_form(&$form_state, $node) { + global $user; + $node_images = node_images_load($node); + $is_translation_source = _node_images_is_translation_source($node); + + $form = array( + '#cache' => TRUE, + ); + + if (_node_images_access('create', $node) && $is_translation_source) { + $form['new'] = array( + '#type' => 'fieldset', + '#title' => t('Upload a new image'), + '#collapsible' => TRUE, + '#collapsed' => !empty($node_images), + '#weight' => -30, + '#theme' => 'node_images_form_upload', + ); + $limits = _node_images_file_limits($user); + $form['new']['description'] = array( + '#type' => 'textfield', + '#title' => t('Description'), + '#size' => 50, + '#maxlength' => 255, + '#default_value' => '', + '#description' => t('Enter a description for the new image, max 255 chars.'), + ); + $form['new']['weight'] = array( + '#type' => 'weight', + '#title' => t('Weight'), + ); + $form['new']['list'] = array( + '#type' => 'radios', + '#title' => t('List'), + '#options' => array(1 => t('Yes'), 0 => t('No')), + '#default_value' => 1, + ); + $form['new']['node_images_file'] = array( + '#type' => 'file', + '#title' => t('Attach new image'), + '#size' => 40, + '#description' => ($limits['resolution'] ? t('Images larger than %resolution will be resized. ', array('%resolution' => $limits['resolution'])) : '') . t('The maximum upload size is %filesize. Only files with the following extensions may be uploaded: %extensions. ', array('%extensions' => $limits['extensions'], '%filesize' => format_size($limits['file_size']))), + ); + $form['new']['node_images_attach'] = array( + '#type' => 'submit', + '#value' => t('Upload'), + '#name' => 'attach', + '#ahah' => array( + 'path' => 'node_images/js', + 'wrapper' => 'node_images-attach-wrapper', + 'progress' => array('type' => 'bar', 'message' => t('Please wait...')), + ), + '#submit' => array('_node_images_upload_submit'), + ); + // This value is used in _node_images_js(). + $form['new']['nid'] = array('#type' => 'hidden', '#value' => $node->nid); + } + + $form['node_images'] = array( + '#theme' => 'node_images_form_list', + '#tree' => TRUE, + '#type' => 'fieldset', + '#title' => t('Image list'), + '#weight' => 10, + ); + + if (!empty($node_images) && is_array($node_images)) { + // set an appropriate value for delta in weight selectbox + $delta = db_result(db_query('SELECT MAX(ABS(weight)) FROM {node_images} WHERE nid=%d', $node->nid)); + $delta += count($node_images); + $authors = array(); + $submit = FALSE; + $form['node_images']['is_translation_source'] = array('#type' => 'value', '#value' => $is_translation_source); + + foreach ($node_images as $key => $file) { + $file = (object)$file; + $update_access = _node_images_access('update', $node, $file); + $delete_access = _node_images_access('delete', $node, $file); + if (!$submit && ($update_access || $delete_access)) $submit = TRUE; + + if (!isset($authors[$file->uid])) { + $authors[$file->uid] = user_load(array('uid' => $file->uid)); + } + $url = file_create_url($file->filepath); + $thumb = file_create_url($file->thumbpath); + $row = array(); + $row['id'] = array('#type' => 'value', '#value' => $file->id); + $row['thumbnail'] = array('#value' => ''); + + if ($is_translation_source) { + $row['delete'] = array('#type' => 'checkbox', '#attributes' => ($delete_access ? array() : array('disabled' => 'true'))); + $row['list'] = array('#type' => 'checkbox', '#default_value' => $file->list, + '#attributes' => ($update_access && $is_translation_source ? array() : array('disabled' => 'true'))); + _node_images_translate($file); + $element = array('#type' => 'textfield', '#default_value' => $file->description, '#maxlength' => 255, '#size' => 40); + } + else { + _node_images_translate($file); + $element = array('#value' => $file->description); + } + $row['description'] = $element; + + $author = $authors[$file->uid]; + $row['author'] = array('#value' => theme('username', $author)); + + $row['filepath'] = array('#value' => l($file->filepath, file_create_url($file->filepath), array('attributes' => array('target' => '_blank')))); + $row['filesize'] = array('#value' => format_size($file->filesize)); + $row['date'] = array('#value' => format_date($file->timestamp)); + $row['weight'] = array('#type' => 'weight', '#delta' => $delta, '#default_value' => $file->weight); + $form['node_images']['images'][$key] = $row; + } + + if ($submit && $is_translation_source) { + $form['node_images']['submit'] = array('#type' => 'submit', '#value' => t('Save changes'), '#submit' => array('_node_images_form_submit')); + } + } + + return $form; +} + +/** + * Save changes to the image list. + * + * @param $form + * The form structure. + * @param $form_state + * An associative array containing the current state of the form. + */ +function _node_images_form_submit($form, &$form_state) { + global $user, $language; + + $node = node_load($form_state['values']['nid']); + if (!isset($node->nid)) return drupal_set_message(t('Unable to load node.'), 'error'); + + // Get languages + $current_language = $language->language; + $default_language = language_default('language'); + + foreach($form_state['values']['node_images']['images'] as $id => $values) { + $file = db_fetch_object(db_query('SELECT id, filepath, thumbpath, uid, nid FROM {node_images} WHERE id=%d AND nid=%d', + $id, $node->nid)); + if ($values['delete']) { + // check delete access for the current image + $delete_access = _node_images_access('delete', $node, $file); + if (!$delete_access) continue; + + // delete selected image + file_delete($file->filepath); + file_delete($file->thumbpath); + db_query('DELETE FROM {node_images} WHERE id=%d AND nid=%d', $id, $node->nid); + module_invoke('i18nstrings', 'remove_string', _node_images_build_i18nstrings_context($file)); + } + else { + // check update access for the current image + $update_access = _node_images_access('update', $node, $file); + if (!$update_access) continue; + + $update = array('id', 'nid'); + $object = (object)$values; + $object->nid = $node->nid; + _node_images_translate($object, TRUE); + drupal_write_record('node_images', $object, $update); + } + } + drupal_set_message(t('The changes have been saved.')); +} + +/** + * Theme the upload form. + * + * @ingroup themeable + */ +function theme_node_images_form_upload(&$form) { + $output = ''; + foreach (array('description', 'weight') as $key) { + $output .= '
'; + $output .= drupal_render($form[$key]); + $output .= '
'; + } + $output .= '
'; + $output .= drupal_render($form['list']); + $output .= '
'; + + $output .= '
'; + $output .= drupal_render($form); + return $output; +} + +/** + * Theme the image list. + * + * @ingroup themeable + */ +function theme_node_images_form_list(&$form) { + $is_translation_source = $form['is_translation_source']['#value']; + + $header = array(); + if ($is_translation_source) { + $header = array('', t('Delete'), t('List')); + } + $header = array_merge($header, array(t('Thumbnail'), t('Description and info'), t('Weight'), t('Size'))); + drupal_add_tabledrag('node_images_list', 'order', 'sibling', 'node_images-weight'); + + foreach (element_children($form['images']) as $key) { + if (!$form['images'][$key]['thumbnail']) continue; + + // Add class to group weight fields for drag and drop. + $form['images'][$key]['weight']['#attributes']['class'] = 'node_images-weight'; + + $info = '
'.t('Author: !name', array('!name' => drupal_render($form['images'][$key]['author']))).'
'; + $info .= '
'.t('Uploaded on: %date', array('%date' => drupal_render($form['images'][$key]['date']))).'
'; + $info .= '
'.t('Path: !path', array('!path' => drupal_render($form['images'][$key]['filepath']))).'
'; + + $row = array(); + if ($is_translation_source) { + $row[] = ''; + $row[] = drupal_render($form['images'][$key]['delete']); + $row[] = drupal_render($form['images'][$key]['list']); + } + $row[] = drupal_render($form['images'][$key]['thumbnail']); + $row[] = array('data' => drupal_render($form['images'][$key]['description']).$info, 'width' => '100%'); + $row[] = drupal_render($form['images'][$key]['weight']); + $row[] = array('data' => drupal_render($form['images'][$key]['filesize']), 'class' => 'nowrap'); + if ($is_translation_source) { + $rows[] = array('data' => $row, 'class' => 'draggable'); + } + else { + $rows[] = $row; + } + } + + $output = ' '; + if (!empty($rows)) $output .= theme('table', $header, $rows, array('id' => 'node_images_list')); + $output .= drupal_render($form['translation_warning']); + $output .= drupal_render($form); + return $output; +} + + +/************************************************************ + * Gallery functions + ************************************************************/ + +function _node_images_gallery($node) { + $node->node_images = node_images_load($node, 0, 1); + if (empty($node->node_images)) { + drupal_set_message(t('No images uploaded for this content.')); + if (user_access('create node images') && node_access('update', $node)) { + $output = t('Click here to upload new images.', array('!url' => url('node/'.$node->nid.'/images'))); + } + return '

'.$output.'

'; + } + + $settings = array('images' => array()); + + $i = 1; + $thumbs = array(); + foreach ($node->node_images as $id => $image) { + // i18n support for description + if (module_exists('i18nstrings')) { + // translate description + to('node_images:node_image:'.$image->id.':description', $image, array('description')); + } + + $thumb_path = file_create_url($image->thumbpath); + $thumbs[$i] = array( + 'src' => $thumb_path, + 'title' => $image->description, + ); + + $path = $image->filepath; + $info = image_get_info($path); + $settings['images'][$i++] = array( + 'src' => file_create_url($path), + 'title' => $image->description, + 'description' => $image->description, + 'width' => $info['width'], + 'height' => $info['height'], + 'thumb' => $thumb_path, + ); + } + $total = count($settings['images']); + + if ($total > 0) { + $current = (int)$_GET['page']; + $current = ($current < 1 || $current > $total) ? 1 : $current; + + $settings['current'] = $current; + $settings['total'] = $total; + + $path = drupal_get_path('module', 'node_images'); + drupal_add_js($path.'/node_images.js'); + drupal_add_js(array('node_images_slideshow' => array($node->nid => $settings)), 'setting'); + drupal_add_css($path.'/node_images.css'); + + $slideshow = array( + '#type' => 'node_images_gallery', + '#title' => $settings['images'][$current]['title'], + '#attributes' => array('id' => 'slideshow-'. $node->nid), + '#status' => array( + 'current' => $current, + 'total' => $total, + 'previous' => $current <= 1 ? $total : $current - 1, + 'next' => $current >= $total ? 1 : $current + 1, + ), + '#image' => $settings['images'][$current], + '#weight' => -5, + ); + } + + $output = '
'; + $output .= '
'.drupal_render($slideshow).'
'; + $output .= '
'.theme('node_images_gallery_thumbs', $thumbs).'
'; + $output .= '
'; + $output .= '
'.node_view($node); + + $title = t('Photo gallery for %title', array('%title' => $node->title)); + drupal_set_title($title); + return $output; +} + +function theme_node_images_gallery($element) { + $output = ' +
+ '. l(t('Previous'), $_GET['q'], array('attributes' => array('class' => 'previous'), 'query' => 'page='. $element['#status']['previous'])) .' | + '. t('Image !current of !total', array( + '!current' => ''. $element['#status']['current'] .'', + '!total' => ''. $element['#status']['total'] .'')) .' | + '. l(t('Next'), $_GET['q'], array('attributes' => array('class' => 'next'), 'query' => 'page='. $element['#status']['next'])) .' +
+ +

'. $element['#title'] .'

+ '; + return $output; +} + +function theme_node_images_gallery_thumbs($thumbs, $cols = 2) { + $output = ''; + foreach($thumbs as $id => $thumb) { + $url = url($_GET['q'], array('query' => 'page='.$id, 'absolute' => TRUE)); + $description = '
'.truncate_utf8($thumb['title'], 40, FALSE, TRUE).'
'; + $output .= '
'.l('', $url, array('attributes' => array('title' => $thumb['title']), 'html' => TRUE)).$description.'
'; + } + return $output; +} \ No newline at end of file diff --git a/html/modules/node_images/node_images.views.inc b/html/modules/node_images/node_images.views.inc new file mode 100644 index 0000000..95aa2bf --- /dev/null +++ b/html/modules/node_images/node_images.views.inc @@ -0,0 +1,87 @@ + 'node_images', + 'provider' => 'internal', + 'join' => array( + 'left' => array( + 'table' => 'node', + 'field' => 'nid' + ), + 'right' => array( + 'field' => 'nid' + ), + ), + 'fields' => array( + 'node_images_display' => array( + 'name' => t('Node Images: Display node images'), + 'notafield' => TRUE, + 'query_handler' => '_node_images_query_handler_display', + 'handler' => array( + '_node_images_views_handler_image_thumb' => t('Thumbnails'), + '_node_images_views_handler_image_fullsize' => t('Full size'), + ), + 'sortable' => false, + 'help' => t('Display all node images in one field.'), + ), + ), + 'filters' => array( + 'nid' => array( + 'name' => t('Node images: Has node images'), + 'operator' => array('=' => t('Exists')), + 'list' => 'views_handler_operator_yesno', + 'list-type' => 'select', + 'handler' => '_node_images_handler_images_exist', + 'help' => t('Filter whether the node has node images.'), + ), + ), + ); + return $tables; +} + +function _node_images_query_handler_display($field, $fieldinfo, &$query) { + $query->add_field('nid', 'node'); +} + +/** + * Views handler for displaying images. + */ +function _node_images_views_handler_image_thumb($fieldinfo, $fielddata, $value, $data, $thumb = TRUE) { + $node = node_load(array('nid' => $data->nid)); + $format = ($thumb ? 'thumbs' :'fullsize'); + return theme('node_images_view', $node, TRUE, FALSE, $format); +} + +/** + * Views handler for displaying images in links to a popup window + */ +function _node_images_views_handler_image_fullsize($fieldinfo, $fielddata, $value, $data) { + return _node_images_views_handler_image_thumb($fieldinfo, $fielddata, $value, $data, FALSE); +} + +/** + * Views handler for filtering + */ +function _node_images_handler_images_exist($op, $filter, $filterdata, &$query) { + switch ($op) { + case 'handler': + $query->ensure_table('node_images'); + if ($filter['value']) { + $query->set_distinct(); + $table_data = _views_get_tables(); + $joins = array('type' => 'inner'); + $joins = array_merge($joins, $table_data['node_images']['join']); + $query->joins['node_images'][1] = $joins; + } + else { + $query->add_where('ISNULL(node_images.id)'); + } + } +} \ No newline at end of file diff --git a/html/modules/node_images/translations/node_images.pot b/html/modules/node_images/translations/node_images.pot new file mode 100644 index 0000000..a241d42 --- /dev/null +++ b/html/modules/node_images/translations/node_images.pot @@ -0,0 +1,397 @@ +# $Id: node_images.pot,v 1.1.2.1 2009/02/03 13:45:50 stefano73 Exp $ +# +# LANGUAGE translation of Drupal (general) +# Copyright YEAR NAME +# Generated from files: +# node_images.admin.inc,v 1.1.2.2 2008/08/23 08:53:35 stefano73 +# node_images.pages.inc,v 1.1.2.4 2008/08/26 18:20:17 stefano73 +# node_images.module,v 1.11.2.15 2009/01/31 14:25:53 stefano73 +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PROJECT VERSION\n" +"POT-Creation-Date: 2009-02-03 12:31+0100\n" +"PO-Revision-Date: YYYY-mm-DD HH:MM+ZZZZ\n" +"Last-Translator: NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n" + +#: node_images.admin.inc:17 +msgid "Node images path" +msgstr "" + +#: node_images.admin.inc:20 +msgid "Subdirectory in the directory %dir where the node images will be stored. This directory has to exist and be writable by Drupal. You can use the following variables: %uid, %username, %nid" +msgstr "" + +#: node_images.admin.inc:25 +msgid "Resolution for large images" +msgstr "" + +#: node_images.admin.inc:29 +msgid "The size for large images in the node gallery, expressed as WIDTHxHEIGHT (e.g. 400x300)." +msgstr "" + +#: node_images.admin.inc:33 +msgid "Resolution for thumbnails" +msgstr "" + +#: node_images.admin.inc:37 +msgid "The thumbnail size expressed as WIDTHxHEIGHT (e.g. 100x75)." +msgstr "" + +#: node_images.admin.inc:41 +msgid "Default maximum file size per upload" +msgstr "" + +#: node_images.admin.inc:45 +msgid "The default maximum file size a user can upload. If a maximum resolution is set, the size will be checked after the file has been resized." +msgstr "" + +#: node_images.admin.inc:50 +msgid "Default permitted image extensions" +msgstr "" + +#: node_images.admin.inc:53 +msgid "Default image extensions that users can upload. Separate extensions with a space and do not include the leading dot." +msgstr "" + +#: node_images.admin.inc:57 +msgid "Maximum number of images" +msgstr "" + +#: node_images.admin.inc:61 +msgid "The maximum number of images a user can upload for each node." +msgstr "" + +#: node_images.admin.inc:65 +msgid "MD5 filenames" +msgstr "" + +#: node_images.admin.inc:66 +msgid "Override uploaded filenames with the MD5 hash of the file." +msgstr "" + +#: node_images.pages.inc:21 +msgid "Node images are disabled. The file directories have not been properly configured." +msgstr "" + +#: node_images.pages.inc:23 +msgid "Please visit the node_images configuration page." +msgstr "" + +#: node_images.pages.inc:26 +msgid "Please contact the site administrator." +msgstr "" + +#: node_images.pages.inc:34 +msgid "Node images are disabled in the view page for the current node type." +msgstr "" + +#: node_images.pages.inc:61 +msgid "Upload a new image" +msgstr "" + +#: node_images.pages.inc:70 node_images.module:705 +msgid "Description" +msgstr "" + +#: node_images.pages.inc:74 +msgid "Enter a description for the new image, max 255 chars." +msgstr "" + +#: node_images.pages.inc:78;249 node_images.module:705 +msgid "Weight" +msgstr "" + +#: node_images.pages.inc:82;247 node_images.module:705 +msgid "List" +msgstr "" + +#: node_images.pages.inc:83 +msgid "Yes" +msgstr "" + +#: node_images.pages.inc:83 +msgid "No" +msgstr "" + +#: node_images.pages.inc:88 node_images.module:376 +msgid "Attach new image" +msgstr "" + +#: node_images.pages.inc:90 node_images.module:378 +msgid "Images larger than %resolution will be resized. " +msgstr "" + +#: node_images.pages.inc:90 node_images.module:378 +msgid "The maximum upload size is %filesize. Only files with the following extensions may be uploaded: %extensions. " +msgstr "" + +#: node_images.pages.inc:94 +msgid "Upload" +msgstr "" + +#: node_images.pages.inc:99 node_images.module:388 +msgid "Please wait..." +msgstr "" + +#: node_images.pages.inc:111 +msgid "Image list" +msgstr "" + +#: node_images.pages.inc:162 +msgid "Save changes" +msgstr "" + +#: node_images.pages.inc:181 +msgid "Unable to load node." +msgstr "" + +#: node_images.pages.inc:213 +msgid "The changes have been saved." +msgstr "" + +#: node_images.pages.inc:247 node_images.module:705 +msgid "Delete" +msgstr "" + +#: node_images.pages.inc:249 node_images.module:705 +msgid "Thumbnail" +msgstr "" + +#: node_images.pages.inc:249 +msgid "Description and info" +msgstr "" + +#: node_images.pages.inc:249 node_images.module:705 +msgid "Size" +msgstr "" + +#: node_images.pages.inc:258 +msgid "Author: !name" +msgstr "" + +#: node_images.pages.inc:259 +msgid "Uploaded on: %date" +msgstr "" + +#: node_images.pages.inc:260 +msgid "Path: !path" +msgstr "" + +#: node_images.pages.inc:295 +msgid "No images uploaded for this content." +msgstr "" + +#: node_images.pages.inc:297 +msgid "Click here to upload new images." +msgstr "" + +#: node_images.pages.inc:365 +msgid "Photo gallery for %title" +msgstr "" + +#: node_images.pages.inc:373 +msgid "Previous" +msgstr "" + +#: node_images.pages.inc:374 +msgid "Image !current of !total" +msgstr "" + +#: node_images.pages.inc:377 +msgid "Next" +msgstr "" + +#: node_images.module:144;151 +msgid "Thumbnails" +msgstr "" + +#: node_images.module:17 +msgid "This page allows you to manage node images." +msgstr "" + +#: node_images.module:20 +msgid "This page allows you to view node images." +msgstr "" + +#: node_images.module:22 +msgid "You can upload new images or edit current images by switching to the default language." +msgstr "" + +#: node_images.module:26 +msgid "Image descriptions can be translated using the localization interface." +msgstr "" + +#: node_images.module:29 +msgid "Image descriptions can be translated using the localization interface and the i18nstrings module (available in the Internationalization package)." +msgstr "" + +#: node_images.module:65;105;162;355 +msgid "Node images" +msgstr "" + +#: node_images.module:66 +msgid "Control how to upload node images." +msgstr "" + +#: node_images.module:73;195 +msgid "Images" +msgstr "" + +#: node_images.module:83 +msgid "Gallery" +msgstr "" + +#: node_images.module:111 +msgid "Position" +msgstr "" + +#: node_images.module:113;121 +msgid "Do not show" +msgstr "" + +#: node_images.module:113 +msgid "Show above node body" +msgstr "" + +#: node_images.module:113 +msgid "Show below node body" +msgstr "" + +#: node_images.module:114 +msgid "Manually set in node template by variable $node->node_images" +msgstr "" + +#: node_images.module:115 +msgid "The position of images in the node view." +msgstr "" + +#: node_images.module:119 +msgid "Link to image gallery" +msgstr "" + +#: node_images.module:121 +msgid "Show" +msgstr "" + +#: node_images.module:122 +msgid "Choose whether to show or not the link to the image gallery." +msgstr "" + +#: node_images.module:126 +msgid "Number of images in node teaser" +msgstr "" + +#: node_images.module:130 +msgid "The maximum number of images to show in the node teaser. 0 will not show images. Leave blank to show all images." +msgstr "" + +#: node_images.module:134 +msgid "Number of images in node body" +msgstr "" + +#: node_images.module:138 +msgid "The maximum number of images to show in the node body. 0 will not show images. Leave blank to show all images." +msgstr "" + +#: node_images.module:142 +msgid "Image format in node teaser" +msgstr "" + +#: node_images.module:144;151 +msgid "Full size images" +msgstr "" + +#: node_images.module:145 +msgid "Image format in node teaser." +msgstr "" + +#: node_images.module:149 +msgid "Image format in node body" +msgstr "" + +#: node_images.module:152 +msgid "Image format in node body." +msgstr "" + +#: node_images.module:165 +msgid "Changes made to the node images are not permanent until you save this post." +msgstr "" + +#: node_images.module:194 +msgid "Click the !images tab to edit node images." +msgstr "" + +#: node_images.module:291 +msgid "Edit node images" +msgstr "" + +#: node_images.module:297 +msgid "Open the image gallery" +msgstr "" + +#: node_images.module:383 +msgid "Attach" +msgstr "" + +#: node_images.module:440 +msgid "File upload error." +msgstr "" + +#: node_images.module:524 +msgid "Unable to copy image \"%image\"." +msgstr "" + +#: node_images.module:657 +msgid "Node not valid." +msgstr "" + +#: node_images.module:661 +msgid "You are not allowed to upload node images." +msgstr "" + +#: node_images.module:686 +msgid "The image has been successfully uploaded." +msgstr "" + +#: node_images.module:777 +msgid "View all images" +msgstr "" + +#: node_images.module:836 +msgid "Make sure you have a working image toolkit installed and enabled, for more information see: %settings" +msgstr "" + +#: node_images.module:836 +msgid "the settings page" +msgstr "" + +#: node_images.module:905 +msgid "Its name exceeds the 255 characters limit. Please rename the file and try again." +msgstr "" + +#: node_images.module:41 +msgid "administer node images" +msgstr "" + +#: node_images.module:41 +msgid "edit own node images" +msgstr "" + +#: node_images.module:41 +msgid "edit any node image" +msgstr "" + +#: node_images.module:41 +msgid "delete own node images" +msgstr "" + +#: node_images.module:41 +msgid "delete any node image" +msgstr "" \ No newline at end of file diff --git a/html/modules/suggestedterms/LICENSE.txt b/html/modules/suggestedterms/LICENSE.txt new file mode 100644 index 0000000..2c095c8 --- /dev/null +++ b/html/modules/suggestedterms/LICENSE.txt @@ -0,0 +1,274 @@ +GNU GENERAL PUBLIC LICENSE + + Version 2, June 1991 + +Copyright (C) 1989, 1991 Free Software Foundation, Inc. 675 Mass Ave, +Cambridge, MA 02139, USA. Everyone is permitted to copy and distribute +verbatim copies of this license document, but changing it is not allowed. + + Preamble + +The licenses for most software are designed to take away your freedom to +share and change it. By contrast, the GNU General Public License is +intended to guarantee your freedom to share and change free software--to +make sure the software is free for all its users. This General Public License +applies to most of the Free Software Foundation's software and to any other +program whose authors commit to using it. (Some other Free Software +Foundation software is covered by the GNU Library General Public License +instead.) You can apply it to your programs, too. + +When we speak of free software, we are referring to freedom, not price. Our +General Public Licenses are designed to make sure that you have the +freedom to distribute copies of free software (and charge for this service if +you wish), that you receive source code or can get it if you want it, that you +can change the software or use pieces of it in new free programs; and that +you know you can do these things. + +To protect your rights, we need to make restrictions that forbid anyone to +deny you these rights or to ask you to surrender the rights. These restrictions +translate to certain responsibilities for you if you distribute copies of the +software, or if you modify it. + +For example, if you distribute copies of such a program, whether gratis or for +a fee, you must give the recipients all the rights that you have. You must make +sure that they, too, receive or can get the source code. And you must show +them these terms so they know their rights. + +We protect your rights with two steps: (1) copyright the software, and (2) +offer you this license which gives you legal permission to copy, distribute +and/or modify the software. + +Also, for each author's protection and ours, we want to make certain that +everyone understands that there is no warranty for this free software. If the +software is modified by someone else and passed on, we want its recipients +to know that what they have is not the original, so that any problems +introduced by others will not reflect on the original authors' reputations. + +Finally, any free program is threatened constantly by software patents. We +wish to avoid the danger that redistributors of a free program will individually +obtain patent licenses, in effect making the program proprietary. To prevent +this, we have made it clear that any patent must be licensed for everyone's +free use or not licensed at all. + +The precise terms and conditions for copying, distribution and modification +follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND + MODIFICATION + +0. This License applies to any program or other work which contains a notice +placed by the copyright holder saying it may be distributed under the terms +of this General Public License. The "Program", below, refers to any such +program or work, and a "work based on the Program" means either the +Program or any derivative work under copyright law: that is to say, a work +containing the Program or a portion of it, either verbatim or with +modifications and/or translated into another language. (Hereinafter, translation +is included without limitation in the term "modification".) Each licensee is +addressed as "you". + +Activities other than copying, distribution and modification are not covered +by this License; they are outside its scope. The act of running the Program is +not restricted, and the output from the Program is covered only if its contents +constitute a work based on the Program (independent of having been made +by running the Program). Whether that is true depends on what the Program +does. + +1. You may copy and distribute verbatim copies of the Program's source +code as you receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice and +disclaimer of warranty; keep intact all the notices that refer to this License +and to the absence of any warranty; and give any other recipients of the +Program a copy of this License along with the Program. + +You may charge a fee for the physical act of transferring a copy, and you +may at your option offer warranty protection in exchange for a fee. + +2. You may modify your copy or copies of the Program or any portion of it, +thus forming a work based on the Program, and copy and distribute such +modifications or work under the terms of Section 1 above, provided that you +also meet all of these conditions: + +a) You must cause the modified files to carry prominent notices stating that +you changed the files and the date of any change. + +b) You must cause any work that you distribute or publish, that in whole or in +part contains or is derived from the Program or any part thereof, to be +licensed as a whole at no charge to all third parties under the terms of this +License. + +c) If the modified program normally reads commands interactively when run, +you must cause it, when started running for such interactive use in the most +ordinary way, to print or display an announcement including an appropriate +copyright notice and a notice that there is no warranty (or else, saying that +you provide a warranty) and that users may redistribute the program under +these conditions, and telling the user how to view a copy of this License. +(Exception: if the Program itself is interactive but does not normally print such +an announcement, your work based on the Program is not required to print +an announcement.) + +These requirements apply to the modified work as a whole. If identifiable +sections of that work are not derived from the Program, and can be +reasonably considered independent and separate works in themselves, then +this License, and its terms, do not apply to those sections when you distribute +them as separate works. But when you distribute the same sections as part +of a whole which is a work based on the Program, the distribution of the +whole must be on the terms of this License, whose permissions for other +licensees extend to the entire whole, and thus to each and every part +regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest your rights to +work written entirely by you; rather, the intent is to exercise the right to +control the distribution of derivative or collective works based on the +Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of a +storage or distribution medium does not bring the other work under the scope +of this License. + +3. You may copy and distribute the Program (or a work based on it, under +Section 2) in object code or executable form under the terms of Sections 1 +and 2 above provided that you also do one of the following: + +a) Accompany it with the complete corresponding machine-readable source +code, which must be distributed under the terms of Sections 1 and 2 above +on a medium customarily used for software interchange; or, + +b) Accompany it with a written offer, valid for at least three years, to give +any third party, for a charge no more than your cost of physically performing +source distribution, a complete machine-readable copy of the corresponding +source code, to be distributed under the terms of Sections 1 and 2 above on +a medium customarily used for software interchange; or, + +c) Accompany it with the information you received as to the offer to distribute +corresponding source code. (This alternative is allowed only for +noncommercial distribution and only if you received the program in object +code or executable form with such an offer, in accord with Subsection b +above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source code +means all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation and +installation of the executable. However, as a special exception, the source +code distributed need not include anything that is normally distributed (in +either source or binary form) with the major components (compiler, kernel, +and so on) of the operating system on which the executable runs, unless that +component itself accompanies the executable. + +If distribution of executable or object code is made by offering access to +copy from a designated place, then offering equivalent access to copy the +source code from the same place counts as distribution of the source code, +even though third parties are not compelled to copy the source along with the +object code. + +4. You may not copy, modify, sublicense, or distribute the Program except as +expressly provided under this License. Any attempt otherwise to copy, +modify, sublicense or distribute the Program is void, and will automatically +terminate your rights under this License. However, parties who have received +copies, or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + +5. You are not required to accept this License, since you have not signed it. +However, nothing else grants you permission to modify or distribute the +Program or its derivative works. These actions are prohibited by law if you +do not accept this License. Therefore, by modifying or distributing the +Program (or any work based on the Program), you indicate your acceptance +of this License to do so, and all its terms and conditions for copying, +distributing or modifying the Program or works based on it. + +6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the original +licensor to copy, distribute or modify the Program subject to these terms and +conditions. You may not impose any further restrictions on the recipients' +exercise of the rights granted herein. You are not responsible for enforcing +compliance by third parties to this License. + +7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), conditions +are imposed on you (whether by court order, agreement or otherwise) that +contradict the conditions of this License, they do not excuse you from the +conditions of this License. If you cannot distribute so as to satisfy +simultaneously your obligations under this License and any other pertinent +obligations, then as a consequence you may not distribute the Program at all. +For example, if a patent license would not permit royalty-free redistribution +of the Program by all those who receive copies directly or indirectly through +you, then the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply and +the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any patents or +other property right claims or to contest validity of any such claims; this +section has the sole purpose of protecting the integrity of the free software +distribution system, which is implemented by public license practices. Many +people have made generous contributions to the wide range of software +distributed through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing to +distribute software through any other system and a licensee cannot impose +that choice. + +This section is intended to make thoroughly clear what is believed to be a +consequence of the rest of this License. + +8. If the distribution and/or use of the Program is restricted in certain +countries either by patents or by copyrighted interfaces, the original copyright +holder who places the Program under this License may add an explicit +geographical distribution limitation excluding those countries, so that +distribution is permitted only in or among countries not thus excluded. In such +case, this License incorporates the limitation as if written in the body of this +License. + +9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will be +similar in spirit to the present version, but may differ in detail to address new +problems or concerns. + +Each version is given a distinguishing version number. If the Program specifies +a version number of this License which applies to it and "any later version", +you have the option of following the terms and conditions either of that +version or of any later version published by the Free Software Foundation. If +the Program does not specify a version number of this License, you may +choose any version ever published by the Free Software Foundation. + +10. If you wish to incorporate parts of the Program into other free programs +whose distribution conditions are different, write to the author to ask for +permission. For software which is copyrighted by the Free Software +Foundation, write to the Free Software Foundation; we sometimes make +exceptions for this. Our decision will be guided by the two goals of +preserving the free status of all derivatives of our free software and of +promoting the sharing and reuse of software generally. + + NO WARRANTY + +11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, +THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT +PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE +STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT +WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND +PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL +NECESSARY SERVICING, REPAIR OR CORRECTION. + +12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR +AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR +ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE +LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, +SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES +ARISING OUT OF THE USE OR INABILITY TO USE THE +PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA +OR DATA BEING RENDERED INACCURATE OR LOSSES +SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE +PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN +IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF +THE POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS diff --git a/html/modules/suggestedterms/README.txt b/html/modules/suggestedterms/README.txt new file mode 100755 index 0000000..70bdac2 --- /dev/null +++ b/html/modules/suggestedterms/README.txt @@ -0,0 +1,32 @@ +ABOUT + +This module provides "suggested terms" for free-tagging Taxonomy fields based on terms +already submitted. It replaces the description field on free-tagging fields with a +clickable list of previously entered terms. If javascript is not enabled the list will +still appear but not be clickable. It provides the best of both worlds between a +pre-existing list of terms and the ability to add new terms on the fly as needed. + +REQUIREMENTS + +- Drupal 5.x + +CONFIGURATION + +Once the module is enabled, go to admin/content/suggestedterms to configure the module. +Terms may be ordered by popularity, alphabetically, or most recently added and the list +may be limited to any number of terms. The settings will apply to all free-tagging fields +on all nodes. + +AUTHOR AND CREDIT + +Original Development: +Gareth Arch +garch@tampabay.rr.com + +Maintainer: +Larry Garfield +garfield@palantir.net +http://www.palantir.net/ + +This module was initially developed by Palantir.net and released to the Drupal +community under the GNU General Public License v2. diff --git a/html/modules/suggestedterms/suggested_terms.png b/html/modules/suggestedterms/suggested_terms.png new file mode 100755 index 0000000..3b21f0a Binary files /dev/null and b/html/modules/suggestedterms/suggested_terms.png differ diff --git a/html/modules/suggestedterms/suggestedterms.info b/html/modules/suggestedterms/suggestedterms.info new file mode 100644 index 0000000..01fdada --- /dev/null +++ b/html/modules/suggestedterms/suggestedterms.info @@ -0,0 +1,11 @@ +; $Id: suggestedterms.info,v 1.1.4.1 2008/10/09 17:34:07 crell Exp $ +name = Suggested Terms +description = "Replaces the default 'description' text on free-tagging textfields on node edit pages with a list of taxonomy terms in that vocabulary, with appropriate labeling" +core = 6.x + +; Information added by drupal.org packaging script on 2009-10-29 +version = "6.x-1.3" +core = "6.x" +project = "suggestedterms" +datestamp = "1256834497" + diff --git a/html/modules/suggestedterms/suggestedterms.install b/html/modules/suggestedterms/suggestedterms.install new file mode 100755 index 0000000..07b1feb --- /dev/null +++ b/html/modules/suggestedterms/suggestedterms.install @@ -0,0 +1,24 @@ +' + this.innerHTML + '') + .attr('href', '#') + .addClass($(this).attr('class')) + .bind('click', function(event) { + event.preventDefault(); + var input = $(this).parents(".form-item").children('input'); + var text = $(this).text(); + if (((', ' + input.val() + ',').indexOf(', ' + text + ',') < 0) && ((', ' + input.val() + ',').indexOf(', "' + text + '",') < 0)) { + if ((input.val()).length > 0) { + input.val(input.val() + ', '); + } + input.val(input.val() + text); + } + }); // end bind + $(this).before(a).remove(); + }); // end span.suggestedterm +} // end build_links diff --git a/html/modules/suggestedterms/suggestedterms.module b/html/modules/suggestedterms/suggestedterms.module new file mode 100755 index 0000000..a9781ad --- /dev/null +++ b/html/modules/suggestedterms/suggestedterms.module @@ -0,0 +1,202 @@ +'. t("Replaces the default \'description\' text on free-tagging textfields on node edit pages with a list of taxonomy terms in that vocabulary, with appropriate labeling"). '

'; + break; + } + + return $output; +} + +/** + * Implementation of hook_perm(). + */ +function suggestedterms_perm() { + return array('administer suggested terms configuration'); +} + +/** + * Implementation of hook_theme(). + */ +function suggestedterms_theme() { + return array( + 'suggestedterm' => array( + 'arguments' => array('name' => NULL), + ), + ); +} + +/** + * Build tags for each term. + * + * @ingroup themeable + * @param $name + * The term name to theme. + */ +function theme_suggestedterm($name) { + if (FALSE !== strpos($name, ',')) { + $name = '"'. $name .'"'; + } + return "{$name}"; +} + +/** +* Creates all spans from vocabulary terms +* +* @return +* A string containing the clickable spans for the suggestedterms module. +*/ +function _suggestedterms_build_suggestions($vid, $sort_order, $display_what) { + // Use left join to display all entries, inner join to diaplay only used ones. + $query_type = ($display_what == SUGGESTEDTERMS_DISPLAY_ALL) ? " LEFT " : " INNER "; + + // Get total terms set by admin. + $total_terms = variable_get('suggestedterms_maximum_displayed', 5); + + // Get sort order set by admin. + switch ($sort_order) { + case SUGGESTEDTERMS_SORT_POPULAR: + $query = "SELECT t.name AS name, COUNT(t.name) AS total FROM {term_data} t $query_type JOIN {term_node} tn ON (t.tid = tn.tid) WHERE t.vid = %d GROUP BY t.name ORDER BY total desc"; + break; + + case SUGGESTEDTERMS_SORT_RECENT: + $query = "SELECT t.name AS name FROM {term_data} t $query_type JOIN {term_node} tn ON (t.tid = nt.tid) WHERE t.vid = %d GROUP BY t.name ORDER BY t.tid desc"; + break; + + case SUGGESTEDTERMS_SORT_WEIGHT: + $query = "SELECT t.name AS name, t.weight AS weight FROM {term_data} t $query_type JOIN {term_node} tn ON (t.tid = tn.tid) WHERE t.vid = %d GROUP BY t.name ORDER BY weight, name"; + break; + + case SUGGESTEDTERMS_SORT_USED: + $query = "SELECT DISTINCT t.name AS name FROM {term_data} t $query_type JOIN {term_node} tn ON (t.tid = tn.tid) $query_type JOIN {node} n ON (tn.nid = n.nid) WHERE t.vid = %d ORDER BY n.changed DESC, name"; + break; + + default: + $query = "SELECT t.name AS name FROM {term_data} t $query_type JOIN {term_node} tn ON (t.tid = tn.tid) WHERE t.vid = %d GROUP BY t.name ORDER BY t.name"; + break; + } + $query_result = ($total_terms > 0) ? db_query_range($query, $vid, 0, $total_terms) : db_query($query, $vid); + $terms = array(); + while($record = db_fetch_object($query_result)) { + $terms[] = theme('suggestedterm', check_plain($record->name)); + } + return implode(', ', $terms); +} + +/** + * Implementation of hook_form_alter(). + */ +function suggestedterms_form_alter(&$form, $form_state, $form_id) { + if ((isset($form['type']) && $form['type']['#value'] .'_node_form' == $form_id) && isset($form['taxonomy']['tags'])) { + $path = drupal_get_path('module', 'suggestedterms'); + drupal_add_js($path . '/suggestedterms.js'); + $display_what = variable_get('suggestedterms_display_mode', SUGGESTEDTERMS_DISPLAY_ALL); + // If multiple vocabularies, loop over each. + foreach ($form['taxonomy']['tags'] as $vid => $thisindex) { + // If free-tagging taxonomy is true, add new links for each free-tag textfield. + $sort_order = variable_get('suggestedterms_sort_order', SUGGESTEDTERMS_SORT_NAME); + switch ($sort_order) { + case SUGGESTEDTERMS_SORT_POPULAR: + $tag_description = t('Most popular terms'); + break; + + case SUGGESTEDTERMS_SORT_RECENT: + $tag_description = t('Recently added'); + break; + + case SUGGESTEDTERMS_SORT_WEIGHT: + $tag_description = t('Terms by Weight'); + break; + + case SUGGESTEDTERMS_SORT_USED: + $tag_description = t('Recently Used'); + break; + + default: + $tag_description = t('Terms by name'); + break; + } + // Build the suggested terms and set in description tag. + $form['taxonomy']['tags'][$vid]['#description'] .= "
\n". $tag_description . ': ' . _suggestedterms_build_suggestions($vid, $sort_order, $display_what); + } + } +} + +/** + * Administration page for suggested terms. + */ +function suggestedterms_admin() { + + $form['suggestedterms_maximum_displayed'] = array( + '#type' => 'textfield', + '#title' => t('Maximum number of links'), + '#default_value' => variable_get('suggestedterms_maximum_displayed', 5), + '#size' => 2, + '#maxlength' => 2, + '#description' => t('The maximum number of links to display in the block. Enter 0 to show all.'), + ); + $form['suggestedterms_sort_order'] = array( + '#type' => 'radios', + '#title' => t('Link sort order'), + '#default_value' => variable_get('suggestedterms_sort_order', SUGGESTEDTERMS_SORT_NAME), + '#description' => t("The sort order for the links displayed."), + '#options' => array( + SUGGESTEDTERMS_SORT_POPULAR => t('Most used'), + SUGGESTEDTERMS_SORT_NAME => t('Alphabetically'), + SUGGESTEDTERMS_SORT_WEIGHT => t('Weight'), + SUGGESTEDTERMS_SORT_RECENT => t('Most recently added'), + SUGGESTEDTERMS_SORT_USED => t('Most recently used'), + ), + ); + $form['suggestedterms_display_mode'] = array( + '#type' => 'radios', + '#title' => t('Which terms to display'), + '#default_value' => variable_get('suggestedterms_display_mode', SUGGESTEDTERMS_DISPLAY_ALL), + '#description' => t("Whether to display all defined terms or only the ones previously used."), + '#options' => array( + SUGGESTEDTERMS_DISPLAY_USED => t('Previously-used terms'), + SUGGESTEDTERMS_DISPLAY_ALL => t('All terms'), + ), + ); + + return system_settings_form($form); +} + +/** + * Implementation of hook_menu(). + */ +function suggestedterms_menu() { + + $items = array(); + + $items['admin/content/suggestedterms'] = array( + 'title' => t('Suggested terms'), + 'description' => t('Provide integrated suggestions of recent or popular tags from the given vocabulary.'), + 'page callback' => 'drupal_get_form', + 'page arguments' => array('suggestedterms_admin'), + 'access arguments' => array('administer suggested terms configuration'), + 'type' => MENU_NORMAL_ITEM, + ); + + return $items; +} diff --git a/html/scripts/example.html b/html/scripts/example.html new file mode 100644 index 0000000..580d41b --- /dev/null +++ b/html/scripts/example.html @@ -0,0 +1,33 @@ + + + +JQuery Cycle Plugin - Example Slideshow + + + + + + + + + + + +
+ + + + + +
+ + diff --git a/html/scripts/readme.txt b/html/scripts/readme.txt new file mode 100644 index 0000000..e487de3 --- /dev/null +++ b/html/scripts/readme.txt @@ -0,0 +1,58 @@ +jQuery Cycle Plugin +------------------- +The jQuery Cycle plugin is comprised of two basic parts: + 1. Core logic for controlling a slideshow + 2. A plugin architecture to support a wide variety of transition effects + (including custom transition effects that you can write yourself). + +Files +------ +jquery.cycle.all.min.js contains all of the slideshow logic and all of the +predefined transition effects that I have developed (over two dozen effects). +Use this script if you want to choose from a variety of transition effects. + +jquery.cycle.min.js contains all of the slideshow logic, but only a single +transition effect: 'fade'. +Use this script if you want a 'fade' slideshow, or if you want to write your +own custom transition. + +jquery.cycle.lite.min.js is a small, streamlined slideshow script that has fewer options. +Use this script if you want a 'fade' slideshow and you are willing to trade features for file size. + +The 'src' folder in this archive contains the uncompressed scripts. + + +Getting Started +--------------- +Open the 'example.html' file in an editor or browser to see how easy it is to +create a slideshow. + + +Documentation and Demos: +------------------------ +http://jquery.malsup.com/cycle/ +http://jquery.malsup.com/cycle/lite/ + + +Support +------- +Support can be found on the jQuery Forum: +http://forum.jquery.com/ + + +Donations +--------- +Donations are always welcome: +https://www.paypal.com/cgi-bin/webscr?cmd=_xclick&business=malsup%40gmail%2ecom&item_name=jQuery%20Cycle%20Plugin + + +License +------- +The jQuery Cycle plugin is dual-licensed under the MIT and GPL licenses: +- http://www.opensource.org/licenses/mit-license.php +- http://www.gnu.org/licenses/gpl.html + + +Dependencies +------------ +Requires: jQuery v1.3.2 or later. diff --git a/html/scripts/src/jquery.cycle.all.js b/html/scripts/src/jquery.cycle.all.js new file mode 100644 index 0000000..dd30762 --- /dev/null +++ b/html/scripts/src/jquery.cycle.all.js @@ -0,0 +1,1395 @@ +/*! + * jQuery Cycle Plugin (with Transition Definitions) + * Examples and documentation at: http://jquery.malsup.com/cycle/ + * Copyright (c) 2007-2010 M. Alsup + * Version: 2.99 (12-MAR-2011) + * Dual licensed under the MIT and GPL licenses. + * http://jquery.malsup.com/license.html + * Requires: jQuery v1.3.2 or later + */ +;(function($) { + +var ver = '2.99'; + +// if $.support is not defined (pre jQuery 1.3) add what I need +if ($.support == undefined) { + $.support = { + opacity: !($.browser.msie) + }; +} + +function debug(s) { + $.fn.cycle.debug && log(s); +} +function log() { + window.console && console.log && console.log('[cycle] ' + Array.prototype.join.call(arguments,' ')); +} +$.expr[':'].paused = function(el) { + return el.cyclePause; +} + + +// the options arg can be... +// a number - indicates an immediate transition should occur to the given slide index +// a string - 'pause', 'resume', 'toggle', 'next', 'prev', 'stop', 'destroy' or the name of a transition effect (ie, 'fade', 'zoom', etc) +// an object - properties to control the slideshow +// +// the arg2 arg can be... +// the name of an fx (only used in conjunction with a numeric value for 'options') +// the value true (only used in first arg == 'resume') and indicates +// that the resume should occur immediately (not wait for next timeout) + +$.fn.cycle = function(options, arg2) { + var o = { s: this.selector, c: this.context }; + + // in 1.3+ we can fix mistakes with the ready state + if (this.length === 0 && options != 'stop') { + if (!$.isReady && o.s) { + log('DOM not ready, queuing slideshow'); + $(function() { + $(o.s,o.c).cycle(options,arg2); + }); + return this; + } + // is your DOM ready? http://docs.jquery.com/Tutorials:Introducing_$(document).ready() + log('terminating; zero elements found by selector' + ($.isReady ? '' : ' (DOM not ready)')); + return this; + } + + // iterate the matched nodeset + return this.each(function() { + var opts = handleArguments(this, options, arg2); + if (opts === false) + return; + + opts.updateActivePagerLink = opts.updateActivePagerLink || $.fn.cycle.updateActivePagerLink; + + // stop existing slideshow for this container (if there is one) + if (this.cycleTimeout) + clearTimeout(this.cycleTimeout); + this.cycleTimeout = this.cyclePause = 0; + + var $cont = $(this); + var $slides = opts.slideExpr ? $(opts.slideExpr, this) : $cont.children(); + var els = $slides.get(); + if (els.length < 2) { + log('terminating; too few slides: ' + els.length); + return; + } + + var opts2 = buildOptions($cont, $slides, els, opts, o); + if (opts2 === false) + return; + + var startTime = opts2.continuous ? 10 : getTimeout(els[opts2.currSlide], els[opts2.nextSlide], opts2, !opts2.backwards); + + // if it's an auto slideshow, kick it off + if (startTime) { + startTime += (opts2.delay || 0); + if (startTime < 10) + startTime = 10; + debug('first timeout: ' + startTime); + this.cycleTimeout = setTimeout(function(){go(els,opts2,0,!opts.backwards)}, startTime); + } + }); +}; + +// process the args that were passed to the plugin fn +function handleArguments(cont, options, arg2) { + if (cont.cycleStop == undefined) + cont.cycleStop = 0; + if (options === undefined || options === null) + options = {}; + if (options.constructor == String) { + switch(options) { + case 'destroy': + case 'stop': + var opts = $(cont).data('cycle.opts'); + if (!opts) + return false; + cont.cycleStop++; // callbacks look for change + if (cont.cycleTimeout) + clearTimeout(cont.cycleTimeout); + cont.cycleTimeout = 0; + $(cont).removeData('cycle.opts'); + if (options == 'destroy') + destroy(opts); + return false; + case 'toggle': + cont.cyclePause = (cont.cyclePause === 1) ? 0 : 1; + checkInstantResume(cont.cyclePause, arg2, cont); + return false; + case 'pause': + cont.cyclePause = 1; + return false; + case 'resume': + cont.cyclePause = 0; + checkInstantResume(false, arg2, cont); + return false; + case 'prev': + case 'next': + var opts = $(cont).data('cycle.opts'); + if (!opts) { + log('options not found, "prev/next" ignored'); + return false; + } + $.fn.cycle[options](opts); + return false; + default: + options = { fx: options }; + }; + return options; + } + else if (options.constructor == Number) { + // go to the requested slide + var num = options; + options = $(cont).data('cycle.opts'); + if (!options) { + log('options not found, can not advance slide'); + return false; + } + if (num < 0 || num >= options.elements.length) { + log('invalid slide index: ' + num); + return false; + } + options.nextSlide = num; + if (cont.cycleTimeout) { + clearTimeout(cont.cycleTimeout); + cont.cycleTimeout = 0; + } + if (typeof arg2 == 'string') + options.oneTimeFx = arg2; + go(options.elements, options, 1, num >= options.currSlide); + return false; + } + return options; + + function checkInstantResume(isPaused, arg2, cont) { + if (!isPaused && arg2 === true) { // resume now! + var options = $(cont).data('cycle.opts'); + if (!options) { + log('options not found, can not resume'); + return false; + } + if (cont.cycleTimeout) { + clearTimeout(cont.cycleTimeout); + cont.cycleTimeout = 0; + } + go(options.elements, options, 1, !options.backwards); + } + } +}; + +function removeFilter(el, opts) { + if (!$.support.opacity && opts.cleartype && el.style.filter) { + try { el.style.removeAttribute('filter'); } + catch(smother) {} // handle old opera versions + } +}; + +// unbind event handlers +function destroy(opts) { + if (opts.next) + $(opts.next).unbind(opts.prevNextEvent); + if (opts.prev) + $(opts.prev).unbind(opts.prevNextEvent); + + if (opts.pager || opts.pagerAnchorBuilder) + $.each(opts.pagerAnchors || [], function() { + this.unbind().remove(); + }); + opts.pagerAnchors = null; + if (opts.destroy) // callback + opts.destroy(opts); +}; + +// one-time initialization +function buildOptions($cont, $slides, els, options, o) { + // support metadata plugin (v1.0 and v2.0) + var opts = $.extend({}, $.fn.cycle.defaults, options || {}, $.metadata ? $cont.metadata() : $.meta ? $cont.data() : {}); + if (opts.autostop) + opts.countdown = opts.autostopCount || els.length; + + var cont = $cont[0]; + $cont.data('cycle.opts', opts); + opts.$cont = $cont; + opts.stopCount = cont.cycleStop; + opts.elements = els; + opts.before = opts.before ? [opts.before] : []; + opts.after = opts.after ? [opts.after] : []; + + // push some after callbacks + if (!$.support.opacity && opts.cleartype) + opts.after.push(function() { removeFilter(this, opts); }); + if (opts.continuous) + opts.after.push(function() { go(els,opts,0,!opts.backwards); }); + + saveOriginalOpts(opts); + + // clearType corrections + if (!$.support.opacity && opts.cleartype && !opts.cleartypeNoBg) + clearTypeFix($slides); + + // container requires non-static position so that slides can be position within + if ($cont.css('position') == 'static') + $cont.css('position', 'relative'); + if (opts.width) + $cont.width(opts.width); + if (opts.height && opts.height != 'auto') + $cont.height(opts.height); + + if (opts.startingSlide) + opts.startingSlide = parseInt(opts.startingSlide); + else if (opts.backwards) + opts.startingSlide = els.length - 1; + + // if random, mix up the slide array + if (opts.random) { + opts.randomMap = []; + for (var i = 0; i < els.length; i++) + opts.randomMap.push(i); + opts.randomMap.sort(function(a,b) {return Math.random() - 0.5;}); + opts.randomIndex = 1; + opts.startingSlide = opts.randomMap[1]; + } + else if (opts.startingSlide >= els.length) + opts.startingSlide = 0; // catch bogus input + opts.currSlide = opts.startingSlide || 0; + var first = opts.startingSlide; + + // set position and zIndex on all the slides + $slides.css({position: 'absolute', top:0, left:0}).hide().each(function(i) { + var z; + if (opts.backwards) + z = first ? i <= first ? els.length + (i-first) : first-i : els.length-i; + else + z = first ? i >= first ? els.length - (i-first) : first-i : els.length-i; + console.log(z); + $(this).css('z-index', z) + }); + + // make sure first slide is visible + $(els[first]).css('opacity',1).show(); // opacity bit needed to handle restart use case + removeFilter(els[first], opts); + + // stretch slides + if (opts.fit && opts.width) + $slides.width(opts.width); + if (opts.fit && opts.height && opts.height != 'auto') + $slides.height(opts.height); + + // stretch container + var reshape = opts.containerResize && !$cont.innerHeight(); + if (reshape) { // do this only if container has no size http://tinyurl.com/da2oa9 + var maxw = 0, maxh = 0; + for(var j=0; j < els.length; j++) { + var $e = $(els[j]), e = $e[0], w = $e.outerWidth(), h = $e.outerHeight(); + if (!w) w = e.offsetWidth || e.width || $e.attr('width'); + if (!h) h = e.offsetHeight || e.height || $e.attr('height'); + maxw = w > maxw ? w : maxw; + maxh = h > maxh ? h : maxh; + } + if (maxw > 0 && maxh > 0) + $cont.css({width:maxw+'px',height:maxh+'px'}); + } + + if (opts.pause) + $cont.hover(function(){this.cyclePause++;},function(){this.cyclePause--;}); + + if (supportMultiTransitions(opts) === false) + return false; + + // apparently a lot of people use image slideshows without height/width attributes on the images. + // Cycle 2.50+ requires the sizing info for every slide; this block tries to deal with that. + var requeue = false; + options.requeueAttempts = options.requeueAttempts || 0; + $slides.each(function() { + // try to get height/width of each slide + var $el = $(this); + this.cycleH = (opts.fit && opts.height) ? opts.height : ($el.height() || this.offsetHeight || this.height || $el.attr('height') || 0); + this.cycleW = (opts.fit && opts.width) ? opts.width : ($el.width() || this.offsetWidth || this.width || $el.attr('width') || 0); + + if ( $el.is('img') ) { + // sigh.. sniffing, hacking, shrugging... this crappy hack tries to account for what browsers do when + // an image is being downloaded and the markup did not include sizing info (height/width attributes); + // there seems to be some "default" sizes used in this situation + var loadingIE = ($.browser.msie && this.cycleW == 28 && this.cycleH == 30 && !this.complete); + var loadingFF = ($.browser.mozilla && this.cycleW == 34 && this.cycleH == 19 && !this.complete); + var loadingOp = ($.browser.opera && ((this.cycleW == 42 && this.cycleH == 19) || (this.cycleW == 37 && this.cycleH == 17)) && !this.complete); + var loadingOther = (this.cycleH == 0 && this.cycleW == 0 && !this.complete); + // don't requeue for images that are still loading but have a valid size + if (loadingIE || loadingFF || loadingOp || loadingOther) { + if (o.s && opts.requeueOnImageNotLoaded && ++options.requeueAttempts < 100) { // track retry count so we don't loop forever + log(options.requeueAttempts,' - img slide not loaded, requeuing slideshow: ', this.src, this.cycleW, this.cycleH); + setTimeout(function() {$(o.s,o.c).cycle(options)}, opts.requeueTimeout); + requeue = true; + return false; // break each loop + } + else { + log('could not determine size of image: '+this.src, this.cycleW, this.cycleH); + } + } + } + return true; + }); + + if (requeue) + return false; + + opts.cssBefore = opts.cssBefore || {}; + opts.cssAfter = opts.cssAfter || {}; + opts.cssFirst = opts.cssFirst || {}; + opts.animIn = opts.animIn || {}; + opts.animOut = opts.animOut || {}; + + $slides.not(':eq('+first+')').css(opts.cssBefore); + $($slides[first]).css(opts.cssFirst); + + if (opts.timeout) { + opts.timeout = parseInt(opts.timeout); + // ensure that timeout and speed settings are sane + if (opts.speed.constructor == String) + opts.speed = $.fx.speeds[opts.speed] || parseInt(opts.speed); + if (!opts.sync) + opts.speed = opts.speed / 2; + + var buffer = opts.fx == 'none' ? 0 : opts.fx == 'shuffle' ? 500 : 250; + while((opts.timeout - opts.speed) < buffer) // sanitize timeout + opts.timeout += opts.speed; + } + if (opts.easing) + opts.easeIn = opts.easeOut = opts.easing; + if (!opts.speedIn) + opts.speedIn = opts.speed; + if (!opts.speedOut) + opts.speedOut = opts.speed; + + opts.slideCount = els.length; + opts.currSlide = opts.lastSlide = first; + if (opts.random) { + if (++opts.randomIndex == els.length) + opts.randomIndex = 0; + opts.nextSlide = opts.randomMap[opts.randomIndex]; + } + else if (opts.backwards) + opts.nextSlide = opts.startingSlide == 0 ? (els.length-1) : opts.startingSlide-1; + else + opts.nextSlide = opts.startingSlide >= (els.length-1) ? 0 : opts.startingSlide+1; + + // run transition init fn + if (!opts.multiFx) { + var init = $.fn.cycle.transitions[opts.fx]; + if ($.isFunction(init)) + init($cont, $slides, opts); + else if (opts.fx != 'custom' && !opts.multiFx) { + log('unknown transition: ' + opts.fx,'; slideshow terminating'); + return false; + } + } + + // fire artificial events + var e0 = $slides[first]; + if (opts.before.length) + opts.before[0].apply(e0, [e0, e0, opts, true]); + if (opts.after.length) + opts.after[0].apply(e0, [e0, e0, opts, true]); + + if (opts.next) + $(opts.next).bind(opts.prevNextEvent,function(){return advance(opts,1)}); + if (opts.prev) + $(opts.prev).bind(opts.prevNextEvent,function(){return advance(opts,0)}); + if (opts.pager || opts.pagerAnchorBuilder) + buildPager(els,opts); + + exposeAddSlide(opts, els); + + return opts; +}; + +// save off original opts so we can restore after clearing state +function saveOriginalOpts(opts) { + opts.original = { before: [], after: [] }; + opts.original.cssBefore = $.extend({}, opts.cssBefore); + opts.original.cssAfter = $.extend({}, opts.cssAfter); + opts.original.animIn = $.extend({}, opts.animIn); + opts.original.animOut = $.extend({}, opts.animOut); + $.each(opts.before, function() { opts.original.before.push(this); }); + $.each(opts.after, function() { opts.original.after.push(this); }); +}; + +function supportMultiTransitions(opts) { + var i, tx, txs = $.fn.cycle.transitions; + // look for multiple effects + if (opts.fx.indexOf(',') > 0) { + opts.multiFx = true; + opts.fxs = opts.fx.replace(/\s*/g,'').split(','); + // discard any bogus effect names + for (i=0; i < opts.fxs.length; i++) { + var fx = opts.fxs[i]; + tx = txs[fx]; + if (!tx || !txs.hasOwnProperty(fx) || !$.isFunction(tx)) { + log('discarding unknown transition: ',fx); + opts.fxs.splice(i,1); + i--; + } + } + // if we have an empty list then we threw everything away! + if (!opts.fxs.length) { + log('No valid transitions named; slideshow terminating.'); + return false; + } + } + else if (opts.fx == 'all') { // auto-gen the list of transitions + opts.multiFx = true; + opts.fxs = []; + for (p in txs) { + tx = txs[p]; + if (txs.hasOwnProperty(p) && $.isFunction(tx)) + opts.fxs.push(p); + } + } + if (opts.multiFx && opts.randomizeEffects) { + // munge the fxs array to make effect selection random + var r1 = Math.floor(Math.random() * 20) + 30; + for (i = 0; i < r1; i++) { + var r2 = Math.floor(Math.random() * opts.fxs.length); + opts.fxs.push(opts.fxs.splice(r2,1)[0]); + } + debug('randomized fx sequence: ',opts.fxs); + } + return true; +}; + +// provide a mechanism for adding slides after the slideshow has started +function exposeAddSlide(opts, els) { + opts.addSlide = function(newSlide, prepend) { + var $s = $(newSlide), s = $s[0]; + if (!opts.autostopCount) + opts.countdown++; + els[prepend?'unshift':'push'](s); + if (opts.els) + opts.els[prepend?'unshift':'push'](s); // shuffle needs this + opts.slideCount = els.length; + + $s.css('position','absolute'); + $s[prepend?'prependTo':'appendTo'](opts.$cont); + + if (prepend) { + opts.currSlide++; + opts.nextSlide++; + } + + if (!$.support.opacity && opts.cleartype && !opts.cleartypeNoBg) + clearTypeFix($s); + + if (opts.fit && opts.width) + $s.width(opts.width); + if (opts.fit && opts.height && opts.height != 'auto') + $s.height(opts.height); + s.cycleH = (opts.fit && opts.height) ? opts.height : $s.height(); + s.cycleW = (opts.fit && opts.width) ? opts.width : $s.width(); + + $s.css(opts.cssBefore); + + if (opts.pager || opts.pagerAnchorBuilder) + $.fn.cycle.createPagerAnchor(els.length-1, s, $(opts.pager), els, opts); + + if ($.isFunction(opts.onAddSlide)) + opts.onAddSlide($s); + else + $s.hide(); // default behavior + }; +} + +// reset internal state; we do this on every pass in order to support multiple effects +$.fn.cycle.resetState = function(opts, fx) { + fx = fx || opts.fx; + opts.before = []; opts.after = []; + opts.cssBefore = $.extend({}, opts.original.cssBefore); + opts.cssAfter = $.extend({}, opts.original.cssAfter); + opts.animIn = $.extend({}, opts.original.animIn); + opts.animOut = $.extend({}, opts.original.animOut); + opts.fxFn = null; + $.each(opts.original.before, function() { opts.before.push(this); }); + $.each(opts.original.after, function() { opts.after.push(this); }); + + // re-init + var init = $.fn.cycle.transitions[fx]; + if ($.isFunction(init)) + init(opts.$cont, $(opts.elements), opts); +}; + +// this is the main engine fn, it handles the timeouts, callbacks and slide index mgmt +function go(els, opts, manual, fwd) { + // opts.busy is true if we're in the middle of an animation + if (manual && opts.busy && opts.manualTrump) { + // let manual transitions requests trump active ones + debug('manualTrump in go(), stopping active transition'); + $(els).stop(true,true); + opts.busy = 0; + } + // don't begin another timeout-based transition if there is one active + if (opts.busy) { + debug('transition active, ignoring new tx request'); + return; + } + + var p = opts.$cont[0], curr = els[opts.currSlide], next = els[opts.nextSlide]; + + // stop cycling if we have an outstanding stop request + if (p.cycleStop != opts.stopCount || p.cycleTimeout === 0 && !manual) + return; + + // check to see if we should stop cycling based on autostop options + if (!manual && !p.cyclePause && !opts.bounce && + ((opts.autostop && (--opts.countdown <= 0)) || + (opts.nowrap && !opts.random && opts.nextSlide < opts.currSlide))) { + if (opts.end) + opts.end(opts); + return; + } + + // if slideshow is paused, only transition on a manual trigger + var changed = false; + if ((manual || !p.cyclePause) && (opts.nextSlide != opts.currSlide)) { + changed = true; + var fx = opts.fx; + // keep trying to get the slide size if we don't have it yet + curr.cycleH = curr.cycleH || $(curr).height(); + curr.cycleW = curr.cycleW || $(curr).width(); + next.cycleH = next.cycleH || $(next).height(); + next.cycleW = next.cycleW || $(next).width(); + + // support multiple transition types + if (opts.multiFx) { + if (opts.lastFx == undefined || ++opts.lastFx >= opts.fxs.length) + opts.lastFx = 0; + fx = opts.fxs[opts.lastFx]; + opts.currFx = fx; + } + + // one-time fx overrides apply to: $('div').cycle(3,'zoom'); + if (opts.oneTimeFx) { + fx = opts.oneTimeFx; + opts.oneTimeFx = null; + } + + $.fn.cycle.resetState(opts, fx); + + // run the before callbacks + if (opts.before.length) + $.each(opts.before, function(i,o) { + if (p.cycleStop != opts.stopCount) return; + o.apply(next, [curr, next, opts, fwd]); + }); + + // stage the after callacks + var after = function() { + opts.busy = 0; + $.each(opts.after, function(i,o) { + if (p.cycleStop != opts.stopCount) return; + o.apply(next, [curr, next, opts, fwd]); + }); + }; + + debug('tx firing('+fx+'); currSlide: ' + opts.currSlide + '; nextSlide: ' + opts.nextSlide); + + // get ready to perform the transition + opts.busy = 1; + if (opts.fxFn) // fx function provided? + opts.fxFn(curr, next, opts, after, fwd, manual && opts.fastOnEvent); + else if ($.isFunction($.fn.cycle[opts.fx])) // fx plugin ? + $.fn.cycle[opts.fx](curr, next, opts, after, fwd, manual && opts.fastOnEvent); + else + $.fn.cycle.custom(curr, next, opts, after, fwd, manual && opts.fastOnEvent); + } + + if (changed || opts.nextSlide == opts.currSlide) { + // calculate the next slide + opts.lastSlide = opts.currSlide; + if (opts.random) { + opts.currSlide = opts.nextSlide; + if (++opts.randomIndex == els.length) + opts.randomIndex = 0; + opts.nextSlide = opts.randomMap[opts.randomIndex]; + if (opts.nextSlide == opts.currSlide) + opts.nextSlide = (opts.currSlide == opts.slideCount - 1) ? 0 : opts.currSlide + 1; + } + else if (opts.backwards) { + var roll = (opts.nextSlide - 1) < 0; + if (roll && opts.bounce) { + opts.backwards = !opts.backwards; + opts.nextSlide = 1; + opts.currSlide = 0; + } + else { + opts.nextSlide = roll ? (els.length-1) : opts.nextSlide-1; + opts.currSlide = roll ? 0 : opts.nextSlide+1; + } + } + else { // sequence + var roll = (opts.nextSlide + 1) == els.length; + if (roll && opts.bounce) { + opts.backwards = !opts.backwards; + opts.nextSlide = els.length-2; + opts.currSlide = els.length-1; + } + else { + opts.nextSlide = roll ? 0 : opts.nextSlide+1; + opts.currSlide = roll ? els.length-1 : opts.nextSlide-1; + } + } + } + if (changed && opts.pager) + opts.updateActivePagerLink(opts.pager, opts.currSlide, opts.activePagerClass); + + // stage the next transition + var ms = 0; + if (opts.timeout && !opts.continuous) + ms = getTimeout(els[opts.currSlide], els[opts.nextSlide], opts, fwd); + else if (opts.continuous && p.cyclePause) // continuous shows work off an after callback, not this timer logic + ms = 10; + if (ms > 0) + p.cycleTimeout = setTimeout(function(){ go(els, opts, 0, !opts.backwards) }, ms); +}; + +// invoked after transition +$.fn.cycle.updateActivePagerLink = function(pager, currSlide, clsName) { + $(pager).each(function() { + $(this).children().removeClass(clsName).eq(currSlide).addClass(clsName); + }); +}; + +// calculate timeout value for current transition +function getTimeout(curr, next, opts, fwd) { + if (opts.timeoutFn) { + // call user provided calc fn + var t = opts.timeoutFn.call(curr,curr,next,opts,fwd); + while (opts.fx != 'none' && (t - opts.speed) < 250) // sanitize timeout + t += opts.speed; + debug('calculated timeout: ' + t + '; speed: ' + opts.speed); + if (t !== false) + return t; + } + return opts.timeout; +}; + +// expose next/prev function, caller must pass in state +$.fn.cycle.next = function(opts) { advance(opts,1); }; +$.fn.cycle.prev = function(opts) { advance(opts,0);}; + +// advance slide forward or back +function advance(opts, moveForward) { + var val = moveForward ? 1 : -1; + var els = opts.elements; + var p = opts.$cont[0], timeout = p.cycleTimeout; + if (timeout) { + clearTimeout(timeout); + p.cycleTimeout = 0; + } + if (opts.random && val < 0) { + // move back to the previously display slide + opts.randomIndex--; + if (--opts.randomIndex == -2) + opts.randomIndex = els.length-2; + else if (opts.randomIndex == -1) + opts.randomIndex = els.length-1; + opts.nextSlide = opts.randomMap[opts.randomIndex]; + } + else if (opts.random) { + opts.nextSlide = opts.randomMap[opts.randomIndex]; + } + else { + opts.nextSlide = opts.currSlide + val; + if (opts.nextSlide < 0) { + if (opts.nowrap) return false; + opts.nextSlide = els.length - 1; + } + else if (opts.nextSlide >= els.length) { + if (opts.nowrap) return false; + opts.nextSlide = 0; + } + } + + var cb = opts.onPrevNextEvent || opts.prevNextClick; // prevNextClick is deprecated + if ($.isFunction(cb)) + cb(val > 0, opts.nextSlide, els[opts.nextSlide]); + go(els, opts, 1, moveForward); + return false; +}; + +function buildPager(els, opts) { + var $p = $(opts.pager); + $.each(els, function(i,o) { + $.fn.cycle.createPagerAnchor(i,o,$p,els,opts); + }); + opts.updateActivePagerLink(opts.pager, opts.startingSlide, opts.activePagerClass); +}; + +$.fn.cycle.createPagerAnchor = function(i, el, $p, els, opts) { + var a; + if ($.isFunction(opts.pagerAnchorBuilder)) { + a = opts.pagerAnchorBuilder(i,el); + debug('pagerAnchorBuilder('+i+', el) returned: ' + a); + } + else + a = ''+(i+1)+''; + + if (!a) + return; + var $a = $(a); + // don't reparent if anchor is in the dom + if ($a.parents('body').length === 0) { + var arr = []; + if ($p.length > 1) { + $p.each(function() { + var $clone = $a.clone(true); + $(this).append($clone); + arr.push($clone[0]); + }); + $a = $(arr); + } + else { + $a.appendTo($p); + } + } + + opts.pagerAnchors = opts.pagerAnchors || []; + opts.pagerAnchors.push($a); + $a.bind(opts.pagerEvent, function(e) { + e.preventDefault(); + opts.nextSlide = i; + var p = opts.$cont[0], timeout = p.cycleTimeout; + if (timeout) { + clearTimeout(timeout); + p.cycleTimeout = 0; + } + var cb = opts.onPagerEvent || opts.pagerClick; // pagerClick is deprecated + if ($.isFunction(cb)) + cb(opts.nextSlide, els[opts.nextSlide]); + go(els,opts,1,opts.currSlide < i); // trigger the trans +// return false; // <== allow bubble + }); + + if ( ! /^click/.test(opts.pagerEvent) && !opts.allowPagerClickBubble) + $a.bind('click.cycle', function(){return false;}); // suppress click + + if (opts.pauseOnPagerHover) + $a.hover(function() { opts.$cont[0].cyclePause++; }, function() { opts.$cont[0].cyclePause--; } ); +}; + +// helper fn to calculate the number of slides between the current and the next +$.fn.cycle.hopsFromLast = function(opts, fwd) { + var hops, l = opts.lastSlide, c = opts.currSlide; + if (fwd) + hops = c > l ? c - l : opts.slideCount - l; + else + hops = c < l ? l - c : l + opts.slideCount - c; + return hops; +}; + +// fix clearType problems in ie6 by setting an explicit bg color +// (otherwise text slides look horrible during a fade transition) +function clearTypeFix($slides) { + debug('applying clearType background-color hack'); + function hex(s) { + s = parseInt(s).toString(16); + return s.length < 2 ? '0'+s : s; + }; + function getBg(e) { + for ( ; e && e.nodeName.toLowerCase() != 'html'; e = e.parentNode) { + var v = $.css(e,'background-color'); + if (v && v.indexOf('rgb') >= 0 ) { + var rgb = v.match(/\d+/g); + return '#'+ hex(rgb[0]) + hex(rgb[1]) + hex(rgb[2]); + } + if (v && v != 'transparent') + return v; + } + return '#ffffff'; + }; + $slides.each(function() { $(this).css('background-color', getBg(this)); }); +}; + +// reset common props before the next transition +$.fn.cycle.commonReset = function(curr,next,opts,w,h,rev) { + $(opts.elements).not(curr).hide(); + if (typeof opts.cssBefore.opacity == 'undefined') + opts.cssBefore.opacity = 1; + opts.cssBefore.display = 'block'; + if (opts.slideResize && w !== false && next.cycleW > 0) + opts.cssBefore.width = next.cycleW; + if (opts.slideResize && h !== false && next.cycleH > 0) + opts.cssBefore.height = next.cycleH; + opts.cssAfter = opts.cssAfter || {}; + opts.cssAfter.display = 'none'; + $(curr).css('zIndex',opts.slideCount + (rev === true ? 1 : 0)); + $(next).css('zIndex',opts.slideCount + (rev === true ? 0 : 1)); +}; + +// the actual fn for effecting a transition +$.fn.cycle.custom = function(curr, next, opts, cb, fwd, speedOverride) { + var $l = $(curr), $n = $(next); + var speedIn = opts.speedIn, speedOut = opts.speedOut, easeIn = opts.easeIn, easeOut = opts.easeOut; + $n.css(opts.cssBefore); + if (speedOverride) { + if (typeof speedOverride == 'number') + speedIn = speedOut = speedOverride; + else + speedIn = speedOut = 1; + easeIn = easeOut = null; + } + var fn = function() { + $n.animate(opts.animIn, speedIn, easeIn, function() { + cb(); + }); + }; + $l.animate(opts.animOut, speedOut, easeOut, function() { + $l.css(opts.cssAfter); + if (!opts.sync) + fn(); + }); + if (opts.sync) fn(); +}; + +// transition definitions - only fade is defined here, transition pack defines the rest +$.fn.cycle.transitions = { + fade: function($cont, $slides, opts) { + $slides.not(':eq('+opts.currSlide+')').css('opacity',0); + opts.before.push(function(curr,next,opts) { + $.fn.cycle.commonReset(curr,next,opts); + opts.cssBefore.opacity = 0; + }); + opts.animIn = { opacity: 1 }; + opts.animOut = { opacity: 0 }; + opts.cssBefore = { top: 0, left: 0 }; + } +}; + +$.fn.cycle.ver = function() { return ver; }; + +// override these globally if you like (they are all optional) +$.fn.cycle.defaults = { + activePagerClass: 'activeSlide', // class name used for the active pager link + after: null, // transition callback (scope set to element that was shown): function(currSlideElement, nextSlideElement, options, forwardFlag) + allowPagerClickBubble: false, // allows or prevents click event on pager anchors from bubbling + animIn: null, // properties that define how the slide animates in + animOut: null, // properties that define how the slide animates out + autostop: 0, // true to end slideshow after X transitions (where X == slide count) + autostopCount: 0, // number of transitions (optionally used with autostop to define X) + backwards: false, // true to start slideshow at last slide and move backwards through the stack + before: null, // transition callback (scope set to element to be shown): function(currSlideElement, nextSlideElement, options, forwardFlag) + cleartype: !$.support.opacity, // true if clearType corrections should be applied (for IE) + cleartypeNoBg: false, // set to true to disable extra cleartype fixing (leave false to force background color setting on slides) + containerResize: 1, // resize container to fit largest slide + continuous: 0, // true to start next transition immediately after current one completes + cssAfter: null, // properties that defined the state of the slide after transitioning out + cssBefore: null, // properties that define the initial state of the slide before transitioning in + delay: 0, // additional delay (in ms) for first transition (hint: can be negative) + easeIn: null, // easing for "in" transition + easeOut: null, // easing for "out" transition + easing: null, // easing method for both in and out transitions + end: null, // callback invoked when the slideshow terminates (use with autostop or nowrap options): function(options) + fastOnEvent: 0, // force fast transitions when triggered manually (via pager or prev/next); value == time in ms + fit: 0, // force slides to fit container + fx: 'fade', // name of transition effect (or comma separated names, ex: 'fade,scrollUp,shuffle') + fxFn: null, // function used to control the transition: function(currSlideElement, nextSlideElement, options, afterCalback, forwardFlag) + height: 'auto', // container height + manualTrump: true, // causes manual transition to stop an active transition instead of being ignored + next: null, // selector for element to use as event trigger for next slide + nowrap: 0, // true to prevent slideshow from wrapping + onPagerEvent: null, // callback fn for pager events: function(zeroBasedSlideIndex, slideElement) + onPrevNextEvent: null, // callback fn for prev/next events: function(isNext, zeroBasedSlideIndex, slideElement) + pager: null, // selector for element to use as pager container + pagerAnchorBuilder: null, // callback fn for building anchor links: function(index, DOMelement) + pagerEvent: 'click.cycle', // name of event which drives the pager navigation + pause: 0, // true to enable "pause on hover" + pauseOnPagerHover: 0, // true to pause when hovering over pager link + prev: null, // selector for element to use as event trigger for previous slide + prevNextEvent:'click.cycle',// event which drives the manual transition to the previous or next slide + random: 0, // true for random, false for sequence (not applicable to shuffle fx) + randomizeEffects: 1, // valid when multiple effects are used; true to make the effect sequence random + requeueOnImageNotLoaded: true, // requeue the slideshow if any image slides are not yet loaded + requeueTimeout: 250, // ms delay for requeue + rev: 0, // causes animations to transition in reverse (for effects that support it such as scrollHorz/scrollVert/shuffle) + shuffle: null, // coords for shuffle animation, ex: { top:15, left: 200 } + slideExpr: null, // expression for selecting slides (if something other than all children is required) + slideResize: 1, // force slide width/height to fixed size before every transition + speed: 1000, // speed of the transition (any valid fx speed value) + speedIn: null, // speed of the 'in' transition + speedOut: null, // speed of the 'out' transition + startingSlide: 0, // zero-based index of the first slide to be displayed + sync: 1, // true if in/out transitions should occur simultaneously + timeout: 4000, // milliseconds between slide transitions (0 to disable auto advance) + timeoutFn: null, // callback for determining per-slide timeout value: function(currSlideElement, nextSlideElement, options, forwardFlag) + updateActivePagerLink: null // callback fn invoked to update the active pager link (adds/removes activePagerClass style) +}; + +})(jQuery); + + +/*! + * jQuery Cycle Plugin Transition Definitions + * This script is a plugin for the jQuery Cycle Plugin + * Examples and documentation at: http://malsup.com/jquery/cycle/ + * Copyright (c) 2007-2010 M. Alsup + * Version: 2.73 + * Dual licensed under the MIT and GPL licenses: + * http://www.opensource.org/licenses/mit-license.php + * http://www.gnu.org/licenses/gpl.html + */ +(function($) { + +// +// These functions define slide initialization and properties for the named +// transitions. To save file size feel free to remove any of these that you +// don't need. +// +$.fn.cycle.transitions.none = function($cont, $slides, opts) { + opts.fxFn = function(curr,next,opts,after){ + $(next).show(); + $(curr).hide(); + after(); + }; +}; + +// not a cross-fade, fadeout only fades out the top slide +$.fn.cycle.transitions.fadeout = function($cont, $slides, opts) { + $slides.not(':eq('+opts.currSlide+')').css({ display: 'block', 'opacity': 1 }); + opts.before.push(function(curr,next,opts,w,h,rev) { + $(curr).css('zIndex',opts.slideCount + (!rev === true ? 1 : 0)); + $(next).css('zIndex',opts.slideCount + (!rev === true ? 0 : 1)); + }); + opts.animIn.opacity = 1; + opts.animOut.opacity = 0; + opts.cssBefore.opacity = 1; + opts.cssBefore.display = 'block'; + opts.cssAfter.zIndex = 0; +}; + +// scrollUp/Down/Left/Right +$.fn.cycle.transitions.scrollUp = function($cont, $slides, opts) { + $cont.css('overflow','hidden'); + opts.before.push($.fn.cycle.commonReset); + var h = $cont.height(); + opts.cssBefore.top = h; + opts.cssBefore.left = 0; + opts.cssFirst.top = 0; + opts.animIn.top = 0; + opts.animOut.top = -h; +}; +$.fn.cycle.transitions.scrollDown = function($cont, $slides, opts) { + $cont.css('overflow','hidden'); + opts.before.push($.fn.cycle.commonReset); + var h = $cont.height(); + opts.cssFirst.top = 0; + opts.cssBefore.top = -h; + opts.cssBefore.left = 0; + opts.animIn.top = 0; + opts.animOut.top = h; +}; +$.fn.cycle.transitions.scrollLeft = function($cont, $slides, opts) { + $cont.css('overflow','hidden'); + opts.before.push($.fn.cycle.commonReset); + var w = $cont.width(); + opts.cssFirst.left = 0; + opts.cssBefore.left = w; + opts.cssBefore.top = 0; + opts.animIn.left = 0; + opts.animOut.left = 0-w; +}; +$.fn.cycle.transitions.scrollRight = function($cont, $slides, opts) { + $cont.css('overflow','hidden'); + opts.before.push($.fn.cycle.commonReset); + var w = $cont.width(); + opts.cssFirst.left = 0; + opts.cssBefore.left = -w; + opts.cssBefore.top = 0; + opts.animIn.left = 0; + opts.animOut.left = w; +}; +$.fn.cycle.transitions.scrollHorz = function($cont, $slides, opts) { + $cont.css('overflow','hidden').width(); + opts.before.push(function(curr, next, opts, fwd) { + if (opts.rev) + fwd = !fwd; + $.fn.cycle.commonReset(curr,next,opts); + opts.cssBefore.left = fwd ? (next.cycleW-1) : (1-next.cycleW); + opts.animOut.left = fwd ? -curr.cycleW : curr.cycleW; + }); + opts.cssFirst.left = 0; + opts.cssBefore.top = 0; + opts.animIn.left = 0; + opts.animOut.top = 0; +}; +$.fn.cycle.transitions.scrollVert = function($cont, $slides, opts) { + $cont.css('overflow','hidden'); + opts.before.push(function(curr, next, opts, fwd) { + if (opts.rev) + fwd = !fwd; + $.fn.cycle.commonReset(curr,next,opts); + opts.cssBefore.top = fwd ? (1-next.cycleH) : (next.cycleH-1); + opts.animOut.top = fwd ? curr.cycleH : -curr.cycleH; + }); + opts.cssFirst.top = 0; + opts.cssBefore.left = 0; + opts.animIn.top = 0; + opts.animOut.left = 0; +}; + +// slideX/slideY +$.fn.cycle.transitions.slideX = function($cont, $slides, opts) { + opts.before.push(function(curr, next, opts) { + $(opts.elements).not(curr).hide(); + $.fn.cycle.commonReset(curr,next,opts,false,true); + opts.animIn.width = next.cycleW; + }); + opts.cssBefore.left = 0; + opts.cssBefore.top = 0; + opts.cssBefore.width = 0; + opts.animIn.width = 'show'; + opts.animOut.width = 0; +}; +$.fn.cycle.transitions.slideY = function($cont, $slides, opts) { + opts.before.push(function(curr, next, opts) { + $(opts.elements).not(curr).hide(); + $.fn.cycle.commonReset(curr,next,opts,true,false); + opts.animIn.height = next.cycleH; + }); + opts.cssBefore.left = 0; + opts.cssBefore.top = 0; + opts.cssBefore.height = 0; + opts.animIn.height = 'show'; + opts.animOut.height = 0; +}; + +// shuffle +$.fn.cycle.transitions.shuffle = function($cont, $slides, opts) { + var i, w = $cont.css('overflow', 'visible').width(); + $slides.css({left: 0, top: 0}); + opts.before.push(function(curr,next,opts) { + $.fn.cycle.commonReset(curr,next,opts,true,true,true); + }); + // only adjust speed once! + if (!opts.speedAdjusted) { + opts.speed = opts.speed / 2; // shuffle has 2 transitions + opts.speedAdjusted = true; + } + opts.random = 0; + opts.shuffle = opts.shuffle || {left:-w, top:15}; + opts.els = []; + for (i=0; i < $slides.length; i++) + opts.els.push($slides[i]); + + for (i=0; i < opts.currSlide; i++) + opts.els.push(opts.els.shift()); + + // custom transition fn (hat tip to Benjamin Sterling for this bit of sweetness!) + opts.fxFn = function(curr, next, opts, cb, fwd) { + if (opts.rev) + fwd = !fwd; + var $el = fwd ? $(curr) : $(next); + $(next).css(opts.cssBefore); + var count = opts.slideCount; + $el.animate(opts.shuffle, opts.speedIn, opts.easeIn, function() { + var hops = $.fn.cycle.hopsFromLast(opts, fwd); + for (var k=0; k < hops; k++) + fwd ? opts.els.push(opts.els.shift()) : opts.els.unshift(opts.els.pop()); + if (fwd) { + for (var i=0, len=opts.els.length; i < len; i++) + $(opts.els[i]).css('z-index', len-i+count); + } + else { + var z = $(curr).css('z-index'); + $el.css('z-index', parseInt(z)+1+count); + } + $el.animate({left:0, top:0}, opts.speedOut, opts.easeOut, function() { + $(fwd ? this : curr).hide(); + if (cb) cb(); + }); + }); + }; + $.extend(opts.cssBefore, { display: 'block', opacity: 1, top: 0, left: 0 }); +}; + +// turnUp/Down/Left/Right +$.fn.cycle.transitions.turnUp = function($cont, $slides, opts) { + opts.before.push(function(curr, next, opts) { + $.fn.cycle.commonReset(curr,next,opts,true,false); + opts.cssBefore.top = next.cycleH; + opts.animIn.height = next.cycleH; + opts.animOut.width = next.cycleW; + }); + opts.cssFirst.top = 0; + opts.cssBefore.left = 0; + opts.cssBefore.height = 0; + opts.animIn.top = 0; + opts.animOut.height = 0; +}; +$.fn.cycle.transitions.turnDown = function($cont, $slides, opts) { + opts.before.push(function(curr, next, opts) { + $.fn.cycle.commonReset(curr,next,opts,true,false); + opts.animIn.height = next.cycleH; + opts.animOut.top = curr.cycleH; + }); + opts.cssFirst.top = 0; + opts.cssBefore.left = 0; + opts.cssBefore.top = 0; + opts.cssBefore.height = 0; + opts.animOut.height = 0; +}; +$.fn.cycle.transitions.turnLeft = function($cont, $slides, opts) { + opts.before.push(function(curr, next, opts) { + $.fn.cycle.commonReset(curr,next,opts,false,true); + opts.cssBefore.left = next.cycleW; + opts.animIn.width = next.cycleW; + }); + opts.cssBefore.top = 0; + opts.cssBefore.width = 0; + opts.animIn.left = 0; + opts.animOut.width = 0; +}; +$.fn.cycle.transitions.turnRight = function($cont, $slides, opts) { + opts.before.push(function(curr, next, opts) { + $.fn.cycle.commonReset(curr,next,opts,false,true); + opts.animIn.width = next.cycleW; + opts.animOut.left = curr.cycleW; + }); + $.extend(opts.cssBefore, { top: 0, left: 0, width: 0 }); + opts.animIn.left = 0; + opts.animOut.width = 0; +}; + +// zoom +$.fn.cycle.transitions.zoom = function($cont, $slides, opts) { + opts.before.push(function(curr, next, opts) { + $.fn.cycle.commonReset(curr,next,opts,false,false,true); + opts.cssBefore.top = next.cycleH/2; + opts.cssBefore.left = next.cycleW/2; + $.extend(opts.animIn, { top: 0, left: 0, width: next.cycleW, height: next.cycleH }); + $.extend(opts.animOut, { width: 0, height: 0, top: curr.cycleH/2, left: curr.cycleW/2 }); + }); + opts.cssFirst.top = 0; + opts.cssFirst.left = 0; + opts.cssBefore.width = 0; + opts.cssBefore.height = 0; +}; + +// fadeZoom +$.fn.cycle.transitions.fadeZoom = function($cont, $slides, opts) { + opts.before.push(function(curr, next, opts) { + $.fn.cycle.commonReset(curr,next,opts,false,false); + opts.cssBefore.left = next.cycleW/2; + opts.cssBefore.top = next.cycleH/2; + $.extend(opts.animIn, { top: 0, left: 0, width: next.cycleW, height: next.cycleH }); + }); + opts.cssBefore.width = 0; + opts.cssBefore.height = 0; + opts.animOut.opacity = 0; +}; + +// blindX +$.fn.cycle.transitions.blindX = function($cont, $slides, opts) { + var w = $cont.css('overflow','hidden').width(); + opts.before.push(function(curr, next, opts) { + $.fn.cycle.commonReset(curr,next,opts); + opts.animIn.width = next.cycleW; + opts.animOut.left = curr.cycleW; + }); + opts.cssBefore.left = w; + opts.cssBefore.top = 0; + opts.animIn.left = 0; + opts.animOut.left = w; +}; +// blindY +$.fn.cycle.transitions.blindY = function($cont, $slides, opts) { + var h = $cont.css('overflow','hidden').height(); + opts.before.push(function(curr, next, opts) { + $.fn.cycle.commonReset(curr,next,opts); + opts.animIn.height = next.cycleH; + opts.animOut.top = curr.cycleH; + }); + opts.cssBefore.top = h; + opts.cssBefore.left = 0; + opts.animIn.top = 0; + opts.animOut.top = h; +}; +// blindZ +$.fn.cycle.transitions.blindZ = function($cont, $slides, opts) { + var h = $cont.css('overflow','hidden').height(); + var w = $cont.width(); + opts.before.push(function(curr, next, opts) { + $.fn.cycle.commonReset(curr,next,opts); + opts.animIn.height = next.cycleH; + opts.animOut.top = curr.cycleH; + }); + opts.cssBefore.top = h; + opts.cssBefore.left = w; + opts.animIn.top = 0; + opts.animIn.left = 0; + opts.animOut.top = h; + opts.animOut.left = w; +}; + +// growX - grow horizontally from centered 0 width +$.fn.cycle.transitions.growX = function($cont, $slides, opts) { + opts.before.push(function(curr, next, opts) { + $.fn.cycle.commonReset(curr,next,opts,false,true); + opts.cssBefore.left = this.cycleW/2; + opts.animIn.left = 0; + opts.animIn.width = this.cycleW; + opts.animOut.left = 0; + }); + opts.cssBefore.top = 0; + opts.cssBefore.width = 0; +}; +// growY - grow vertically from centered 0 height +$.fn.cycle.transitions.growY = function($cont, $slides, opts) { + opts.before.push(function(curr, next, opts) { + $.fn.cycle.commonReset(curr,next,opts,true,false); + opts.cssBefore.top = this.cycleH/2; + opts.animIn.top = 0; + opts.animIn.height = this.cycleH; + opts.animOut.top = 0; + }); + opts.cssBefore.height = 0; + opts.cssBefore.left = 0; +}; + +// curtainX - squeeze in both edges horizontally +$.fn.cycle.transitions.curtainX = function($cont, $slides, opts) { + opts.before.push(function(curr, next, opts) { + $.fn.cycle.commonReset(curr,next,opts,false,true,true); + opts.cssBefore.left = next.cycleW/2; + opts.animIn.left = 0; + opts.animIn.width = this.cycleW; + opts.animOut.left = curr.cycleW/2; + opts.animOut.width = 0; + }); + opts.cssBefore.top = 0; + opts.cssBefore.width = 0; +}; +// curtainY - squeeze in both edges vertically +$.fn.cycle.transitions.curtainY = function($cont, $slides, opts) { + opts.before.push(function(curr, next, opts) { + $.fn.cycle.commonReset(curr,next,opts,true,false,true); + opts.cssBefore.top = next.cycleH/2; + opts.animIn.top = 0; + opts.animIn.height = next.cycleH; + opts.animOut.top = curr.cycleH/2; + opts.animOut.height = 0; + }); + opts.cssBefore.height = 0; + opts.cssBefore.left = 0; +}; + +// cover - curr slide covered by next slide +$.fn.cycle.transitions.cover = function($cont, $slides, opts) { + var d = opts.direction || 'left'; + var w = $cont.css('overflow','hidden').width(); + var h = $cont.height(); + opts.before.push(function(curr, next, opts) { + $.fn.cycle.commonReset(curr,next,opts); + if (d == 'right') + opts.cssBefore.left = -w; + else if (d == 'up') + opts.cssBefore.top = h; + else if (d == 'down') + opts.cssBefore.top = -h; + else + opts.cssBefore.left = w; + }); + opts.animIn.left = 0; + opts.animIn.top = 0; + opts.cssBefore.top = 0; + opts.cssBefore.left = 0; +}; + +// uncover - curr slide moves off next slide +$.fn.cycle.transitions.uncover = function($cont, $slides, opts) { + var d = opts.direction || 'left'; + var w = $cont.css('overflow','hidden').width(); + var h = $cont.height(); + opts.before.push(function(curr, next, opts) { + $.fn.cycle.commonReset(curr,next,opts,true,true,true); + if (d == 'right') + opts.animOut.left = w; + else if (d == 'up') + opts.animOut.top = -h; + else if (d == 'down') + opts.animOut.top = h; + else + opts.animOut.left = -w; + }); + opts.animIn.left = 0; + opts.animIn.top = 0; + opts.cssBefore.top = 0; + opts.cssBefore.left = 0; +}; + +// toss - move top slide and fade away +$.fn.cycle.transitions.toss = function($cont, $slides, opts) { + var w = $cont.css('overflow','visible').width(); + var h = $cont.height(); + opts.before.push(function(curr, next, opts) { + $.fn.cycle.commonReset(curr,next,opts,true,true,true); + // provide default toss settings if animOut not provided + if (!opts.animOut.left && !opts.animOut.top) + $.extend(opts.animOut, { left: w*2, top: -h/2, opacity: 0 }); + else + opts.animOut.opacity = 0; + }); + opts.cssBefore.left = 0; + opts.cssBefore.top = 0; + opts.animIn.left = 0; +}; + +// wipe - clip animation +$.fn.cycle.transitions.wipe = function($cont, $slides, opts) { + var w = $cont.css('overflow','hidden').width(); + var h = $cont.height(); + opts.cssBefore = opts.cssBefore || {}; + var clip; + if (opts.clip) { + if (/l2r/.test(opts.clip)) + clip = 'rect(0px 0px '+h+'px 0px)'; + else if (/r2l/.test(opts.clip)) + clip = 'rect(0px '+w+'px '+h+'px '+w+'px)'; + else if (/t2b/.test(opts.clip)) + clip = 'rect(0px '+w+'px 0px 0px)'; + else if (/b2t/.test(opts.clip)) + clip = 'rect('+h+'px '+w+'px '+h+'px 0px)'; + else if (/zoom/.test(opts.clip)) { + var top = parseInt(h/2); + var left = parseInt(w/2); + clip = 'rect('+top+'px '+left+'px '+top+'px '+left+'px)'; + } + } + + opts.cssBefore.clip = opts.cssBefore.clip || clip || 'rect(0px 0px 0px 0px)'; + + var d = opts.cssBefore.clip.match(/(\d+)/g); + var t = parseInt(d[0]), r = parseInt(d[1]), b = parseInt(d[2]), l = parseInt(d[3]); + + opts.before.push(function(curr, next, opts) { + if (curr == next) return; + var $curr = $(curr), $next = $(next); + $.fn.cycle.commonReset(curr,next,opts,true,true,false); + opts.cssAfter.display = 'block'; + + var step = 1, count = parseInt((opts.speedIn / 13)) - 1; + (function f() { + var tt = t ? t - parseInt(step * (t/count)) : 0; + var ll = l ? l - parseInt(step * (l/count)) : 0; + var bb = b < h ? b + parseInt(step * ((h-b)/count || 1)) : h; + var rr = r < w ? r + parseInt(step * ((w-r)/count || 1)) : w; + $next.css({ clip: 'rect('+tt+'px '+rr+'px '+bb+'px '+ll+'px)' }); + (step++ <= count) ? setTimeout(f, 13) : $curr.css('display', 'none'); + })(); + }); + $.extend(opts.cssBefore, { display: 'block', opacity: 1, top: 0, left: 0 }); + opts.animIn = { left: 0 }; + opts.animOut = { left: 0 }; +}; + +})(jQuery); diff --git a/html/scripts/src/jquery.cycle.js b/html/scripts/src/jquery.cycle.js new file mode 100644 index 0000000..a324703 --- /dev/null +++ b/html/scripts/src/jquery.cycle.js @@ -0,0 +1,927 @@ +/*! + * jQuery Cycle Plugin (core engine only) + * Examples and documentation at: http://jquery.malsup.com/cycle/ + * Copyright (c) 2007-2010 M. Alsup + * Version: 2.99 (12-MAR-2011) + * Dual licensed under the MIT and GPL licenses. + * http://jquery.malsup.com/license.html + * Requires: jQuery v1.3.2 or later + */ +;(function($) { + +var ver = '2.99'; + +// if $.support is not defined (pre jQuery 1.3) add what I need +if ($.support == undefined) { + $.support = { + opacity: !($.browser.msie) + }; +} + +function debug(s) { + $.fn.cycle.debug && log(s); +} +function log() { + window.console && console.log && console.log('[cycle] ' + Array.prototype.join.call(arguments,' ')); +} +$.expr[':'].paused = function(el) { + return el.cyclePause; +} + + +// the options arg can be... +// a number - indicates an immediate transition should occur to the given slide index +// a string - 'pause', 'resume', 'toggle', 'next', 'prev', 'stop', 'destroy' or the name of a transition effect (ie, 'fade', 'zoom', etc) +// an object - properties to control the slideshow +// +// the arg2 arg can be... +// the name of an fx (only used in conjunction with a numeric value for 'options') +// the value true (only used in first arg == 'resume') and indicates +// that the resume should occur immediately (not wait for next timeout) + +$.fn.cycle = function(options, arg2) { + var o = { s: this.selector, c: this.context }; + + // in 1.3+ we can fix mistakes with the ready state + if (this.length === 0 && options != 'stop') { + if (!$.isReady && o.s) { + log('DOM not ready, queuing slideshow'); + $(function() { + $(o.s,o.c).cycle(options,arg2); + }); + return this; + } + // is your DOM ready? http://docs.jquery.com/Tutorials:Introducing_$(document).ready() + log('terminating; zero elements found by selector' + ($.isReady ? '' : ' (DOM not ready)')); + return this; + } + + // iterate the matched nodeset + return this.each(function() { + var opts = handleArguments(this, options, arg2); + if (opts === false) + return; + + opts.updateActivePagerLink = opts.updateActivePagerLink || $.fn.cycle.updateActivePagerLink; + + // stop existing slideshow for this container (if there is one) + if (this.cycleTimeout) + clearTimeout(this.cycleTimeout); + this.cycleTimeout = this.cyclePause = 0; + + var $cont = $(this); + var $slides = opts.slideExpr ? $(opts.slideExpr, this) : $cont.children(); + var els = $slides.get(); + if (els.length < 2) { + log('terminating; too few slides: ' + els.length); + return; + } + + var opts2 = buildOptions($cont, $slides, els, opts, o); + if (opts2 === false) + return; + + var startTime = opts2.continuous ? 10 : getTimeout(els[opts2.currSlide], els[opts2.nextSlide], opts2, !opts2.backwards); + + // if it's an auto slideshow, kick it off + if (startTime) { + startTime += (opts2.delay || 0); + if (startTime < 10) + startTime = 10; + debug('first timeout: ' + startTime); + this.cycleTimeout = setTimeout(function(){go(els,opts2,0,!opts.backwards)}, startTime); + } + }); +}; + +// process the args that were passed to the plugin fn +function handleArguments(cont, options, arg2) { + if (cont.cycleStop == undefined) + cont.cycleStop = 0; + if (options === undefined || options === null) + options = {}; + if (options.constructor == String) { + switch(options) { + case 'destroy': + case 'stop': + var opts = $(cont).data('cycle.opts'); + if (!opts) + return false; + cont.cycleStop++; // callbacks look for change + if (cont.cycleTimeout) + clearTimeout(cont.cycleTimeout); + cont.cycleTimeout = 0; + $(cont).removeData('cycle.opts'); + if (options == 'destroy') + destroy(opts); + return false; + case 'toggle': + cont.cyclePause = (cont.cyclePause === 1) ? 0 : 1; + checkInstantResume(cont.cyclePause, arg2, cont); + return false; + case 'pause': + cont.cyclePause = 1; + return false; + case 'resume': + cont.cyclePause = 0; + checkInstantResume(false, arg2, cont); + return false; + case 'prev': + case 'next': + var opts = $(cont).data('cycle.opts'); + if (!opts) { + log('options not found, "prev/next" ignored'); + return false; + } + $.fn.cycle[options](opts); + return false; + default: + options = { fx: options }; + }; + return options; + } + else if (options.constructor == Number) { + // go to the requested slide + var num = options; + options = $(cont).data('cycle.opts'); + if (!options) { + log('options not found, can not advance slide'); + return false; + } + if (num < 0 || num >= options.elements.length) { + log('invalid slide index: ' + num); + return false; + } + options.nextSlide = num; + if (cont.cycleTimeout) { + clearTimeout(cont.cycleTimeout); + cont.cycleTimeout = 0; + } + if (typeof arg2 == 'string') + options.oneTimeFx = arg2; + go(options.elements, options, 1, num >= options.currSlide); + return false; + } + return options; + + function checkInstantResume(isPaused, arg2, cont) { + if (!isPaused && arg2 === true) { // resume now! + var options = $(cont).data('cycle.opts'); + if (!options) { + log('options not found, can not resume'); + return false; + } + if (cont.cycleTimeout) { + clearTimeout(cont.cycleTimeout); + cont.cycleTimeout = 0; + } + go(options.elements, options, 1, !options.backwards); + } + } +}; + +function removeFilter(el, opts) { + if (!$.support.opacity && opts.cleartype && el.style.filter) { + try { el.style.removeAttribute('filter'); } + catch(smother) {} // handle old opera versions + } +}; + +// unbind event handlers +function destroy(opts) { + if (opts.next) + $(opts.next).unbind(opts.prevNextEvent); + if (opts.prev) + $(opts.prev).unbind(opts.prevNextEvent); + + if (opts.pager || opts.pagerAnchorBuilder) + $.each(opts.pagerAnchors || [], function() { + this.unbind().remove(); + }); + opts.pagerAnchors = null; + if (opts.destroy) // callback + opts.destroy(opts); +}; + +// one-time initialization +function buildOptions($cont, $slides, els, options, o) { + // support metadata plugin (v1.0 and v2.0) + var opts = $.extend({}, $.fn.cycle.defaults, options || {}, $.metadata ? $cont.metadata() : $.meta ? $cont.data() : {}); + if (opts.autostop) + opts.countdown = opts.autostopCount || els.length; + + var cont = $cont[0]; + $cont.data('cycle.opts', opts); + opts.$cont = $cont; + opts.stopCount = cont.cycleStop; + opts.elements = els; + opts.before = opts.before ? [opts.before] : []; + opts.after = opts.after ? [opts.after] : []; + + // push some after callbacks + if (!$.support.opacity && opts.cleartype) + opts.after.push(function() { removeFilter(this, opts); }); + if (opts.continuous) + opts.after.push(function() { go(els,opts,0,!opts.backwards); }); + + saveOriginalOpts(opts); + + // clearType corrections + if (!$.support.opacity && opts.cleartype && !opts.cleartypeNoBg) + clearTypeFix($slides); + + // container requires non-static position so that slides can be position within + if ($cont.css('position') == 'static') + $cont.css('position', 'relative'); + if (opts.width) + $cont.width(opts.width); + if (opts.height && opts.height != 'auto') + $cont.height(opts.height); + + if (opts.startingSlide) + opts.startingSlide = parseInt(opts.startingSlide); + else if (opts.backwards) + opts.startingSlide = els.length - 1; + + // if random, mix up the slide array + if (opts.random) { + opts.randomMap = []; + for (var i = 0; i < els.length; i++) + opts.randomMap.push(i); + opts.randomMap.sort(function(a,b) {return Math.random() - 0.5;}); + opts.randomIndex = 1; + opts.startingSlide = opts.randomMap[1]; + } + else if (opts.startingSlide >= els.length) + opts.startingSlide = 0; // catch bogus input + opts.currSlide = opts.startingSlide || 0; + var first = opts.startingSlide; + + // set position and zIndex on all the slides + $slides.css({position: 'absolute', top:0, left:0}).hide().each(function(i) { + var z; + if (opts.backwards) + z = first ? i <= first ? els.length + (i-first) : first-i : els.length-i; + else + z = first ? i >= first ? els.length - (i-first) : first-i : els.length-i; + $(this).css('z-index', z) + }); + + // make sure first slide is visible + $(els[first]).css('opacity',1).show(); // opacity bit needed to handle restart use case + removeFilter(els[first], opts); + + // stretch slides + if (opts.fit && opts.width) + $slides.width(opts.width); + if (opts.fit && opts.height && opts.height != 'auto') + $slides.height(opts.height); + + // stretch container + var reshape = opts.containerResize && !$cont.innerHeight(); + if (reshape) { // do this only if container has no size http://tinyurl.com/da2oa9 + var maxw = 0, maxh = 0; + for(var j=0; j < els.length; j++) { + var $e = $(els[j]), e = $e[0], w = $e.outerWidth(), h = $e.outerHeight(); + if (!w) w = e.offsetWidth || e.width || $e.attr('width'); + if (!h) h = e.offsetHeight || e.height || $e.attr('height'); + maxw = w > maxw ? w : maxw; + maxh = h > maxh ? h : maxh; + } + if (maxw > 0 && maxh > 0) + $cont.css({width:maxw+'px',height:maxh+'px'}); + } + + if (opts.pause) + $cont.hover(function(){this.cyclePause++;},function(){this.cyclePause--;}); + + if (supportMultiTransitions(opts) === false) + return false; + + // apparently a lot of people use image slideshows without height/width attributes on the images. + // Cycle 2.50+ requires the sizing info for every slide; this block tries to deal with that. + var requeue = false; + options.requeueAttempts = options.requeueAttempts || 0; + $slides.each(function() { + // try to get height/width of each slide + var $el = $(this); + this.cycleH = (opts.fit && opts.height) ? opts.height : ($el.height() || this.offsetHeight || this.height || $el.attr('height') || 0); + this.cycleW = (opts.fit && opts.width) ? opts.width : ($el.width() || this.offsetWidth || this.width || $el.attr('width') || 0); + + if ( $el.is('img') ) { + // sigh.. sniffing, hacking, shrugging... this crappy hack tries to account for what browsers do when + // an image is being downloaded and the markup did not include sizing info (height/width attributes); + // there seems to be some "default" sizes used in this situation + var loadingIE = ($.browser.msie && this.cycleW == 28 && this.cycleH == 30 && !this.complete); + var loadingFF = ($.browser.mozilla && this.cycleW == 34 && this.cycleH == 19 && !this.complete); + var loadingOp = ($.browser.opera && ((this.cycleW == 42 && this.cycleH == 19) || (this.cycleW == 37 && this.cycleH == 17)) && !this.complete); + var loadingOther = (this.cycleH == 0 && this.cycleW == 0 && !this.complete); + // don't requeue for images that are still loading but have a valid size + if (loadingIE || loadingFF || loadingOp || loadingOther) { + if (o.s && opts.requeueOnImageNotLoaded && ++options.requeueAttempts < 100) { // track retry count so we don't loop forever + log(options.requeueAttempts,' - img slide not loaded, requeuing slideshow: ', this.src, this.cycleW, this.cycleH); + setTimeout(function() {$(o.s,o.c).cycle(options)}, opts.requeueTimeout); + requeue = true; + return false; // break each loop + } + else { + log('could not determine size of image: '+this.src, this.cycleW, this.cycleH); + } + } + } + return true; + }); + + if (requeue) + return false; + + opts.cssBefore = opts.cssBefore || {}; + opts.cssAfter = opts.cssAfter || {}; + opts.cssFirst = opts.cssFirst || {}; + opts.animIn = opts.animIn || {}; + opts.animOut = opts.animOut || {}; + + $slides.not(':eq('+first+')').css(opts.cssBefore); + $($slides[first]).css(opts.cssFirst); + + if (opts.timeout) { + opts.timeout = parseInt(opts.timeout); + // ensure that timeout and speed settings are sane + if (opts.speed.constructor == String) + opts.speed = $.fx.speeds[opts.speed] || parseInt(opts.speed); + if (!opts.sync) + opts.speed = opts.speed / 2; + + var buffer = opts.fx == 'none' ? 0 : opts.fx == 'shuffle' ? 500 : 250; + while((opts.timeout - opts.speed) < buffer) // sanitize timeout + opts.timeout += opts.speed; + } + if (opts.easing) + opts.easeIn = opts.easeOut = opts.easing; + if (!opts.speedIn) + opts.speedIn = opts.speed; + if (!opts.speedOut) + opts.speedOut = opts.speed; + + opts.slideCount = els.length; + opts.currSlide = opts.lastSlide = first; + if (opts.random) { + if (++opts.randomIndex == els.length) + opts.randomIndex = 0; + opts.nextSlide = opts.randomMap[opts.randomIndex]; + } + else if (opts.backwards) + opts.nextSlide = opts.startingSlide == 0 ? (els.length-1) : opts.startingSlide-1; + else + opts.nextSlide = opts.startingSlide >= (els.length-1) ? 0 : opts.startingSlide+1; + + // run transition init fn + if (!opts.multiFx) { + var init = $.fn.cycle.transitions[opts.fx]; + if ($.isFunction(init)) + init($cont, $slides, opts); + else if (opts.fx != 'custom' && !opts.multiFx) { + log('unknown transition: ' + opts.fx,'; slideshow terminating'); + return false; + } + } + + // fire artificial events + var e0 = $slides[first]; + if (opts.before.length) + opts.before[0].apply(e0, [e0, e0, opts, true]); + if (opts.after.length) + opts.after[0].apply(e0, [e0, e0, opts, true]); + + if (opts.next) + $(opts.next).bind(opts.prevNextEvent,function(){return advance(opts,1)}); + if (opts.prev) + $(opts.prev).bind(opts.prevNextEvent,function(){return advance(opts,0)}); + if (opts.pager || opts.pagerAnchorBuilder) + buildPager(els,opts); + + exposeAddSlide(opts, els); + + return opts; +}; + +// save off original opts so we can restore after clearing state +function saveOriginalOpts(opts) { + opts.original = { before: [], after: [] }; + opts.original.cssBefore = $.extend({}, opts.cssBefore); + opts.original.cssAfter = $.extend({}, opts.cssAfter); + opts.original.animIn = $.extend({}, opts.animIn); + opts.original.animOut = $.extend({}, opts.animOut); + $.each(opts.before, function() { opts.original.before.push(this); }); + $.each(opts.after, function() { opts.original.after.push(this); }); +}; + +function supportMultiTransitions(opts) { + var i, tx, txs = $.fn.cycle.transitions; + // look for multiple effects + if (opts.fx.indexOf(',') > 0) { + opts.multiFx = true; + opts.fxs = opts.fx.replace(/\s*/g,'').split(','); + // discard any bogus effect names + for (i=0; i < opts.fxs.length; i++) { + var fx = opts.fxs[i]; + tx = txs[fx]; + if (!tx || !txs.hasOwnProperty(fx) || !$.isFunction(tx)) { + log('discarding unknown transition: ',fx); + opts.fxs.splice(i,1); + i--; + } + } + // if we have an empty list then we threw everything away! + if (!opts.fxs.length) { + log('No valid transitions named; slideshow terminating.'); + return false; + } + } + else if (opts.fx == 'all') { // auto-gen the list of transitions + opts.multiFx = true; + opts.fxs = []; + for (p in txs) { + tx = txs[p]; + if (txs.hasOwnProperty(p) && $.isFunction(tx)) + opts.fxs.push(p); + } + } + if (opts.multiFx && opts.randomizeEffects) { + // munge the fxs array to make effect selection random + var r1 = Math.floor(Math.random() * 20) + 30; + for (i = 0; i < r1; i++) { + var r2 = Math.floor(Math.random() * opts.fxs.length); + opts.fxs.push(opts.fxs.splice(r2,1)[0]); + } + debug('randomized fx sequence: ',opts.fxs); + } + return true; +}; + +// provide a mechanism for adding slides after the slideshow has started +function exposeAddSlide(opts, els) { + opts.addSlide = function(newSlide, prepend) { + var $s = $(newSlide), s = $s[0]; + if (!opts.autostopCount) + opts.countdown++; + els[prepend?'unshift':'push'](s); + if (opts.els) + opts.els[prepend?'unshift':'push'](s); // shuffle needs this + opts.slideCount = els.length; + + $s.css('position','absolute'); + $s[prepend?'prependTo':'appendTo'](opts.$cont); + + if (prepend) { + opts.currSlide++; + opts.nextSlide++; + } + + if (!$.support.opacity && opts.cleartype && !opts.cleartypeNoBg) + clearTypeFix($s); + + if (opts.fit && opts.width) + $s.width(opts.width); + if (opts.fit && opts.height && opts.height != 'auto') + $s.height(opts.height); + s.cycleH = (opts.fit && opts.height) ? opts.height : $s.height(); + s.cycleW = (opts.fit && opts.width) ? opts.width : $s.width(); + + $s.css(opts.cssBefore); + + if (opts.pager || opts.pagerAnchorBuilder) + $.fn.cycle.createPagerAnchor(els.length-1, s, $(opts.pager), els, opts); + + if ($.isFunction(opts.onAddSlide)) + opts.onAddSlide($s); + else + $s.hide(); // default behavior + }; +} + +// reset internal state; we do this on every pass in order to support multiple effects +$.fn.cycle.resetState = function(opts, fx) { + fx = fx || opts.fx; + opts.before = []; opts.after = []; + opts.cssBefore = $.extend({}, opts.original.cssBefore); + opts.cssAfter = $.extend({}, opts.original.cssAfter); + opts.animIn = $.extend({}, opts.original.animIn); + opts.animOut = $.extend({}, opts.original.animOut); + opts.fxFn = null; + $.each(opts.original.before, function() { opts.before.push(this); }); + $.each(opts.original.after, function() { opts.after.push(this); }); + + // re-init + var init = $.fn.cycle.transitions[fx]; + if ($.isFunction(init)) + init(opts.$cont, $(opts.elements), opts); +}; + +// this is the main engine fn, it handles the timeouts, callbacks and slide index mgmt +function go(els, opts, manual, fwd) { + // opts.busy is true if we're in the middle of an animation + if (manual && opts.busy && opts.manualTrump) { + // let manual transitions requests trump active ones + debug('manualTrump in go(), stopping active transition'); + $(els).stop(true,true); + opts.busy = 0; + } + // don't begin another timeout-based transition if there is one active + if (opts.busy) { + debug('transition active, ignoring new tx request'); + return; + } + + var p = opts.$cont[0], curr = els[opts.currSlide], next = els[opts.nextSlide]; + + // stop cycling if we have an outstanding stop request + if (p.cycleStop != opts.stopCount || p.cycleTimeout === 0 && !manual) + return; + + // check to see if we should stop cycling based on autostop options + if (!manual && !p.cyclePause && !opts.bounce && + ((opts.autostop && (--opts.countdown <= 0)) || + (opts.nowrap && !opts.random && opts.nextSlide < opts.currSlide))) { + if (opts.end) + opts.end(opts); + return; + } + + // if slideshow is paused, only transition on a manual trigger + var changed = false; + if ((manual || !p.cyclePause) && (opts.nextSlide != opts.currSlide)) { + changed = true; + var fx = opts.fx; + // keep trying to get the slide size if we don't have it yet + curr.cycleH = curr.cycleH || $(curr).height(); + curr.cycleW = curr.cycleW || $(curr).width(); + next.cycleH = next.cycleH || $(next).height(); + next.cycleW = next.cycleW || $(next).width(); + + // support multiple transition types + if (opts.multiFx) { + if (opts.lastFx == undefined || ++opts.lastFx >= opts.fxs.length) + opts.lastFx = 0; + fx = opts.fxs[opts.lastFx]; + opts.currFx = fx; + } + + // one-time fx overrides apply to: $('div').cycle(3,'zoom'); + if (opts.oneTimeFx) { + fx = opts.oneTimeFx; + opts.oneTimeFx = null; + } + + $.fn.cycle.resetState(opts, fx); + + // run the before callbacks + if (opts.before.length) + $.each(opts.before, function(i,o) { + if (p.cycleStop != opts.stopCount) return; + o.apply(next, [curr, next, opts, fwd]); + }); + + // stage the after callacks + var after = function() { + opts.busy = 0; + $.each(opts.after, function(i,o) { + if (p.cycleStop != opts.stopCount) return; + o.apply(next, [curr, next, opts, fwd]); + }); + }; + + debug('tx firing('+fx+'); currSlide: ' + opts.currSlide + '; nextSlide: ' + opts.nextSlide); + + // get ready to perform the transition + opts.busy = 1; + if (opts.fxFn) // fx function provided? + opts.fxFn(curr, next, opts, after, fwd, manual && opts.fastOnEvent); + else if ($.isFunction($.fn.cycle[opts.fx])) // fx plugin ? + $.fn.cycle[opts.fx](curr, next, opts, after, fwd, manual && opts.fastOnEvent); + else + $.fn.cycle.custom(curr, next, opts, after, fwd, manual && opts.fastOnEvent); + } + + if (changed || opts.nextSlide == opts.currSlide) { + // calculate the next slide + opts.lastSlide = opts.currSlide; + if (opts.random) { + opts.currSlide = opts.nextSlide; + if (++opts.randomIndex == els.length) + opts.randomIndex = 0; + opts.nextSlide = opts.randomMap[opts.randomIndex]; + if (opts.nextSlide == opts.currSlide) + opts.nextSlide = (opts.currSlide == opts.slideCount - 1) ? 0 : opts.currSlide + 1; + } + else if (opts.backwards) { + var roll = (opts.nextSlide - 1) < 0; + if (roll && opts.bounce) { + opts.backwards = !opts.backwards; + opts.nextSlide = 1; + opts.currSlide = 0; + } + else { + opts.nextSlide = roll ? (els.length-1) : opts.nextSlide-1; + opts.currSlide = roll ? 0 : opts.nextSlide+1; + } + } + else { // sequence + var roll = (opts.nextSlide + 1) == els.length; + if (roll && opts.bounce) { + opts.backwards = !opts.backwards; + opts.nextSlide = els.length-2; + opts.currSlide = els.length-1; + } + else { + opts.nextSlide = roll ? 0 : opts.nextSlide+1; + opts.currSlide = roll ? els.length-1 : opts.nextSlide-1; + } + } + } + if (changed && opts.pager) + opts.updateActivePagerLink(opts.pager, opts.currSlide, opts.activePagerClass); + + // stage the next transition + var ms = 0; + if (opts.timeout && !opts.continuous) + ms = getTimeout(els[opts.currSlide], els[opts.nextSlide], opts, fwd); + else if (opts.continuous && p.cyclePause) // continuous shows work off an after callback, not this timer logic + ms = 10; + if (ms > 0) + p.cycleTimeout = setTimeout(function(){ go(els, opts, 0, !opts.backwards) }, ms); +}; + +// invoked after transition +$.fn.cycle.updateActivePagerLink = function(pager, currSlide, clsName) { + $(pager).each(function() { + $(this).children().removeClass(clsName).eq(currSlide).addClass(clsName); + }); +}; + +// calculate timeout value for current transition +function getTimeout(curr, next, opts, fwd) { + if (opts.timeoutFn) { + // call user provided calc fn + var t = opts.timeoutFn.call(curr,curr,next,opts,fwd); + while (opts.fx != 'none' && (t - opts.speed) < 250) // sanitize timeout + t += opts.speed; + debug('calculated timeout: ' + t + '; speed: ' + opts.speed); + if (t !== false) + return t; + } + return opts.timeout; +}; + +// expose next/prev function, caller must pass in state +$.fn.cycle.next = function(opts) { advance(opts,1); }; +$.fn.cycle.prev = function(opts) { advance(opts,0);}; + +// advance slide forward or back +function advance(opts, moveForward) { + var val = moveForward ? 1 : -1; + var els = opts.elements; + var p = opts.$cont[0], timeout = p.cycleTimeout; + if (timeout) { + clearTimeout(timeout); + p.cycleTimeout = 0; + } + if (opts.random && val < 0) { + // move back to the previously display slide + opts.randomIndex--; + if (--opts.randomIndex == -2) + opts.randomIndex = els.length-2; + else if (opts.randomIndex == -1) + opts.randomIndex = els.length-1; + opts.nextSlide = opts.randomMap[opts.randomIndex]; + } + else if (opts.random) { + opts.nextSlide = opts.randomMap[opts.randomIndex]; + } + else { + opts.nextSlide = opts.currSlide + val; + if (opts.nextSlide < 0) { + if (opts.nowrap) return false; + opts.nextSlide = els.length - 1; + } + else if (opts.nextSlide >= els.length) { + if (opts.nowrap) return false; + opts.nextSlide = 0; + } + } + + var cb = opts.onPrevNextEvent || opts.prevNextClick; // prevNextClick is deprecated + if ($.isFunction(cb)) + cb(val > 0, opts.nextSlide, els[opts.nextSlide]); + go(els, opts, 1, moveForward); + return false; +}; + +function buildPager(els, opts) { + var $p = $(opts.pager); + $.each(els, function(i,o) { + $.fn.cycle.createPagerAnchor(i,o,$p,els,opts); + }); + opts.updateActivePagerLink(opts.pager, opts.startingSlide, opts.activePagerClass); +}; + +$.fn.cycle.createPagerAnchor = function(i, el, $p, els, opts) { + var a; + if ($.isFunction(opts.pagerAnchorBuilder)) { + a = opts.pagerAnchorBuilder(i,el); + debug('pagerAnchorBuilder('+i+', el) returned: ' + a); + } + else + a = ''+(i+1)+''; + + if (!a) + return; + var $a = $(a); + // don't reparent if anchor is in the dom + if ($a.parents('body').length === 0) { + var arr = []; + if ($p.length > 1) { + $p.each(function() { + var $clone = $a.clone(true); + $(this).append($clone); + arr.push($clone[0]); + }); + $a = $(arr); + } + else { + $a.appendTo($p); + } + } + + opts.pagerAnchors = opts.pagerAnchors || []; + opts.pagerAnchors.push($a); + $a.bind(opts.pagerEvent, function(e) { + e.preventDefault(); + opts.nextSlide = i; + var p = opts.$cont[0], timeout = p.cycleTimeout; + if (timeout) { + clearTimeout(timeout); + p.cycleTimeout = 0; + } + var cb = opts.onPagerEvent || opts.pagerClick; // pagerClick is deprecated + if ($.isFunction(cb)) + cb(opts.nextSlide, els[opts.nextSlide]); + go(els,opts,1,opts.currSlide < i); // trigger the trans +// return false; // <== allow bubble + }); + + if ( ! /^click/.test(opts.pagerEvent) && !opts.allowPagerClickBubble) + $a.bind('click.cycle', function(){return false;}); // suppress click + + if (opts.pauseOnPagerHover) + $a.hover(function() { opts.$cont[0].cyclePause++; }, function() { opts.$cont[0].cyclePause--; } ); +}; + +// helper fn to calculate the number of slides between the current and the next +$.fn.cycle.hopsFromLast = function(opts, fwd) { + var hops, l = opts.lastSlide, c = opts.currSlide; + if (fwd) + hops = c > l ? c - l : opts.slideCount - l; + else + hops = c < l ? l - c : l + opts.slideCount - c; + return hops; +}; + +// fix clearType problems in ie6 by setting an explicit bg color +// (otherwise text slides look horrible during a fade transition) +function clearTypeFix($slides) { + debug('applying clearType background-color hack'); + function hex(s) { + s = parseInt(s).toString(16); + return s.length < 2 ? '0'+s : s; + }; + function getBg(e) { + for ( ; e && e.nodeName.toLowerCase() != 'html'; e = e.parentNode) { + var v = $.css(e,'background-color'); + if (v && v.indexOf('rgb') >= 0 ) { + var rgb = v.match(/\d+/g); + return '#'+ hex(rgb[0]) + hex(rgb[1]) + hex(rgb[2]); + } + if (v && v != 'transparent') + return v; + } + return '#ffffff'; + }; + $slides.each(function() { $(this).css('background-color', getBg(this)); }); +}; + +// reset common props before the next transition +$.fn.cycle.commonReset = function(curr,next,opts,w,h,rev) { + $(opts.elements).not(curr).hide(); + if (typeof opts.cssBefore.opacity == 'undefined') + opts.cssBefore.opacity = 1; + opts.cssBefore.display = 'block'; + if (opts.slideResize && w !== false && next.cycleW > 0) + opts.cssBefore.width = next.cycleW; + if (opts.slideResize && h !== false && next.cycleH > 0) + opts.cssBefore.height = next.cycleH; + opts.cssAfter = opts.cssAfter || {}; + opts.cssAfter.display = 'none'; + $(curr).css('zIndex',opts.slideCount + (rev === true ? 1 : 0)); + $(next).css('zIndex',opts.slideCount + (rev === true ? 0 : 1)); +}; + +// the actual fn for effecting a transition +$.fn.cycle.custom = function(curr, next, opts, cb, fwd, speedOverride) { + var $l = $(curr), $n = $(next); + var speedIn = opts.speedIn, speedOut = opts.speedOut, easeIn = opts.easeIn, easeOut = opts.easeOut; + $n.css(opts.cssBefore); + if (speedOverride) { + if (typeof speedOverride == 'number') + speedIn = speedOut = speedOverride; + else + speedIn = speedOut = 1; + easeIn = easeOut = null; + } + var fn = function() { + $n.animate(opts.animIn, speedIn, easeIn, function() { + cb(); + }); + }; + $l.animate(opts.animOut, speedOut, easeOut, function() { + $l.css(opts.cssAfter); + if (!opts.sync) + fn(); + }); + if (opts.sync) fn(); +}; + +// transition definitions - only fade is defined here, transition pack defines the rest +$.fn.cycle.transitions = { + fade: function($cont, $slides, opts) { + $slides.not(':eq('+opts.currSlide+')').css('opacity',0); + opts.before.push(function(curr,next,opts) { + $.fn.cycle.commonReset(curr,next,opts); + opts.cssBefore.opacity = 0; + }); + opts.animIn = { opacity: 1 }; + opts.animOut = { opacity: 0 }; + opts.cssBefore = { top: 0, left: 0 }; + } +}; + +$.fn.cycle.ver = function() { return ver; }; + +// override these globally if you like (they are all optional) +$.fn.cycle.defaults = { + activePagerClass: 'activeSlide', // class name used for the active pager link + after: null, // transition callback (scope set to element that was shown): function(currSlideElement, nextSlideElement, options, forwardFlag) + allowPagerClickBubble: false, // allows or prevents click event on pager anchors from bubbling + animIn: null, // properties that define how the slide animates in + animOut: null, // properties that define how the slide animates out + autostop: 0, // true to end slideshow after X transitions (where X == slide count) + autostopCount: 0, // number of transitions (optionally used with autostop to define X) + backwards: false, // true to start slideshow at last slide and move backwards through the stack + before: null, // transition callback (scope set to element to be shown): function(currSlideElement, nextSlideElement, options, forwardFlag) + cleartype: !$.support.opacity, // true if clearType corrections should be applied (for IE) + cleartypeNoBg: false, // set to true to disable extra cleartype fixing (leave false to force background color setting on slides) + containerResize: 1, // resize container to fit largest slide + continuous: 0, // true to start next transition immediately after current one completes + cssAfter: null, // properties that defined the state of the slide after transitioning out + cssBefore: null, // properties that define the initial state of the slide before transitioning in + delay: 0, // additional delay (in ms) for first transition (hint: can be negative) + easeIn: null, // easing for "in" transition + easeOut: null, // easing for "out" transition + easing: null, // easing method for both in and out transitions + end: null, // callback invoked when the slideshow terminates (use with autostop or nowrap options): function(options) + fastOnEvent: 0, // force fast transitions when triggered manually (via pager or prev/next); value == time in ms + fit: 0, // force slides to fit container + fx: 'fade', // name of transition effect (or comma separated names, ex: 'fade,scrollUp,shuffle') + fxFn: null, // function used to control the transition: function(currSlideElement, nextSlideElement, options, afterCalback, forwardFlag) + height: 'auto', // container height + manualTrump: true, // causes manual transition to stop an active transition instead of being ignored + next: null, // selector for element to use as event trigger for next slide + nowrap: 0, // true to prevent slideshow from wrapping + onPagerEvent: null, // callback fn for pager events: function(zeroBasedSlideIndex, slideElement) + onPrevNextEvent: null, // callback fn for prev/next events: function(isNext, zeroBasedSlideIndex, slideElement) + pager: null, // selector for element to use as pager container + pagerAnchorBuilder: null, // callback fn for building anchor links: function(index, DOMelement) + pagerEvent: 'click.cycle', // name of event which drives the pager navigation + pause: 0, // true to enable "pause on hover" + pauseOnPagerHover: 0, // true to pause when hovering over pager link + prev: null, // selector for element to use as event trigger for previous slide + prevNextEvent:'click.cycle',// event which drives the manual transition to the previous or next slide + random: 0, // true for random, false for sequence (not applicable to shuffle fx) + randomizeEffects: 1, // valid when multiple effects are used; true to make the effect sequence random + requeueOnImageNotLoaded: true, // requeue the slideshow if any image slides are not yet loaded + requeueTimeout: 250, // ms delay for requeue + rev: 0, // causes animations to transition in reverse (for effects that support it such as scrollHorz/scrollVert/shuffle) + shuffle: null, // coords for shuffle animation, ex: { top:15, left: 200 } + slideExpr: null, // expression for selecting slides (if something other than all children is required) + slideResize: 1, // force slide width/height to fixed size before every transition + speed: 1000, // speed of the transition (any valid fx speed value) + speedIn: null, // speed of the 'in' transition + speedOut: null, // speed of the 'out' transition + startingSlide: 0, // zero-based index of the first slide to be displayed + sync: 1, // true if in/out transitions should occur simultaneously + timeout: 4000, // milliseconds between slide transitions (0 to disable auto advance) + timeoutFn: null, // callback for determining per-slide timeout value: function(currSlideElement, nextSlideElement, options, forwardFlag) + updateActivePagerLink: null // callback fn invoked to update the active pager link (adds/removes activePagerClass style) +}; + +})(jQuery); diff --git a/html/scripts/src/jquery.cycle.lite.js b/html/scripts/src/jquery.cycle.lite.js new file mode 100644 index 0000000..eed217e --- /dev/null +++ b/html/scripts/src/jquery.cycle.lite.js @@ -0,0 +1,202 @@ +/* + * jQuery Cycle Lite Plugin + * http://malsup.com/jquery/cycle/lite/ + * Copyright (c) 2008-2011 M. Alsup + * Version: 1.1 (03/07/2011) + * Dual licensed under the MIT and GPL licenses: + * http://www.opensource.org/licenses/mit-license.php + * http://www.gnu.org/licenses/gpl.html + * Requires: jQuery v1.3.2 or later + */ +;(function($) { + +var ver = 'Lite-1.1'; + +$.fn.cycle = function(options) { + return this.each(function() { + options = options || {}; + + if (this.cycleTimeout) clearTimeout(this.cycleTimeout); + this.cycleTimeout = 0; + this.cyclePause = 0; + + var $cont = $(this); + var $slides = options.slideExpr ? $(options.slideExpr, this) : $cont.children(); + var els = $slides.get(); + if (els.length < 2) { + window.console && console.log('terminating; too few slides: ' + els.length); + return; // don't bother + } + + // support metadata plugin (v1.0 and v2.0) + var opts = $.extend({}, $.fn.cycle.defaults, options || {}, $.metadata ? $cont.metadata() : $.meta ? $cont.data() : {}); + + opts.before = opts.before ? [opts.before] : []; + opts.after = opts.after ? [opts.after] : []; + opts.after.unshift(function(){ opts.busy=0; }); + + // allow shorthand overrides of width, height and timeout + var cls = this.className; + opts.width = parseInt((cls.match(/w:(\d+)/)||[])[1]) || opts.width; + opts.height = parseInt((cls.match(/h:(\d+)/)||[])[1]) || opts.height; + opts.timeout = parseInt((cls.match(/t:(\d+)/)||[])[1]) || opts.timeout; + + if ($cont.css('position') == 'static') + $cont.css('position', 'relative'); + if (opts.width) + $cont.width(opts.width); + if (opts.height && opts.height != 'auto') + $cont.height(opts.height); + + var first = 0; + $slides.css({position: 'absolute', top:0, left:0}).each(function(i) { + $(this).css('z-index', els.length-i) + }); + + $(els[first]).css('opacity',1).show(); // opacity bit needed to handle reinit case + if ($.browser.msie) els[first].style.removeAttribute('filter'); + + if (opts.fit && opts.width) + $slides.width(opts.width); + if (opts.fit && opts.height && opts.height != 'auto') + $slides.height(opts.height); + if (opts.pause) + $cont.hover(function(){this.cyclePause=1;}, function(){this.cyclePause=0;}); + + var txFn = $.fn.cycle.transitions[opts.fx]; + txFn && txFn($cont, $slides, opts); + + $slides.each(function() { + var $el = $(this); + this.cycleH = (opts.fit && opts.height) ? opts.height : $el.height(); + this.cycleW = (opts.fit && opts.width) ? opts.width : $el.width(); + }); + + if (opts.cssFirst) + $($slides[first]).css(opts.cssFirst); + + if (opts.timeout) { + // ensure that timeout and speed settings are sane + if (opts.speed.constructor == String) + opts.speed = {slow: 600, fast: 200}[opts.speed] || 400; + if (!opts.sync) + opts.speed = opts.speed / 2; + while((opts.timeout - opts.speed) < 250) + opts.timeout += opts.speed; + } + opts.speedIn = opts.speed; + opts.speedOut = opts.speed; + + opts.slideCount = els.length; + opts.currSlide = first; + opts.nextSlide = 1; + + // fire artificial events + var e0 = $slides[first]; + if (opts.before.length) + opts.before[0].apply(e0, [e0, e0, opts, true]); + if (opts.after.length > 1) + opts.after[1].apply(e0, [e0, e0, opts, true]); + + if (opts.click && !opts.next) + opts.next = opts.click; + if (opts.next) + $(opts.next).bind('click', function(){return advance(els,opts,opts.rev?-1:1)}); + if (opts.prev) + $(opts.prev).bind('click', function(){return advance(els,opts,opts.rev?1:-1)}); + + if (opts.timeout) + this.cycleTimeout = setTimeout(function() { + go(els,opts,0,!opts.rev) + }, opts.timeout + (opts.delay||0)); + }); +}; + +function go(els, opts, manual, fwd) { + if (opts.busy) return; + var p = els[0].parentNode, curr = els[opts.currSlide], next = els[opts.nextSlide]; + if (p.cycleTimeout === 0 && !manual) + return; + + if (manual || !p.cyclePause) { + if (opts.before.length) + $.each(opts.before, function(i,o) { o.apply(next, [curr, next, opts, fwd]); }); + var after = function() { + if ($.browser.msie) + this.style.removeAttribute('filter'); + $.each(opts.after, function(i,o) { o.apply(next, [curr, next, opts, fwd]); }); + }; + + if (opts.nextSlide != opts.currSlide) { + opts.busy = 1; + $.fn.cycle.custom(curr, next, opts, after); + } + var roll = (opts.nextSlide + 1) == els.length; + opts.nextSlide = roll ? 0 : opts.nextSlide+1; + opts.currSlide = roll ? els.length-1 : opts.nextSlide-1; + } + if (opts.timeout) + p.cycleTimeout = setTimeout(function() { go(els,opts,0,!opts.rev) }, opts.timeout); +}; + +// advance slide forward or back +function advance(els, opts, val) { + var p = els[0].parentNode, timeout = p.cycleTimeout; + if (timeout) { + clearTimeout(timeout); + p.cycleTimeout = 0; + } + opts.nextSlide = opts.currSlide + val; + if (opts.nextSlide < 0) { + opts.nextSlide = els.length - 1; + } + else if (opts.nextSlide >= els.length) { + opts.nextSlide = 0; + } + go(els, opts, 1, val>=0); + return false; +}; + +$.fn.cycle.custom = function(curr, next, opts, cb) { + var $l = $(curr), $n = $(next); + $n.css(opts.cssBefore); + var fn = function() {$n.animate(opts.animIn, opts.speedIn, opts.easeIn, cb)}; + $l.animate(opts.animOut, opts.speedOut, opts.easeOut, function() { + $l.css(opts.cssAfter); + if (!opts.sync) fn(); + }); + if (opts.sync) fn(); +}; + +$.fn.cycle.transitions = { + fade: function($cont, $slides, opts) { + opts.cssBefore = { opacity: 0 }; + opts.animOut = { opacity: 0 }; + opts.animIn = { opacity: 1 }; + } +}; + +$.fn.cycle.ver = function() { return ver; }; + +// @see: http://malsup.com/jquery/cycle/lite/ +$.fn.cycle.defaults = { + fx: 'fade', + timeout: 4000, + speed: 1000, + next: null, + prev: null, + before: null, + after: null, + height: 'auto', + sync: 1, + fit: 0, + pause: 0, + delay: 0, + slideExpr: null, + cssBefore: {}, + cssAfter: {}, + animIn: {}, + animOut: {} +}; + +})(jQuery); diff --git a/html/sites/all/themes/plots/js/README.textile b/html/sites/all/themes/plots/js/README.textile new file mode 100644 index 0000000..751fbcd --- /dev/null +++ b/html/sites/all/themes/plots/js/README.textile @@ -0,0 +1,80 @@ +h1. Table of Contents jQuery Plugin + +*Version* 0.8
+*Written by* "Doug Neiner":http://pixelgraphics.us
+*Open Sourced under the MIT License* + +_Based on code and concept by Janko Jovanovic in his article: "Automatically generate table of contents using jQuery":http://www.jankoatwarpspeed.com/post/2009/08/20/Table-of-contents-using-jQuery.aspx ._ + +This plugin creates a table of contents based on the

through
tags of a webpage, or subset of a webpage. It auto generates slug-like id's for linking if they are not present already, can optionally add "Top" links to each heading, offers the choice of nested
    or
  1. lists or straight links, and can optionally create a proportionate display based on the positioning of the headings. + +h2. Examples + +* "Example 1: Standard TOC, Default Options, Bulleted List":http://fuelyourcoding.com/scripts/toc/examples/example1.html +* "Example 2: H2 through H4, With Top Links, Scoped to div#wrapper, Ordered List":http://fuelyourcoding.com/scripts/toc/examples/example2.html +* "Example 3: Proportionate Spacing, H1 through H4":http://fuelyourcoding.com/scripts/toc/examples/example3.html + +h2. Features + +* Can be scoped to any area of the page +* Automatically determines list type based on the container DOM element for the Table of Contents +* Supports nested ordered and unordered lists, as well as a straight listing of links +* Auto-creates slug-like anchors for each heading (on headings that do not already have an id) +* Supports a proportionateSpacing formatting option to create visual TOC that represent actual amounts of content +* Is fully customizable. No CSS classes or ID's hard-coded into the plugin. +* Supports multiple instances on one page. + +h2. Future Feature Ideas + +* A popup preview that shows the first few lines of the content following the heading you hover over. +* Built-in support for the jQuery smooth scrolling anchor plugin (This plugin does nothing to hinder a smooth scrolling anchor plugin's use, it simply does not offer the functionality out of the box). + +h2. Basic Usage + +Include the jQuery library and the Table of Contents Plugin + +
    +
    +
    + +And call the plugin on an empty container element: + +
    +
    + +The container element determines the type of list that is displayed. A
      container will produce a nested ordered list. A