// Copyright © 2006, Robert Kieffer
// 
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License.  You may obtain a copy
// of the License at
// 
// http://www.apache.org/licenses/LICENSE-2.0
// 
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
// License for the specific language governing permissions and limitations
// under the License.
//
// For further information, please contact the author at robert(at)broofa.com


/**
 * The Thumber script is designed to allow web pages to simply and easily
 * display zoomable thumbnail images in-page.  Zoom-behavior is completely
 * defined by CSS and Image pathnames, with no need for additional javascript.
 * This eliminates the need to constantly wrap images inside link tags, and
 * provides powerdul styling capabilities.
 *
 * The script uses the Thumber.PATTERNS structure (defined below) to identify
 * thumbnail images in the current document by their URL. Each matching image
 * is configured so that when it is clicked, the following actions are taken:
 *
 *  - The image path is changed to point to the full-size version of the image
 *  (also defined in the PATTERNS structure)
 *  - A CSS classname of either STYLES[0]+NORMAL_SUFFIX or
 *  STYLES[0]+ACTIVE_SUFFIX is added to the image.
 *  - The parent elements of the image are searched for the a parent with a
 *  classname that matches one of the names defined in the PARENT_STYLES
 *  array.
 *
 * For the first parent found, the classname is set to either
 * parentstyle+NORMAL_SUFFIX or parentstyle+ACTIVE_SUFFIX.
 */
function Thumber() {}

var Thumber = {
  /**
  * Define the mapping between thumbnail files and their full-size counterparts.
  *
  * This structure defines a regular expression used to recognize a thumbnail
  * path, and a corresponding substitution to create the full-size images path.
  * For example, to map thumbnail images with URLs like "image-[one or more
  * numeric digits]a.jpg", to a fullsize image with a path of "image-[whichever
  * digits matched]b.jpg" you would add the following entry:
  *
  *    {re: /image-(\d+)a.jpg/, sub:'image-$1b.jpg'}
  *
  */
  PATTERNS: [
    // "thumb-*" to "*"
    {re: /thumb-/, sub:''},

    // Anything in a WordPress uploads directory
    {re: /(.*wp-content\/uploads.*)-\d+x\d+(.\w*)$/, sub:'$1$2'},

    // "*.thumbnail.*" to "*"
    {re: /\.thumbnail\./, sub:'.'},

    // "/Thumbnails/*" to "/Slides/*"
    {re: /\/Thumbnails\//, sub:'/Slides/'},

    // "(some number)a.*" to "(some number)b.*"
    {re: /(\d+)a.(jpg|gif|png)/i, sub:'$1b.$2'},

    // flickr images
    {re: /(flickr.com.*)_m.jpg/, sub:'$1_o.jpg'},

    null
  ],

  /**
  * CSS styles used by Thumber. The first item in this array is required, and
  * defines the prefix of the style that is assigned to the images Thumber
  * operates on.  All subsequent items define the styles Thumber looks for when
  * setting the class name of an images parent(s).
  */
  STYLES: [
    'thumber',      // (Required) Style for images
    'thumber-parent',   // (Optional) 1st parent style to look for
    'filmstrip'     // (Optional) 2nd parent style... etc.
  ],

  /** String to append to image and parent classnames when the image is in it's normal state */
  NORMAL_SUFFIX: '-small',

  /** String to append to image and parent classnames when the image is in it's active state */
  ACTIVE_SUFFIX: '-large',

  /** The text to set (or append to existing text) */
  TOOLTIP_TEXT: 'Click to Zoom',

  /**
  * Configure event handlers on all images that look like thumbnails
  */
  init: function() {
    // Cache useful style state
    for (var i = 0; i < Thumber.STYLES.length; i++) {
      var s = Thumber.STYLES[i];
      var si = {};
      si.base = Thumber.STYLES[i];
      si.normal = s + Thumber.NORMAL_SUFFIX;
      si.active = s + Thumber.ACTIVE_SUFFIX;
      si.regex = new RegExp('(' + si.normal + '|' + si.active  + ')');
      Thumber.STYLES[i] = si;
    }

    // Loop through all images
    var imgs = document.getElementsByTagName('img');
    for (var i = 0; i < imgs.length; i++) {
      var img = imgs[i];
      if (Thumber.inLink(img)) continue;

      // Loop through all PATTERNS
      for (var j = 0; j < Thumber.PATTERNS.length; j++) {
        var p = Thumber.PATTERNS[j];
        if (!p) continue;

        // If the image matches a pattern ...
        if (img.src && p.re.test(img.src)) {
          var info = {
            origURL: img.src,
            newURL: img.src.replace(p.re,p.sub),
            title: img.title,
            width: img.width,
            height: img.height,
            showingNew: false
          };
          Thumber.attach(img, info);
          break;
        }
      }
    }
  },

  inLink: function(el) {
    while (el) {
      if (el.nodeName == 'a' || el.nodeName == 'A') return true;
      el = el.parentNode;
    }
    return false;
  },

  /**
  * Event handler for all images
  */
  _handleClick: function(e) {
    e = e || event;
    var img = e.target || e.srcElement;

    Thumber._expand(img, !img.info.showingNew);

    // Just in case ...
    try {
      e.preventDefault();
    } catch (e) {}

    return false;
  },

  _expand: function(img, expand) {
      var info = img.info;
      // Toggle which URL we're showing
      info.showingNew = expand;
      img.src = info.showingNew ? info.newURL : info.origURL;
      Thumber.setClassname(img, info.showingNew);
  },

  _expandAll: function(flag) {
    var imgs = document.getElementsByTagName('img');
    for (var i = 0; i < imgs.length; i++) {
      var img = imgs[i];
      if (!img.info) continue;
      switch(flag) {
        case 0: Thumber._expand(img, false); break;
        case 1: Thumber._expand(img, true); break;
        case 2: Thumber._expand(img, !img.info.showingNew); break;
      }
    }
  },

  shrinkAll: function() {this._expandAll(0);},
  expandAll: function() {this._expandAll(1);},
  toggleAll: function() {this._expandAll(2);},

  /**
  * Create the event handlers that manage this image
  */
  attach: function(img, info) {
    // Update the image tooltip
    if (Thumber.TOOLTIP_TEXT) {
      img.title = img.title ? img.title +
        ' (' + Thumber.TOOLTIP_TEXT + ')' : Thumber.TOOLTIP_TEXT;
    }

    // Init the classname
    Thumber.setClassname(img, info.showingNew);

    // Create the event handler
    img.info = info;
    img.onclick = Thumber._handleClick;
  },

  /**
  * Set the CSS classname of an image
  */
  setClassname: function(el, active) {
    var style = Thumber.STYLES[0];

    // Set the class of the image
    Thumber.replaceClassname(el,
        active ? style.active : style.normal, style.regex);

    // Set the class of any marked container
    do {
      el = el.parentNode;
      if (el) {
        for (var i = 1; i < Thumber.STYLES.length; i++) {
          var style = Thumber.STYLES[i];
          if (el.className && el.className.indexOf(style.base) >= 0) {
            Thumber.replaceClassname(el,
                active ? style.active : style.normal, style.regex);
            break;
          }
        }
      }
    } while (el);
  },

  /**
  * Modify the className property of an element so that it includes newClass.
  * And, if a name matching oldClass (either string or regex) is found, remove
  * that.
  */
  replaceClassname: function(el, newClass, oldClass) {
    var cn = el.className || '';
    if (oldClass) {
      cn = cn.replace(oldClass, '');
    }
    cn += ' ' + newClass;
    cn = cn.replace(/\s{2,}/, ' ').replace(/^\s*(.*?)\s*$/, '$1');
    el.className = cn;
  },
};


if (window.addEventListener) {
  window.addEventListener('load', Thumber.init, false);
} else {
  window.onload = Thumber.init;
}
