<commit_before>/*globals document,window,$,jQuery,Image */
/*jslint white: true, browser: true, onevar: true, undef: true, eqeqeq: true, plusplus: true, bitwise: true, regexp: true, strict: true, newcap: true, immed: true, indent: 2 */

"use strict";

/*
 * jQuery UI Lightbox
 *
 * Copyright (c) 2009 Benjamin Doherty
 * Dual licensed under the MIT (MIT-LICENSE.txt)
 * and GPL (GPL-LICENSE.txt) licenses.
 *
 * Depends:
 *  ui.core.js
 *  ui.dialog.js
 *  jquery.mousewheel.js (optional)
 */
(function ($) {

  $.widget('ui.lightbox', {
    _init: function () {
      var self = this;

      // consider event delegation to make this more dynamic
      $(this.options.selector, this.element).click(function (event) {
        event.preventDefault();
        self.open(this);
      });
      $(document).keydown(function (event) {
        if (!self.lightbox || !self.lightbox.dialog('isOpen')) {
          return;
        }
        switch (event.keyCode) {
        case $.ui.keyCode.ESCAPE:
          if (self.options.closeOnEscape) {
            self.close();
          }
          break;
        case $.ui.keyCode.LEFT:
          self.prev("right");
          event.preventDefault();
          break;
        case $.ui.keyCode.UP:
          self.prev("up");
          event.preventDefault();
          break;
        case $.ui.keyCode.RIGHT:
          self.next("left");
          event.preventDefault();
          break;
        case $.ui.keyCode.DOWN:
          self.next("down");
          event.preventDefault();
          break;
        }
      });
      if ($.fn.mousewheel) {
        $(document).mousewheel(function (event, delta) {
          if (!self.lightbox.dialog('isOpen')) {
            return;
          }
          event.preventDefault();
          if (self.lightbox.is(":animated")) {
            return;
          }
          if (delta < 0) {
            self.next("down");
          }
          if (delta > 0) {
            self.prev("up");
          }
        });
      }
    },

    _makeDialog: function () {
      return $('
').dialog({
        autoOpen: false,
        closeOnEscape: false,
        modal: false,
        show: null,
        hide: null,
        dialogClass: this.options.dialogClass,
        position: this.options.position,
        resizable: this.options.resizable,
        draggable: this.options.draggable,
        height: this.options.height,
        width: this.options.width,
        // TODO: Support overlay by implementing focus,dragstop,resizestop
        open: function (event, ui) {
          $('.ui-dialog-buttonpane button', $(this).parents('.ui-dialog'))
            .each(function (index, domElement) {
              $(domElement).addClass('button-' + index);
            });
        }
      });
    },

    _show: function (anchor) {
      var thumb = $(anchor),
        offset = thumb.offset(),
        dialog = this.lightbox.data('dialog'),
        start = {
          left: offset.left,
          top: offset.top,
          width: thumb.width(),
          height: thumb.height(),
          opacity: 0
        },
        stop = {
          left: $(dialog.uiDialog).css("left"),
          top: $(dialog.uiDialog).css("top"),
          width: $(dialog.uiDialog).width(),
          height: $(dialog.uiDialog).height(),
          opacity: 1
        };
      $(dialog.uiDialog).show('scale', { to: stop }, this.options.duration);
    },

    _buttons: {
      'Previous': function (eventObject) {
        $(this).dialog('option', '_lightbox').prev("right");
      },
      'Next': function (eventObject) {
        $(this).dialog('option', '_lightbox').next("left");
      }
    },

    destroy: function () {
      (this.overlay && this.overlay.destroy());
      this.element
        .unbind('.lightbox')
        .removeData('lightbox');

      this._anchors().removeData('lightbox');
    },

    open: function (anchor) {
      var viewer = (this.lightbox = this._makeDialog());

      this.overlay = this.options.modal ? new $.ui.lightbox.overlay(viewer.data('dialog')) : null;
      this.setCurrentAnchor(anchor);

      viewer.dialog('option', 'buttons', this._buttons)
        .unbind('dialogclose.lightbox')
        .bind('dialogclose.lightbox', this._dialogClose)
        .dialog('open');

      viewer.dialog('option', '_lightbox', this);
      this._preloadNeighbours();
    },

    close: function () {
      var viewer = this.lightbox;

      (this.overlay && this.overlay.destroy());

      $.ui.lightbox.overlay.resize();

      // TODO: these need to be destroyed with the widget.
      viewer.dialog('destroy').remove();
    },

    next: function (direction) {
      this._rotate(":gt(", ":first", direction || "up");
    },

    prev: function (direction) {
      this._rotate(":lt(", ":last", direction || "down");
    },

    _anchors: function () {
      // if deemed necessary, cache selection here
      return this.element.find(this.options.selector);
    },

    _setData: function (key, value) {
      if (key === 'cursor') {
        this.options[key] = value;
        this.spinner = new $.ui.lightbox.spinner(this);
        this._display(this._loadContent(value));
        this.spinner.destroy();
      }

      $.widget.prototype._setData.apply(this, arguments);
    },

    _display: function (content) {
      var anchor = this.getCurrentAnchor(),
        viewer = this.lightbox,
        size = this._calculateSize(content),
        $el = $(content).css(size).appendTo(viewer);

      viewer.dialog('option', 'title', $(anchor).attr('title') + this.options.titleSuffix);
      viewer.dialog('option', this._resize($el, size.width, size.height));
    },

    _jQueryToString: function (input) {
      return $('
').append($(input).clone().show()).remove().html();
    },

    _loadContent: function (anchor) {
      var self = this,
        anchorData = $(anchor).data('lightbox') || {};

      if (!this.options.reset && anchorData.content) {
        return anchorData.content;
      }
      else {
        anchorData = {
          content: 'BUG',
          type: this.options.type ? this.options.type : this._deriveType(anchor)
        };
      }

      switch (anchorData.type) {
      case "image":
        anchorData.content = this._jQueryToString($('').attr('src', anchor.href));
        break;
      case "flash":
      case "flashvideo":
      case "quicktime":
      case "realplayer":
      case "windowsmedia":
        $(anchor).media({
          width: this.options.width,
          height: this.options.height,
          src: anchor.href,
          autoplay: 1
        }, function (element, options) {
        }, function (element, data, options, playerName) {
          anchorData.content = this._jQueryToString(data);
          $(this).media('undo');
        });
        break;
      case "iframe":
        anchorData.content = this._element('iframe').attr('src', anchor.href).attr('frameborder', 0).attr('border', 0).html();
        break;
      case "html":
      case "dom":
        anchorData.content = this._jQueryToString(anchor.href);
        break;
      case "ajax":
      case "script":
        $.ajax({
          url: anchor.href,
          type: (parseInt(self.options.post, 10) === 1) ? "POST" : "GET",
          cache: true,
          async: false,
          data: self.options.parameters,
          dataType: (anchorData.type === "ajax") ? "html" : "script",
          success: function (data, textStatus) {
            anchorData.content = data;
          }
        });
        break;
      }

      if (this.options.cache) {
        $(anchor).data('lightbox', anchorData);
      }

      return anchorData.content;
    },

    _deriveType: function (anchor) {
      var reference = anchor.href;
      if (reference.toLowerCase().match(/\.(gif|jpg|jpeg|png)(\?[0123456789]+)?$/)) {
        return "image";
      }
      if (reference.toLowerCase().match(/\.(swf)(\?[0123456789]+)?$/)) {
        return "flash";
      }
      if (reference.toLowerCase().match(/\.(flv)(\?[0123456789]+)?$/)) {
        return "flashvideo";
      }
      if (reference.toLowerCase().match(/\.(aif|aiff|aac|au|bmp|gsm|mov|mid|midi|mpg|mpeg|m4a|m4v|mp4|psd|qt|qtif|qif|qti|snd|tif|tiff|wav|3g2|3gp|wbmp)(\?[0123456789]+)?$/)) {
        return "quicktime";
      }
      if (reference.toLowerCase().match(/\.(ra|ram|rm|rpm|rv|smi|smil)(\?[0123456789]+)?$/)) {
        return "realplayer";
      }
      if (reference.toLowerCase().match(/\.(asf|avi|wma|wmv)(\?[0123456789]+)?$/)) {
        return "windowsmedia";
      }
      return "ajax";
    },

    _preloadNeighbours: function () {
      var anchors = this._anchors(),
        index = anchors.index(this.getCurrentAnchor()),
        self = this;
      anchors.filter(this._neighbours(index, anchors.length)).each(function () {
        self._loadContent(this);
      });
    },

    _neighbours: function (index, length) {
      return ":eq(" + (index === 0 ? length - 1 : index - 1) + "), :eq(" + (index === length - 1 ? 0 : index + 1) + ")";
    },

    _position: function (img) {
    },

    _calculateSize: function (content) {
      var width, height;
      $.swap($(content).appendTo('
').appendTo(document.body)[0], {
        position: "absolute",
        visibility: "hidden",
        display: "block"
      }, function () {
        width = $(this).width();
        height = $(this).height();
        $(this).remove();
      });
      return { width: width, height: height };
    },

    _resize: function (elem, width, height) {
      var viewer = this.lightbox,
        dialog = viewer.data('dialog'),
        offset = 20,

        // difference
        deltaContentWidth = viewer.outerWidth() - viewer.width(),
        deltaContentHeight = viewer.outerHeight() - viewer.height(),

        dialogTitlebarHeight = dialog.uiDialogTitlebar.outerHeight(),

        // Window
        wWidth = $(window).width(),
        wHeight = $(window).height(),

        size = {width: width, height: height},

        // Desired width
        finalWidth = size.width + deltaContentWidth,
        finalHeight = size.height + deltaContentHeight + dialogTitlebarHeight,

        ratio = Math.min(
          Math.min(
            Math.min(wWidth - deltaContentWidth - offset, size.width) / size.width,
            Math.min(wHeight - deltaContentHeight - dialogTitlebarHeight - offset, size.height) / size.height, 1));

      $.extend(size, {
        width: Math.round(ratio * size.width),
        height: Math.round(ratio * size.height)
      });

      elem.css(size);

      if (ratio !== 1) {
        elem.attr('width', '').attr('height', '');
      }

      finalWidth = size.width + deltaContentWidth;
      finalHeight = size.height + deltaContentHeight + dialogTitlebarHeight;

      return { width: finalWidth, height: finalHeight };
    },

    setCurrentAnchor: function (anchor) {
      this._setData('cursor', anchor);
    },

    getCurrentAnchor: function () {
      return this._getData('cursor');
    },

    _rotate: function (selectorA, selectorB, direction) {
      var self = this,
        anchors = this._anchors(),
        current = this.getCurrentAnchor(),
        target = this.getCurrentAnchor(),
        viewer = this.lightbox,
        dialog = viewer.data('dialog').uiDialog,

        effectOut = {
          direction: direction
        },
        effectIn = {
          direction: { up: "down", down: "up", left: "right", right: "left" }[direction]
        };

      if (anchors.length === 1) {
        return;
      }

      target = anchors.filter(selectorA + anchors.index(current) + ")" + selectorB)[0];
      if (!target && this.options.loop && anchors.length > 1) {
        target = anchors.filter(selectorB)[0];
      }

      dialog.hide(this.options.rotateOut, effectOut, this.options.duration, function () {
        viewer
          .unbind('dialogclose.lightbox')
          .bind('dialogclose.lightbox', self._rotateClose)
          .dialog('close')
          .unbind('dialogclose.lightbox');
        self.setCurrentAnchor(target);
        self._preloadNeighbours();
        $(this).show(self.options.rotateIn, effectIn, self.options.duration, function () {
          viewer
            .dialog('open')
            .unbind('dialogclose.lightbox')
            .bind('dialogclose.lightbox', self._dialogClose);
        });
      });
    },

    // Swappable dialog event handlers.

    _rotateClose: function (event, ui) {
      var lightbox = $(this).dialog('option', '_lightbox'),
        dialog = $(lightbox.lightbox).data('dialog');

      $(this).empty();
      $(dialog.uiDialog).hide();
    },

    _dialogClose: function (event, ui) {
      var self = this,
        lightbox = $(this).dialog('option', '_lightbox'),
        dialog = $(lightbox.lightbox).data('dialog'),

        thumb = $(lightbox.getCurrentAnchor()),
        offset = thumb.offset(),
        stop = {
          left: offset.left,
          top: offset.top,
          width: thumb.width(),
          height: thumb.height(),
          opacity: 0
        };

      $(dialog.uiDialog).hide('scale', { to: stop }, lightbox.options.duration, function () {
        $(self).empty().dialog('option', '_lightbox').close();
      });
    },

    _element: function (type, clazz) {
      return $("<" + type + "/>").addClass(clazz).hide();
    }
  });

  $.extend($.ui.lightbox, {
    defaults: {
      loop: true,
      modal: true,
      overlay: {},
      cache: true,
      post: 0,
      dialogClass: 'ui-lightbox',
      closeOnEscape: true,
      resizable: false,
      draggable: false,
      selector: "a[href]:has(img[src])",
      titleSuffix: "",
      position: 'center',
      width: 'auto',
      height: 'auto',
      parameters: {},
      duration: 1000,
      rotateIn: 'drop',
      rotateOut: 'drop',
      show: '',
      hide: ''
    },
    uuid: 0,
    overlay: function (dialog) {
      this.$el = $.ui.lightbox.overlay.create(dialog);
    },
    spinner: function (lightbox) {
      this.$el = $.ui.lightbox.spinner.create(lightbox);
    }
  });

  $.extend($.ui.lightbox.overlay, $.ui.dialog.overlay, {});

  $.extend($.ui.lightbox.overlay.prototype, $.ui.dialog.overlay.prototype, {
    destroy: function () {
      $.ui.lightbox.overlay.destroy(this.$el);
    }
  });

  $.extend($.ui.lightbox.spinner, {
    instances: [],
    create: function (lightbox) {
      if (this.instances.length === 0) {
        var $el = $('

').appendTo(document.body)
          .addClass('ui-loading-indicator ui-corner-all').fadeIn("slow");

        this.instances.push($el);
        return $el;
      }
    },
    destroy: function ($el) {
      this.instances.splice($.inArray(this.instances, $el), 1);

      $el.remove();
    }
  });

  $.extend($.ui.lightbox.spinner.prototype, {
    destroy: function () {
      $.ui.lightbox.spinner.destroy(this.$el);
    }
  });

})(jQuery);
<commit_msg>jquery media lightboxes.<commit_after>/*globals document,window,$,jQuery,Image */
/*jslint white: true, browser: true, onevar: true, undef: true, eqeqeq: true, plusplus: true, bitwise: true, regexp: true, strict: true, newcap: true, immed: true, indent: 2 */

"use strict";

/*
 * jQuery UI Lightbox
 *
 * Copyright (c) 2009 Benjamin Doherty
 * Dual licensed under the MIT (MIT-LICENSE.txt)
 * and GPL (GPL-LICENSE.txt) licenses.
 *
 * Depends:
 *  ui.core.js
 *  ui.dialog.js
 *  jquery.mousewheel.js (optional)
 */
(function ($) {

  $.widget('ui.lightbox', {
    _init: function () {
      var self = this;

      // consider event delegation to make this more dynamic
      $(this.options.selector, this.element).click(function (event) {
        event.preventDefault();
        self.open(this);
      });
      $(document).keydown(function (event) {
        if (!self.lightbox || !self.lightbox.dialog('isOpen')) {
          return;
        }
        switch (event.keyCode) {
        case $.ui.keyCode.ESCAPE:
          if (self.options.closeOnEscape) {
            self.close();
          }
          break;
        case $.ui.keyCode.LEFT:
          self.prev("right");
          event.preventDefault();
          break;
        case $.ui.keyCode.UP:
          self.prev("up");
          event.preventDefault();
          break;
        case $.ui.keyCode.RIGHT:
          self.next("left");
          event.preventDefault();
          break;
        case $.ui.keyCode.DOWN:
          self.next("down");
          event.preventDefault();
          break;
        }
      });
      if ($.fn.mousewheel) {
        $(document).mousewheel(function (event, delta) {
          if (!self.lightbox.dialog('isOpen')) {
            return;
          }
          event.preventDefault();
          if (self.lightbox.is(":animated")) {
            return;
          }
          if (delta < 0) {
            self.next("down");
          }
          if (delta > 0) {
            self.prev("up");
          }
        });
      }
    },

    _makeDialog: function () {
      return $('
').dialog({
        autoOpen: false,
        closeOnEscape: false,
        modal: false,
        show: null,
        hide: null,
        dialogClass: this.options.dialogClass,
        position: this.options.position,
        resizable: this.options.resizable,
        draggable: this.options.draggable,
        height: this.options.height,
        width: this.options.width,
        // TODO: Support overlay by implementing focus,dragstop,resizestop
        open: function (event, ui) {
          $('.ui-dialog-buttonpane button', $(this).parents('.ui-dialog'))
            .each(function (index, domElement) {
              $(domElement).addClass('button-' + index);
            });
        }
      });
    },

    _show: function (anchor) {
      var thumb = $(anchor),
        offset = thumb.offset(),
        dialog = this.lightbox.data('dialog'),
        start = {
          left: offset.left,
          top: offset.top,
          width: thumb.width(),
          height: thumb.height(),
          opacity: 0
        },
        stop = {
          left: $(dialog.uiDialog).css("left"),
          top: $(dialog.uiDialog).css("top"),
          width: $(dialog.uiDialog).width(),
          height: $(dialog.uiDialog).height(),
          opacity: 1
        };
      $(dialog.uiDialog).show('scale', { to: stop }, this.options.duration);
    },

    _buttons: {
      'Previous': function (eventObject) {
        $(this).dialog('option', '_lightbox').prev("right");
      },
      'Next': function (eventObject) {
        $(this).dialog('option', '_lightbox').next("left");
      }
    },

    destroy: function () {
      (this.overlay && this.overlay.destroy());
      this.element
        .unbind('.lightbox')
        .removeData('lightbox');

      this._anchors().removeData('lightbox');
    },

    open: function (anchor) {
      var viewer = (this.lightbox = this._makeDialog());

      this.overlay = this.options.modal ? new $.ui.lightbox.overlay(viewer.data('dialog')) : null;
      this.setCurrentAnchor(anchor);

      viewer.dialog('option', 'buttons', this._buttons)
        .unbind('dialogclose.lightbox')
        .bind('dialogclose.lightbox', this._dialogClose)
        .dialog('open');

      viewer.dialog('option', '_lightbox', this);
      this._preloadNeighbours();
    },

    close: function () {
      var viewer = this.lightbox;

      (this.overlay && this.overlay.destroy());

      $.ui.lightbox.overlay.resize();

      // TODO: these need to be destroyed with the widget.
      viewer.dialog('destroy').remove();
    },

    next: function (direction) {
      this._rotate(":gt(", ":first", direction || "up");
    },

    prev: function (direction) {
      this._rotate(":lt(", ":last", direction || "down");
    },

    _anchors: function () {
      // if deemed necessary, cache selection here
      return this.element.find(this.options.selector);
    },

    _setData: function (key, value) {
      if (key === 'cursor') {
        this.options[key] = value;
        this.spinner = new $.ui.lightbox.spinner(this);
        this._display(this._loadContent(value));
        this.spinner.destroy();
      }

      $.widget.prototype._setData.apply(this, arguments);
    },

    _display: function (content) {
      var anchor = this.getCurrentAnchor(),
        viewer = this.lightbox,
        size = this._calculateSize(content),
        $el = $(content).css(size).appendTo(viewer);

      viewer.dialog('option', 'title', $(anchor).attr('title') + this.options.titleSuffix);
      viewer.dialog('option', this._resize($el, size.width, size.height));
    },

    _jQueryToString: function (input) {
      return $('
').append($(input).clone().show()).remove().html();
    },

    _loadContent: function (anchor) {
      var self = this,
        anchorData = $(anchor).data('lightbox') || {};

      if (!this.options.reset && anchorData.content) {
        return anchorData.content;
      }
      else {
        anchorData = {
          content: 'BUG',
          type: this.options.type ? this.options.type : this._deriveType(anchor)
        };
      }

      switch (anchorData.type) {
      case "image":
        anchorData.content = this._jQueryToString($('').attr('src', anchor.href));
        break;
      case "flash":
      case "flashvideo":
      case "quicktime":
      case "realplayer":
      case "windowsmedia":
        $(anchor).media({
          width: this.options.width,
          height: this.options.height,
          src: anchor.href,
          autoplay: 1
        }, function (element, options) {
        }, function (element, data, options, playerName) {
          anchorData.content = self._jQueryToString(data);
          $(data).media('undo');
        });
        break;
      case "iframe":
        anchorData.content = this._element('iframe').attr('src', anchor.href).attr('frameborder', 0).attr('border', 0).html();
        break;
      case "html":
      case "dom":
        anchorData.content = this._jQueryToString(anchor.href);
        break;
      case "ajax":
      case "script":
        $.ajax({
          url: anchor.href,
          type: (parseInt(self.options.post, 10) === 1) ? "POST" : "GET",
          cache: true,
          async: false,
          data: self.options.parameters,
          dataType: (anchorData.type === "ajax") ? "html" : "script",
          success: function (data, textStatus) {
            anchorData.content = data;
          }
        });
        break;
      }

      if (this.options.cache) {
        $(anchor).data('lightbox', anchorData);
      }

      return anchorData.content;
    },

    _deriveType: function (anchor) {
      var reference = anchor.href;
      if (reference.toLowerCase().match(/\.(gif|jpg|jpeg|png)(\?[0123456789]+)?$/)) {
        return "image";
      }
      if (reference.toLowerCase().match(/\.(swf)(\?[0123456789]+)?$/)) {
        return "flash";
      }
      if (reference.toLowerCase().match(/\.(flv)(\?[0123456789]+)?$/)) {
        return "flashvideo";
      }
      if (reference.toLowerCase().match(/\.(aif|aiff|aac|au|bmp|gsm|mov|mid|midi|mpg|mpeg|m4a|m4v|mp4|psd|qt|qtif|qif|qti|snd|tif|tiff|wav|3g2|3gp|wbmp)(\?[0123456789]+)?$/)) {
        return "quicktime";
      }
      if (reference.toLowerCase().match(/\.(ra|ram|rm|rpm|rv|smi|smil)(\?[0123456789]+)?$/)) {
        return "realplayer";
      }
      if (reference.toLowerCase().match(/\.(asf|avi|wma|wmv)(\?[0123456789]+)?$/)) {
        return "windowsmedia";
      }
      return "ajax";
    },

    _preloadNeighbours: function () {
      var anchors = this._anchors(),
        index = anchors.index(this.getCurrentAnchor()),
        self = this;
      anchors.filter(this._neighbours(index, anchors.length)).each(function () {
        self._loadContent(this);
      });
    },

    _neighbours: function (index, length) {
      return ":eq(" + (index === 0 ? length - 1 : index - 1) + "), :eq(" + (index === length - 1 ? 0 : index + 1) + ")";
    },

    _position: function (img) {
    },

    _calculateSize: function (content) {
      var width, height;
      $.swap($(content).appendTo('
').appendTo(document.body)[0], {
        position: "absolute",
        visibility: "hidden",
        display: "block"
      }, function () {
        width = $(this).width();
        height = $(this).height();
        $(this).remove();
      });
      return { width: width, height: height };
    },

    _resize: function (elem, width, height) {
      var viewer = this.lightbox,
        dialog = viewer.data('dialog'),
        offset = 20,

        // difference
        deltaContentWidth = viewer.outerWidth() - viewer.width(),
        deltaContentHeight = viewer.outerHeight() - viewer.height(),

        dialogTitlebarHeight = dialog.uiDialogTitlebar.outerHeight(),

        // Window
        wWidth = $(window).width(),
        wHeight = $(window).height(),

        size = {width: width, height: height},

        // Desired width
        finalWidth = size.width + deltaContentWidth,
        finalHeight = size.height + deltaContentHeight + dialogTitlebarHeight,

        ratio = Math.min(
          Math.min(
            Math.min(wWidth - deltaContentWidth - offset, size.width) / size.width,
            Math.min(wHeight - deltaContentHeight - dialogTitlebarHeight - offset, size.height) / size.height, 1));

      $.extend(size, {
        width: Math.round(ratio * size.width),
        height: Math.round(ratio * size.height)
      });

      elem.css(size);

      if (ratio !== 1) {
        elem.attr('width', '').attr('height', '');
      }

      finalWidth = size.width + deltaContentWidth;
      finalHeight = size.height + deltaContentHeight + dialogTitlebarHeight;

      return { width: finalWidth, height: finalHeight };
    },

    setCurrentAnchor: function (anchor) {
      this._setData('cursor', anchor);
    },

    getCurrentAnchor: function () {
      return this._getData('cursor');
    },

    _rotate: function (selectorA, selectorB, direction) {
      var self = this,
        anchors = this._anchors(),
        current = this.getCurrentAnchor(),
        target = this.getCurrentAnchor(),
        viewer = this.lightbox,
        dialog = viewer.data('dialog').uiDialog,

        effectOut = {
          direction: direction
        },
        effectIn = {
          direction: { up: "down", down: "up", left: "right", right: "left" }[direction]
        };

      if (anchors.length === 1) {
        return;
      }

      target = anchors.filter(selectorA + anchors.index(current) + ")" + selectorB)[0];
      if (!target && this.options.loop && anchors.length > 1) {
        target = anchors.filter(selectorB)[0];
      }

      dialog.hide(this.options.rotateOut, effectOut, this.options.duration, function () {
        viewer
          .unbind('dialogclose.lightbox')
          .bind('dialogclose.lightbox', self._rotateClose)
          .dialog('close')
          .unbind('dialogclose.lightbox');
        self.setCurrentAnchor(target);
        self._preloadNeighbours();
        $(this).show(self.options.rotateIn, effectIn, self.options.duration, function () {
          viewer
            .dialog('open')
            .unbind('dialogclose.lightbox')
            .bind('dialogclose.lightbox', self._dialogClose);
        });
      });
    },

    // Swappable dialog event handlers.

    _rotateClose: function (event, ui) {
      var lightbox = $(this).dialog('option', '_lightbox'),
        dialog = $(lightbox.lightbox).data('dialog');

      $(this).empty();
      $(dialog.uiDialog).hide();
    },

    _dialogClose: function (event, ui) {
      var self = this,
        lightbox = $(this).dialog('option', '_lightbox'),
        dialog = $(lightbox.lightbox).data('dialog'),

        thumb = $(lightbox.getCurrentAnchor()),
        offset = thumb.offset(),
        stop = {
          left: offset.left,
          top: offset.top,
          width: thumb.width(),
          height: thumb.height(),
          opacity: 0
        };

      $(dialog.uiDialog).hide('scale', { to: stop }, lightbox.options.duration, function () {
        $(self).empty().dialog('option', '_lightbox').close();
      });
    },

    _element: function (type, clazz) {
      return $("<" + type + "/>").addClass(clazz).hide();
    }
  });

  $.extend($.ui.lightbox, {
    defaults: {
      loop: true,
      modal: true,
      overlay: {},
      cache: true,
      post: 0,
      dialogClass: 'ui-lightbox',
      closeOnEscape: true,
      resizable: false,
      draggable: false,
      selector: "a[href]:has(img[src])",
      titleSuffix: "",
      position: 'center',
      width: 'auto',
      height: 'auto',
      parameters: {},
      duration: 1000,
      rotateIn: 'drop',
      rotateOut: 'drop',
      show: '',
      hide: ''
    },
    uuid: 0,
    overlay: function (dialog) {
      this.$el = $.ui.lightbox.overlay.create(dialog);
    },
    spinner: function (lightbox) {
      this.$el = $.ui.lightbox.spinner.create(lightbox);
    }
  });

  $.extend($.ui.lightbox.overlay, $.ui.dialog.overlay, {});

  $.extend($.ui.lightbox.overlay.prototype, $.ui.dialog.overlay.prototype, {
    destroy: function () {
      $.ui.lightbox.overlay.destroy(this.$el);
    }
  });

  $.extend($.ui.lightbox.spinner, {
    instances: [],
    create: function (lightbox) {
      if (this.instances.length === 0) {
        var $el = $('

').appendTo(document.body)
          .addClass('ui-loading-indicator ui-corner-all').fadeIn("slow");

        this.instances.push($el);
        return $el;
      }
    },
    destroy: function ($el) {
      this.instances.splice($.inArray(this.instances, $el), 1);

      $el.remove();
    }
  });

  $.extend($.ui.lightbox.spinner.prototype, {
    destroy: function () {
      $.ui.lightbox.spinner.destroy(this.$el);
    }
  });

})(jQuery);
<|endoftext|>


<commit_before>var authormodel = require('authormodel');

var Backbone = authormodel.Backbone;
var _ = authormodel._;

var $ = require('jquery');

var ActionContext = authormodel.ActionContext;
var FlagCommon = require('authormodel/uniteditors/src/FlagCommon');

var rangy = require('rangy');
rangy.init();

/* Exceptions */
function NoOptionsGivenException() {
  this.message = 'Text Paragraph constructor expects options';
  this.toString = function() {
    return this.message;
  };
}

function IllegalArgumentException(property, message) {
  this.message = "Property '" + property + "' " + message;
  this.toString = function() {
    return this.message;
  };
}

var TextParagraphEditor = Backbone.View.extend({

  tagName: 'div', // should be overridden in case the p is wrapped in something else

  mixins: {},

  events: {
    'keypress': 'onKeypress',
    'authoraction': 'onAuthorAction',
    'focus': 'onFocus',
    'keyup': 'onKeyup',
    'blur': 'saveAuto',
    'input': 'saveAuto'
  },

  initialize: function(options) {
    // the above was added, despite the below, because exceptions don't produce a navigable stack in console
    // also mocha in browser handles exceptions really badly unless they are expected for, just bailing out so you don't see which test
    if (typeof options == 'undefined') throw new NoOptionsGivenException();
    if (!this.model) throw new IllegalArgumentException('model', 'required');
    if (!this.model.get('type')) throw new IllegalArgumentException('type', 'expected for authoring unit');
    this.saveOnChange = options.saveOnChange || false;
    new FlagCommon(this);
  },

  render: function() {
    if (!this.$p) {
      var p = this.$el.find('> p');
      if (!p.length) {
        p = $('<p/>').appendTo(this.$el);
      }
      this.$p = p;
    }
    this.$p.html(this.model.get('content') || '');
    this.$p[0].contentEditable = true;
    this.mixins.FlagCommon.render();
    return this;
  },

  save: function() {
    if (!this.$p) {
      throw new Error('Save can not be used prior to first render');
    }
    var encoded = this.$p.html();
    this.model.set('content', encoded);
  },

  saveAuto: function() {
    if (this.saveOnChange) {
      this.save();
    }
  },

  focus: function() {
    if (!this.$p) {
      throw new Error('Focus can not be used prior to render');
    }
    this.$p.focus();
  },

  onFocus: function(event) {
    // TODO add spec then remove debug
    console.debug('onFocus no-op', event, arguments[1]);
    //this.selectAll();
  },

  onKeyup: function(event) {
    // TODO do we need this emulation, or can 'focus' happen? Works pretty well for now because with mouse it is easy to select manually.
    if (event.keyCode == 9) {
      return; // This happens also if you alt-tab to the window and had the cursor in the text, not sure we'd want that
      console.debug('keyup tab emulates focus by keyboard', event.keyCode, event);
      //this.selectAll();
      // messing with rangy, not sure what would make a difference
      window.setTimeout(this.selectAll.bind(this), 100);
    }
  },

  isEmpty: function() {
    return this.$el.text().length === 0;
  },

  onKeypress: function(event) {
    if (event.keyCode == 13) {
      event.stopPropagation();
      event.preventDefault();

      var actionContext = new ActionContext();
      actionContext.setUnitEditor(this);
      actionContext.setUnit(this.model);
      if (this.hasSelection()) {
        actionContext.setSelection(this.getSelection());
      }
      this.trigger('authorintent', actionContext);
    }
  },

  onAuthorAction: function (actionContext) {
    console.log('onAuthorAction', actionContext);
  },

  hasSelection: function () {
    var selection = this.getSelection();
    if (!selection){
      return false;
    } else if(selection.isCollapsed){
      // If selection starts and ends in the same place, i.e. cursor
      return false;
    } else {
      return true;
    }
  },

  getSelection: function () {
    if (!this.$p) {
      throw new Error('Not rendered, selection is unavailable');
    }
    if (!this.$p[0]) {
      throw new Error('Missing element, selection is unavaialble');
    }
    if (rangy.initialized && rangy.supported) {
      var selection = rangy.getSelection();
      var el = this.$p[0];

      if (typeof selection.anchorNode !== 'undefined') {
        var tempNode = selection.anchorNode;
        while (tempNode) {
          //console.log('tempNode', tempNode);
          if (tempNode === el) {
            //console.log('they are the same!!', selection, el);
            return selection;
          } else {
            tempNode = tempNode.parentElement;
          }
        }
      }

      return undefined;
    } else {
      console.log('Rangy not supported');
      return undefined;
    }
  },

  selectAll: function() {
    if (!this.$p || !this.$p[0].firstChild) {
      throw new Error('Invalid state to make selection');
    }
    // TODO spec, init rangy?
    if (rangy.initialized && rangy.supported) {
      rangy.init();
      var range = rangy.createRange();
      range.setStartAndEnd(this.$p[0].firstChild, 0, this.model.get('content').length);
      console.debug('selectAll range', range);
      return range;
    } else {
      console.log('Rangy not supported');
      return undefined;
    }
  }

});

// NPM and plain javascript tag compatibility
if (typeof module != 'undefined') {
  module.exports = TextParagraphEditor;
} else {
  window.TextParagraphEditor = TextParagraphEditor;
}
<commit_msg>Enables save on change by default. The other option, explicit save, requires a lot more attention.<commit_after>var authormodel = require('authormodel');

var Backbone = authormodel.Backbone;
var _ = authormodel._;

var $ = require('jquery');

var ActionContext = authormodel.ActionContext;
var FlagCommon = require('authormodel/uniteditors/src/FlagCommon');

var rangy = require('rangy');
rangy.init();

/* Exceptions */
function NoOptionsGivenException() {
  this.message = 'Text Paragraph constructor expects options';
  this.toString = function() {
    return this.message;
  };
}

function IllegalArgumentException(property, message) {
  this.message = "Property '" + property + "' " + message;
  this.toString = function() {
    return this.message;
  };
}

var TextParagraphEditor = Backbone.View.extend({

  tagName: 'div', // should be overridden in case the p is wrapped in something else

  mixins: {},

  saveOnChange: true,

  events: {
    'keypress': 'onKeypress',
    'authoraction': 'onAuthorAction',
    'focus': 'onFocus',
    'keyup': 'onKeyup',
    'blur': 'saveAuto',
    'input': 'saveAuto'
  },

  initialize: function(options) {
    // the above was added, despite the below, because exceptions don't produce a navigable stack in console
    // also mocha in browser handles exceptions really badly unless they are expected for, just bailing out so you don't see which test
    if (typeof options == 'undefined') throw new NoOptionsGivenException();
    if (!this.model) throw new IllegalArgumentException('model', 'required');
    if (!this.model.get('type')) throw new IllegalArgumentException('type', 'expected for authoring unit');
    if (typeof options.saveOnChange !== 'undefined') {
      this.saveOnChange = options.saveOnChange;
    }
    new FlagCommon(this);
  },

  render: function() {
    if (!this.$p) {
      var p = this.$el.find('> p');
      if (!p.length) {
        p = $('<p/>').appendTo(this.$el);
      }
      this.$p = p;
    }
    this.$p.html(this.model.get('content') || '');
    this.$p[0].contentEditable = true;
    this.mixins.FlagCommon.render();
    return this;
  },

  save: function() {
    if (!this.$p) {
      throw new Error('Save can not be used prior to first render');
    }
    var encoded = this.$p.html();
    this.model.set('content', encoded);
  },

  saveAuto: function() {
    if (this.saveOnChange) {
      this.save();
    }
  },

  focus: function() {
    if (!this.$p) {
      throw new Error('Focus can not be used prior to render');
    }
    this.$p.focus();
  },

  onFocus: function(event) {
    // TODO add spec then remove debug
    console.debug('onFocus no-op', event, arguments[1]);
    //this.selectAll();
  },

  onKeyup: function(event) {
    // TODO do we need this emulation, or can 'focus' happen? Works pretty well for now because with mouse it is easy to select manually.
    if (event.keyCode == 9) {
      return; // This happens also if you alt-tab to the window and had the cursor in the text, not sure we'd want that
      console.debug('keyup tab emulates focus by keyboard', event.keyCode, event);
      //this.selectAll();
      // messing with rangy, not sure what would make a difference
      window.setTimeout(this.selectAll.bind(this), 100);
    }
  },

  isEmpty: function() {
    return this.$el.text().length === 0;
  },

  onKeypress: function(event) {
    if (event.keyCode == 13) {
      event.stopPropagation();
      event.preventDefault();

      var actionContext = new ActionContext();
      actionContext.setUnitEditor(this);
      actionContext.setUnit(this.model);
      if (this.hasSelection()) {
        actionContext.setSelection(this.getSelection());
      }
      this.trigger('authorintent', actionContext);
    }
  },

  onAuthorAction: function (actionContext) {
    console.log('onAuthorAction', actionContext);
  },

  hasSelection: function () {
    var selection = this.getSelection();
    if (!selection){
      return false;
    } else if(selection.isCollapsed){
      // If selection starts and ends in the same place, i.e. cursor
      return false;
    } else {
      return true;
    }
  },

  getSelection: function () {
    if (!this.$p) {
      throw new Error('Not rendered, selection is unavailable');
    }
    if (!this.$p[0]) {
      throw new Error('Missing element, selection is unavaialble');
    }
    if (rangy.initialized && rangy.supported) {
      var selection = rangy.getSelection();
      var el = this.$p[0];

      if (typeof selection.anchorNode !== 'undefined') {
        var tempNode = selection.anchorNode;
        while (tempNode) {
          //console.log('tempNode', tempNode);
          if (tempNode === el) {
            //console.log('they are the same!!', selection, el);
            return selection;
          } else {
            tempNode = tempNode.parentElement;
          }
        }
      }

      return undefined;
    } else {
      console.log('Rangy not supported');
      return undefined;
    }
  },

  selectAll: function() {
    if (!this.$p || !this.$p[0].firstChild) {
      throw new Error('Invalid state to make selection');
    }
    // TODO spec, init rangy?
    if (rangy.initialized && rangy.supported) {
      rangy.init();
      var range = rangy.createRange();
      range.setStartAndEnd(this.$p[0].firstChild, 0, this.model.get('content').length);
      console.debug('selectAll range', range);
      return range;
    } else {
      console.log('Rangy not supported');
      return undefined;
    }
  }

});

// NPM and plain javascript tag compatibility
if (typeof module != 'undefined') {
  module.exports = TextParagraphEditor;
} else {
  window.TextParagraphEditor = TextParagraphEditor;
}
<|endoftext|>


<commit_before>// load('can/util/make.js')

/**
 * How this all works:
 *
 * There are 4 scenarios to account for with canjs library loading:
 * development, building with env.js, using the standalone canjs scripts,
 * and using a fully built production.js file.
 *
 * 1) development mode - the libaries will be loaded by their corresponding util plugin
 * 		(the _skip option is ignored in dev mode)
 * 2) while building with env.js - skipCallbacks is set in steal and _skip option causes env.js
 * 		not load the libary files (because isBuilding is set in open.js).  All this is to avoid loading
 * 		the libraries in env.js, because env throws errors while loading them.  skipCallbacks allows no
 * 		steal callbacks to be called, so the build finishes without error.  This is mainly for use in this
 * 		make.js script. The libs other than jQuery can't be used yet in a full production.js build because
 * 		of this env weakness.
 * 3) using the standalone canjs scripts - these scripts are build with pluginify, so they don't include any
 * 		reference to steal.  They require the user to load the actual libarary file first, then the corresponding
 * 		canjs script.
 * 4) using a fully built production.js file - This is only really relevant for jQuery until the env build
 * 		problem is fixed. The libs are built into the production.js file.
 *
 */

load("steal/rhino/rhino.js");
steal('steal/build/pluginify', function() {

	steal.File("can/dist").mkdirs();
	steal.build.pluginify("can/util/make/jquery.js",{
		out : "can/dist/can.jquery-edge.js",
		global : "can = {}",
		onefunc : true,
		compress: true,
		skipCallbacks: true,
		exclude : "can/util/jquery/jquery.1.7.1.js"
	});

	steal.build.pluginify("can/util/make/mootools.js",{
		out : "can/dist/can.mootools-edge.js",
		global : "can = {}",
		onefunc : true,
		compress: true,
		skipCallbacks: true
	});

	steal.build.pluginify("can/util/make/zepto.js",{
		out : "can/dist/can.zepto-edge.js",
		global : "can = {}",
		onefunc : true,
		compress: true,
		skipCallbacks: true
	});

	steal.build.pluginify("can/util/make/dojo.js",{
		out : "can/dist/can.dojo-edge.js",
		global : "can = {}",
		onefunc : true,
		compress: true,
		wrapInner: ['\ndefine("can/dojo", ["dojo/query", "dojo/NodeList-dom", "dojo/NodeList-traverse"], function(){\n', '\nreturn can;\n});\n'],
		skipCallbacks: true
	});

	steal.build.pluginify("can/util/make/yui.js",{
		out : "can/dist/can.yui-edge.js",
		global : "can = {}",
		onefunc : true,
		compress: true,
		skipCallbacks: true
	});

	steal.build.pluginify("can/util/make/jquery.js",{
		out : "can/dist/can.jquery-dev-edge.js",
		global : "can = {}",
		onefunc : true,
		compress: false,
		skipCallbacks: true,
		exclude : "can/util/jquery/jquery.1.7.1.js"
	});

	steal.build.pluginify("can/util/make/mootools.js",{
		out : "can/dist/can.mootools-dev-edge.js",
		global : "can = {}",
		onefunc : true,
		compress: false,
		skipCallbacks: true
	});

	steal.build.pluginify("can/util/make/zepto.js",{
		out : "can/dist/can.zepto-dev-edge.js",
		global : "can = {}",
		onefunc : true,
		compress: false,
		skipCallbacks: true
	});

	steal.build.pluginify("can/util/make/dojo.js",{
		out : "can/dist/can.dojo-dev-edge.js",
		global : "can = {}",
		onefunc : true,
		compress: false,
		wrapInner: ['\ndefine("can/dojo", ["dojo/query", "dojo/NodeList-dom", "dojo/NodeList-traverse"], function(){\n', '\nreturn can;\n});\n'],
		skipCallbacks: true
	});

	steal.build.pluginify("can/util/make/yui.js",{
		out : "can/dist/can.yui-dev-edge.js",
		global : "can = {}",
		onefunc : true,
		compress: false,
		skipCallbacks: true
	});

	steal.build.pluginify("can/construct/proxy/proxy.js", {
		out: "can/dist/can.construct.proxy.js",
		global: "can = {}",
		onefunc: true,
		compress: true,
		skipCallbacks: true,
		standAlone: true
	});

	steal.build.pluginify("can/construct/super/super.js", {
		out: "can/dist/can.construct.super.js",
		global: "can = {}",
		onefunc: true,
		compress: true,
		skipCallbacks: true,
		standAlone: true
	});

	steal.build.pluginify("can/control/plugin/plugin.js", {
		out: "can/dist/can.control.plugin.js",
		global: "can = {}",
		onefunc: true,
		compress: true,
		skipCallbacks: true,
		standAlone: true
	});

	steal.build.pluginify("can/observe/attributes/attributes.js", {
		out: "can/dist/can.observe.attributes.js",
		global: "can = {}",
		onefunc: true,
		compress: true,
		skipCallbacks: true,
		standAlone: true
	});

	steal.build.pluginify("can/observe/delegate/delegate.js", {
		out: "can/dist/can.observe.delegate.js",
		global: "can = {}",
		onefunc: true,
		compress: true,
		skipCallbacks: true,
		standAlone: true
	});

	steal.build.pluginify("can/observe/setter/setter.js", {
		out: "can/dist/can.observe.setter.js",
		global: "can = {}",
		onefunc: true,
		compress: true,
		skipCallbacks: true,
		standAlone: true
	});

	steal.build.pluginify("can/observe/backup/backup.js", {
		out: "can/dist/can.observe.backup.js",
		global: "can = {}",
		onefunc: true,
		compress: true,
		skipCallbacks: true,
		standAlone: true
	});

	steal.build.pluginify("can/observe/validations/validations.js", {
		out: "can/dist/can.observe.validations.js",
		global: "can = {}",
		onefunc: true,
		compress: true,
		skipCallbacks: true,
		standAlone: true
	});

});<commit_msg>Updating make script. Adding minified versions to compliment unminified plugins.<commit_after>// load('can/util/make.js')

/**
 * How this all works:
 *
 * There are 4 scenarios to account for with canjs library loading:
 * development, building with env.js, using the standalone canjs scripts,
 * and using a fully built production.js file.
 *
 * 1) development mode - the libaries will be loaded by their corresponding util plugin
 * 		(the _skip option is ignored in dev mode)
 * 2) while building with env.js - skipCallbacks is set in steal and _skip option causes env.js
 * 		not load the libary files (because isBuilding is set in open.js).  All this is to avoid loading
 * 		the libraries in env.js, because env throws errors while loading them.  skipCallbacks allows no
 * 		steal callbacks to be called, so the build finishes without error.  This is mainly for use in this
 * 		make.js script. The libs other than jQuery can't be used yet in a full production.js build because
 * 		of this env weakness.
 * 3) using the standalone canjs scripts - these scripts are build with pluginify, so they don't include any
 * 		reference to steal.  They require the user to load the actual libarary file first, then the corresponding
 * 		canjs script.
 * 4) using a fully built production.js file - This is only really relevant for jQuery until the env build
 * 		problem is fixed. The libs are built into the production.js file.
 *
 */

load("steal/rhino/rhino.js");
steal('steal/build/pluginify', function() {

	steal.File("can/dist").mkdirs();
	steal.build.pluginify("can/util/make/jquery.js",{
		out : "can/dist/can.jquery-edge.js",
		global : "can = {}",
		onefunc : true,
		compress: true,
		skipCallbacks: true,
		exclude : "can/util/jquery/jquery.1.7.1.js"
	});

	steal.build.pluginify("can/util/make/mootools.js",{
		out : "can/dist/can.mootools-edge.js",
		global : "can = {}",
		onefunc : true,
		compress: true,
		skipCallbacks: true
	});

	steal.build.pluginify("can/util/make/zepto.js",{
		out : "can/dist/can.zepto-edge.js",
		global : "can = {}",
		onefunc : true,
		compress: true,
		skipCallbacks: true
	});

	steal.build.pluginify("can/util/make/dojo.js",{
		out : "can/dist/can.dojo-edge.js",
		global : "can = {}",
		onefunc : true,
		compress: true,
		wrapInner: ['\ndefine("can/dojo", ["dojo/query", "dojo/NodeList-dom", "dojo/NodeList-traverse"], function(){\n', '\nreturn can;\n});\n'],
		skipCallbacks: true
	});

	steal.build.pluginify("can/util/make/yui.js",{
		out : "can/dist/can.yui-edge.js",
		global : "can = {}",
		onefunc : true,
		compress: true,
		skipCallbacks: true
	});

	steal.build.pluginify("can/util/make/jquery.js",{
		out : "can/dist/can.jquery-dev-edge.js",
		global : "can = {}",
		onefunc : true,
		compress: false,
		skipCallbacks: true,
		exclude : "can/util/jquery/jquery.1.7.1.js"
	});

	steal.build.pluginify("can/util/make/mootools.js",{
		out : "can/dist/can.mootools-dev-edge.js",
		global : "can = {}",
		onefunc : true,
		compress: false,
		skipCallbacks: true
	});

	steal.build.pluginify("can/util/make/zepto.js",{
		out : "can/dist/can.zepto-dev-edge.js",
		global : "can = {}",
		onefunc : true,
		compress: false,
		skipCallbacks: true
	});

	steal.build.pluginify("can/util/make/dojo.js",{
		out : "can/dist/can.dojo-dev-edge.js",
		global : "can = {}",
		onefunc : true,
		compress: false,
		wrapInner: ['\ndefine("can/dojo", ["dojo/query", "dojo/NodeList-dom", "dojo/NodeList-traverse"], function(){\n', '\nreturn can;\n});\n'],
		skipCallbacks: true
	});

	steal.build.pluginify("can/util/make/yui.js",{
		out : "can/dist/can.yui-dev-edge.js",
		global : "can = {}",
		onefunc : true,
		compress: false,
		skipCallbacks: true
	});

	steal.build.pluginify("can/construct/proxy/proxy.js", {
		out: "can/dist/can.construct.proxy.js",
		global: "can = {}",
		onefunc: true,
		compress: false,
		skipCallbacks: true,
		standAlone: true
	});

	steal.build.pluginify("can/construct/super/super.js", {
		out: "can/dist/can.construct.super.js",
		global: "can = {}",
		onefunc: true,
		compress: false,
		skipCallbacks: true,
		standAlone: true
	});

	steal.build.pluginify("can/control/plugin/plugin.js", {
		out: "can/dist/can.control.plugin.js",
		global: "can = {}",
		onefunc: true,
		compress: false,
		skipCallbacks: true,
		standAlone: true
	});

	steal.build.pluginify("can/observe/attributes/attributes.js", {
		out: "can/dist/can.observe.attributes.js",
		global: "can = {}",
		onefunc: true,
		compress: false,
		skipCallbacks: true,
		standAlone: true
	});

	steal.build.pluginify("can/observe/delegate/delegate.js", {
		out: "can/dist/can.observe.delegate.js",
		global: "can = {}",
		onefunc: true,
		compress: false,
		skipCallbacks: true,
		standAlone: true
	});

	steal.build.pluginify("can/observe/setter/setter.js", {
		out: "can/dist/can.observe.setter.js",
		global: "can = {}",
		onefunc: true,
		compress: false,
		skipCallbacks: true,
		standAlone: true
	});

	steal.build.pluginify("can/observe/backup/backup.js", {
		out: "can/dist/can.observe.backup.js",
		global: "can = {}",
		onefunc: true,
		compress: false,
		skipCallbacks: true,
		standAlone: true
	});

	steal.build.pluginify("can/observe/validations/validations.js", {
		out: "can/dist/can.observe.validations.js",
		global: "can = {}",
		onefunc: true,
		compress: false,
		skipCallbacks: true,
		standAlone: true
	});

	steal.build.pluginify("can/construct/proxy/proxy.js", {
		out: "can/dist/can.construct.proxy.min.js",
		global: "can = {}",
		onefunc: true,
		compress: true,
		skipCallbacks: true,
		standAlone: true
	});

	steal.build.pluginify("can/construct/super/super.js", {
		out: "can/dist/can.construct.super.min.js",
		global: "can = {}",
		onefunc: true,
		compress: true,
		skipCallbacks: true,
		standAlone: true
	});

	steal.build.pluginify("can/control/plugin/plugin.js", {
		out: "can/dist/can.control.plugin.min.js",
		global: "can = {}",
		onefunc: true,
		compress: true,
		skipCallbacks: true,
		standAlone: true
	});

	steal.build.pluginify("can/observe/attributes/attributes.js", {
		out: "can/dist/can.observe.attributes.min.js",
		global: "can = {}",
		onefunc: true,
		compress: true,
		skipCallbacks: true,
		standAlone: true
	});

	steal.build.pluginify("can/observe/delegate/delegate.js", {
		out: "can/dist/can.observe.delegate.min.js",
		global: "can = {}",
		onefunc: true,
		compress: true,
		skipCallbacks: true,
		standAlone: true
	});

	steal.build.pluginify("can/observe/setter/setter.js", {
		out: "can/dist/can.observe.setter.min.js",
		global: "can = {}",
		onefunc: true,
		compress: true,
		skipCallbacks: true,
		standAlone: true
	});

	steal.build.pluginify("can/observe/backup/backup.js", {
		out: "can/dist/can.observe.backup.min.js",
		global: "can = {}",
		onefunc: true,
		compress: true,
		skipCallbacks: true,
		standAlone: true
	});

	steal.build.pluginify("can/observe/validations/validations.js", {
		out: "can/dist/can.observe.validations.min.js",
		global: "can = {}",
		onefunc: true,
		compress: true,
		skipCallbacks: true,
		standAlone: true
	});

});<|endoftext|>


<commit_before>/*!
 * Copyright 2015 Digital Services, University of Cambridge Licensed
 * under the Educational Community 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://opensource.org/licenses/ECL-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.
 */

define(['gh.core'], function(gh) {

    //////////////////////
    //  AUTHENTICATION  //
    //////////////////////

    /**
     * Log in using the local authentication strategy
     *
     * @param  {Event}      ev    The jQuery event
     * @return {Boolean}          Return false to avoid the default form behaviour
     * @private
     */
    var doLogin = function(ev) {
        // Determine whether or not the global admin is in use
        var isGlobalAdminUI = $('body').hasClass('gh-global-admin');

        // Log in to the system if the form is valid and local authentication is enabled.
        // If Shibboleth is required the native form behaviour will be used and no extra
        // handling is required
        if (!ev.isDefaultPrevented() && (isGlobalAdminUI || (gh.config.enableLocalAuth && !gh.config.enableShibbolethAuth))) {
            // Collect and submit the form data
            var formValues = _.object(_.map($(this).serializeArray(), _.values));
            gh.api.authenticationAPI.login(formValues.username, formValues.password, function(err) {
                if (err) {
                    return gh.utils.notification('Could not sign you in', 'Please check that you are entering a correct username & password', 'error');
                }
                window.location.reload();
            });
        }

        // Avoid default form submit behaviour
        return false;
    };

    /**
     * Log the current user out
     *
     * @param  {Event}    ev    The jQuery event
     * @private
     */
    var doLogout = function(ev) {
        // Prevent the form from being submitted
        ev.preventDefault();
        // Sign out the user
        gh.api.authenticationAPI.logout(function(err) {
            if (err) {
                return gh.utils.notification('Logout failed', 'Logging out of the application failed', 'error');
            }
            window.location = '/';
        });
    };

    /////////////
    // BINDING //
    /////////////

    /**
     * Add handlers to various elements
     *
     * @private
     */
    var addBinding = function() {
        // Sign out the user when the form is submitted
        $('body').on('submit', '#gh-signout-form', doLogout);
        // Bind the validator to the login form when it becomes available
        $('body').on('submit', '.gh-signin-form', doLogin).validator({'disable': false});
    };

    addBinding();
});
<commit_msg>Fixed shib login<commit_after>/*!
 * Copyright 2015 Digital Services, University of Cambridge Licensed
 * under the Educational Community 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://opensource.org/licenses/ECL-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.
 */

define(['gh.core'], function(gh) {

    //////////////////////
    //  AUTHENTICATION  //
    //////////////////////

    /**
     * Log in using the local authentication strategy
     *
     * @param  {Event}      ev    The jQuery event
     * @return {Boolean}          Return false to avoid the default form behaviour
     * @private
     */
    var doLogin = function(ev) {

        // Determine whether or not the global admin is in use
        var isGlobalAdminUI = $('body').hasClass('gh-global-admin');

        // Log in to the system if the form is valid and local authentication is enabled.
        // If Shibboleth is required the native form behaviour will be used and no extra
        // handling is required
        if (!ev.isDefaultPrevented() && (isGlobalAdminUI || (gh.config.enableLocalAuth && !gh.config.enableShibbolethAuth))) {
            // Collect and submit the form data
            var formValues = _.object(_.map($(this).serializeArray(), _.values));
            gh.api.authenticationAPI.login(formValues.username, formValues.password, function(err) {
                if (err) {
                    return gh.utils.notification('Could not sign you in', 'Please check that you are entering a correct username & password', 'error');
                }
                window.location.reload();
            });

            // Avoid default form submit behaviour
            return false;
        }
    };

    /**
     * Log the current user out
     *
     * @param  {Event}    ev    The jQuery event
     * @private
     */
    var doLogout = function(ev) {
        // Prevent the form from being submitted
        ev.preventDefault();
        // Sign out the user
        gh.api.authenticationAPI.logout(function(err) {
            if (err) {
                return gh.utils.notification('Logout failed', 'Logging out of the application failed', 'error');
            }
            window.location = '/';
        });
    };

    /////////////
    // BINDING //
    /////////////

    /**
     * Add handlers to various elements
     *
     * @private
     */
    var addBinding = function() {
        // Sign out the user when the form is submitted
        $('body').on('submit', '#gh-signout-form', doLogout);
        // Bind the validator to the login form when it becomes available
        $('body').on('submit', '.gh-signin-form', doLogin).validator({'disable': false});
    };

    addBinding();
});
<|endoftext|>


<commit_before>// Eloquent JavaScript

// Run this file in your terminal using `node my_solution.js`. Make sure it works before moving on!

// Program Structure
// Write your own variable and do something to it.

// Complete one of the exercises: Looping a Triangle, FizzBuzz, or Chess Board

// Functions

// Complete the `minimum` exercise.

// Data Structures: Objects and Arrays
// Create an object called "me" that stores your name, age, 3 favorite foods, and a quirk below.<commit_msg>typed some stuff<commit_after>// Eloquent JavaScript
//
// release 1
//
// Did you learn anything new about JavaScript or programming in general?
//
//   Near the beginning of the chapter he describes point-and-click limitations
//   while highlighting the need for programming languages. It was a nice bit of
//   perspective to think of language as a tool. Also while I knew Java and JS are
//   wildly different, I didn't realize the naming had to do with marketing.
//
// Are there any concepts you feel uncomfortable with?
//
//   Yeah, nothing too wild here. I would like to find some sort of application or
//   plugin that will be a bit more responsive to my JS errors, however.
//
// chapter 1
// Identify two similarities and two differences between JavaScript and Ruby syntax
// with regard to numbers, arithmetic, strings, booleans, and various operators.
//
//   Symbols for operators are mostly the same in both languages. ||, &&, and ! are
//   used in both.
//
//
//   Ruby conditionals behave pretty logically but JS can be used to
//   "short circuit." Both languages use the == operator to check eqaulity,
//   but JS has an additional === operator designed to compare "precise value."
//
//
//

// Run this file in your terminal using `node my_solution.js`. Make sure it works before moving on!

// Program Structure
// Write your own variable and do something to it.

// Complete one of the exercises: Looping a Triangle, FizzBuzz, or Chess Board

// Functions

// Complete the `minimum` exercise.

// Data Structures: Objects and Arrays
// Create an object called "me" that stores your name, age, 3 favorite foods, and a quirk below.<|endoftext|>


<commit_before>// @flow
import * as Constants from '../../constants/searchv3'
import React from 'react'
import {Box, Icon, ClickableBox, Text} from '../../common-adapters/index'
import {globalColors, globalStyles, globalMargins, hairlineWidth} from '../../styles'
import IconOrAvatar from '../icon-or-avatar'
import {followingStateToStyle} from '../shared'
import {isMobile} from '../../constants/platform'

const Left = ({leftService, leftIcon, leftUsername, leftFollowingState}) => {
  return (
    <Box
      style={{
        ...globalStyles.flexBoxRow,
        alignItems: 'center',
        height: '100%',
        paddingLeft: globalMargins.tiny,
        // TODO we might want to change this for the mobile version. Will play around with it more
        width: 215,
      }}
    >
      <Box style={{...globalStyles.flexBoxCenter, width: 32}}>
        <IconOrAvatar service={leftService} username={leftUsername} icon={leftIcon} avatarSize={32} />
      </Box>
      <Text
        type="BodySemibold"
        style={{
          ...followingStateToStyle(leftFollowingState),
          marginLeft: globalMargins.small,
        }}
      >
        {leftUsername}
      </Text>
    </Box>
  )
}

const Middle = ({rightService, rightIcon, rightUsername, rightFullname, rightFollowingState}) => {
  return (
    <Box
      style={{
        ...globalStyles.flexBoxColumn,
        flex: 1,
        height: '100%',
        justifyContent: 'center',
      }}
    >
      <Box style={{...globalStyles.flexBoxRow, alignItems: 'center'}}>
        <IconOrAvatar
          service={rightService}
          username={rightUsername}
          icon={rightIcon}
          avatarSize={12}
          style={{
            fontSize: 12,
            height: 12,
            marginRight: 3,
            width: 12,
          }}
        />
        {!!rightUsername &&
          <Text type="BodySmallSemibold" style={followingStateToStyle(rightFollowingState)}>
            {rightUsername}
          </Text>}
      </Box>
      {!!rightFullname &&
        <Box style={globalStyles.flexBoxRow}>
          <Box
            style={{
              maxWidth: 15,
              minHeight: 1,
              minWidth: 15,
            }}
          />
          <Text type="BodySmall">{rightFullname}</Text>
        </Box>}
    </Box>
  )
}

const Right = ({showTrackerButton, onShowTracker}) => {
  return showTrackerButton
    ? <Icon
        type="iconfont-usercard"
        onClick={onShowTracker}
        style={{marginLeft: globalMargins.small, marginRight: globalMargins.small}}
      />
    : null
}

const Line = () => (
  <Box
    style={{
      ...globalStyles.fillAbsolute,
      backgroundColor: globalColors.black_05,
      left: 54,
      top: undefined,
      maxHeight: hairlineWidth,
      minHeight: hairlineWidth,
    }}
  />
)

const SearchResultRow = (props: Constants.RowProps) => {
  return (
    <ClickableBox
      style={_clickableBoxStyle[(!!props.selected).toString()]}
      underlayColor={globalColors.blue4}
      onClick={props.onClick}
    >
      <Box style={_rowStyle}>
        <Left
          leftFollowingState={props.leftFollowingState}
          leftIcon={props.leftIcon}
          leftService={props.leftService}
          leftUsername={props.leftUsername}
        />
        <Middle
          rightFollowingState={props.rightFollowingState}
          rightFullname={props.rightFullname}
          rightIcon={props.rightIcon}
          rightService={props.rightService}
          rightUsername={props.rightUsername}
        />
        <Right showTrackerButton={props.showTrackerButton} onShowTracker={props.onShowTracker} />
        <Line />
      </Box>
    </ClickableBox>
  )
}

const _clickableBoxStyleCommon = {
  ...globalStyles.flexBoxRow,
  flex: 1,
  width: '100%',
  ...(isMobile
    ? {
        minHeight: 56,
        maxHeight: 56,
      }
    : {
        maxHeight: globalMargins.large,
        minHeight: globalMargins.large,
      }),
}

const _clickableBoxStyle = {
  false: _clickableBoxStyleCommon,
  true: {
    ..._clickableBoxStyleCommon,
    backgroundColor: globalColors.blue4,
  },
}

const _rowStyle = {
  ..._clickableBoxStyleCommon,
  alignItems: 'center',
  justifyContent: 'flex-start',
  position: 'relative',
}

export default SearchResultRow
<commit_msg>fix width for mobile (#7691)<commit_after>// @flow
import * as Constants from '../../constants/searchv3'
import React from 'react'
import {Box, Icon, ClickableBox, Text} from '../../common-adapters/index'
import {globalColors, globalStyles, globalMargins, hairlineWidth} from '../../styles'
import IconOrAvatar from '../icon-or-avatar'
import {followingStateToStyle} from '../shared'
import {isMobile} from '../../constants/platform'

const Left = ({leftService, leftIcon, leftUsername, leftFollowingState}) => {
  return (
    <Box
      style={{
        ...globalStyles.flexBoxRow,
        alignItems: 'center',
        height: '100%',
        paddingLeft: globalMargins.tiny,
        width: isMobile ? 170 : 215,
      }}
    >
      <Box style={{...globalStyles.flexBoxCenter, width: 32}}>
        <IconOrAvatar service={leftService} username={leftUsername} icon={leftIcon} avatarSize={32} />
      </Box>
      <Text
        type="BodySemibold"
        style={{
          ...followingStateToStyle(leftFollowingState),
          marginLeft: globalMargins.small,
        }}
      >
        {leftUsername}
      </Text>
    </Box>
  )
}

const Middle = ({rightService, rightIcon, rightUsername, rightFullname, rightFollowingState}) => {
  return (
    <Box
      style={{
        ...globalStyles.flexBoxColumn,
        flex: 1,
        height: '100%',
        justifyContent: 'center',
      }}
    >
      <Box style={{...globalStyles.flexBoxRow, alignItems: 'center'}}>
        <IconOrAvatar
          service={rightService}
          username={rightUsername}
          icon={rightIcon}
          avatarSize={12}
          style={{
            fontSize: 12,
            height: 12,
            marginRight: 3,
            width: 12,
          }}
        />
        {!!rightUsername &&
          <Text type="BodySmallSemibold" style={followingStateToStyle(rightFollowingState)}>
            {rightUsername}
          </Text>}
      </Box>
      {!!rightFullname &&
        <Box style={globalStyles.flexBoxRow}>
          <Box
            style={{
              maxWidth: 15,
              minHeight: 1,
              minWidth: 15,
            }}
          />
          <Text type="BodySmall">{rightFullname}</Text>
        </Box>}
    </Box>
  )
}

const Right = ({showTrackerButton, onShowTracker}) => {
  return showTrackerButton
    ? <Icon
        type="iconfont-usercard"
        onClick={onShowTracker}
        style={{
          marginLeft: globalMargins.small,
          marginRight: isMobile ? globalMargins.xtiny : globalMargins.small,
        }}
      />
    : null
}

const Line = () => (
  <Box
    style={{
      ...globalStyles.fillAbsolute,
      backgroundColor: globalColors.black_05,
      left: 54,
      top: undefined,
      maxHeight: hairlineWidth,
      minHeight: hairlineWidth,
    }}
  />
)

const SearchResultRow = (props: Constants.RowProps) => {
  return (
    <ClickableBox
      style={_clickableBoxStyle[(!!props.selected).toString()]}
      underlayColor={globalColors.blue4}
      onClick={props.onClick}
    >
      <Box style={_rowStyle}>
        <Left
          leftFollowingState={props.leftFollowingState}
          leftIcon={props.leftIcon}
          leftService={props.leftService}
          leftUsername={props.leftUsername}
        />
        <Middle
          rightFollowingState={props.rightFollowingState}
          rightFullname={props.rightFullname}
          rightIcon={props.rightIcon}
          rightService={props.rightService}
          rightUsername={props.rightUsername}
        />
        <Right showTrackerButton={props.showTrackerButton} onShowTracker={props.onShowTracker} />
        <Line />
      </Box>
    </ClickableBox>
  )
}

const _clickableBoxStyleCommon = {
  ...globalStyles.flexBoxRow,
  flex: 1,
  width: '100%',
  ...(isMobile
    ? {
        minHeight: 56,
        maxHeight: 56,
      }
    : {
        maxHeight: globalMargins.large,
        minHeight: globalMargins.large,
      }),
}

const _clickableBoxStyle = {
  false: _clickableBoxStyleCommon,
  true: {
    ..._clickableBoxStyleCommon,
    backgroundColor: globalColors.blue4,
  },
}

const _rowStyle = {
  ..._clickableBoxStyleCommon,
  alignItems: 'center',
  justifyContent: 'flex-start',
  position: 'relative',
}

export default SearchResultRow
<|endoftext|>


<commit_before>// imported from the inactive react-scroll-components project
// https://github.com/jeroencoumans/react-scroll-components
// Updated to work with React versions > 15.3
// which lack the ViewportMetrics that the original relied on
//

'use strict';

var win = typeof window !== 'undefined' ? window : false;

var ScrollListenerMixin = {

  getDefaultProps: function () {
    return {
      endScrollTimeout: 300
    };
  },

  getInitialState: function () {
    return {
      scrollTop: 0,
      isScrolling: false
    };
  },

  componentDidMount: function () {
    if (win) {
      win.addEventListener('scroll', this._onPageScroll);
    }
  },

  componentWillUnmount: function () {
    if (win) {
      win.removeEventListener('scroll', this._onPageScroll);
    }
  },

  _onPageScrollEnd: function () {
    var scrollTop = win.document.body.scrollTop;
    if (scrollTop === this.state.scrollTop) {
      win.clearTimeout(this._pageScrollTimeout);
      this.setState({ isScrolling: false });

      if (typeof this.onPageScrollEnd === 'function') {
        this.onPageScrollEnd(scrollTop);
      }
    }
  },

  _onPageScroll: function () {
    const { scrollTop } =(document.documentElement || document.body.parentNode || document.body);

    this.setState({
      scrollTop: scrollTop,
      isScrolling: true
    });

    win.clearTimeout(this._pageScrollTimeout);
    this._pageScrollTimeout = win.setTimeout(this._onPageScrollEnd, this.props.endScrollTimeout);

    if (typeof this.onPageScroll === 'function') {
      this.onPageScroll(scrollTop);
    }
  }
};

module.exports = ScrollListenerMixin;
<commit_msg>use document.body to measure scrolling<commit_after>import { bind } from 'lodash';
// imported from the inactive react-scroll-components project
// https://github.com/jeroencoumans/react-scroll-components
// Updated to work with React versions > 15.3
// which lack the ViewportMetrics that the original relied on
//

var win = typeof window !== 'undefined' ? window : false;
const ScrollElement = document.body;

var ScrollListenerMixin = {

  getDefaultProps() {
    return {
      endScrollTimeout: 300,
    };
  },

  getInitialState() {
    return {
      scrollTop: 0,
      isScrolling: false,
    };
  },

  componentDidMount() {
    if (win) {
      win.addEventListener('scroll', this._onPageScroll);
    }
  },

  componentWillUnmount() {
    if (win) {
      win.removeEventListener('scroll', this._onPageScroll);
    }
  },

  _onPageScrollEnd() {
    const { scrollTop } = ScrollElement;

    if (scrollTop === this.state.scrollTop) {
      win.clearTimeout(this._pageScrollTimeout);
      this.setState({ isScrolling: false });

      if (typeof this.onPageScrollEnd === 'function') {
        this.onPageScrollEnd(scrollTop);
      }
    }
  },

  _onPageScroll() {
    const { scrollTop } = ScrollElement;
    this.setState({
      scrollTop: scrollTop,
      isScrolling: true,
    });
    win.clearTimeout(this._pageScrollTimeout);
    this._pageScrollTimeout = win.setTimeout(this._onPageScrollEnd, this.props.endScrollTimeout);
    if (typeof this.onPageScroll === 'function') {
      this.onPageScroll(scrollTop);
    }
  },
};

export default ScrollListenerMixin;
<|endoftext|>


<commit_before>
// sfw_form.js

(function _init()
{
   if ((!("SFW" in window) && setTimeout(_init,100))
       || SFW.delay_init("sfw_form",_init,"iclass"))
      return;

   if (!SFW.derive(_form, "form", "iclass"))
      return;

   // derive() is not called for _form_new or _form_edit until the prototypes
   // of their base classes are complete.  Look for those derives at the end
   // of the module.

   function _form(actors)
   {
      SFW.base.call(this, actors);
   }

   // Make copies of _form for prototype property "class_name" (RTTI).
   function _form_new(base, doc, caller, data) { _form.apply(this, arguments); }
   function _form_edit(base, doc, caller, data) { _form.apply(this, arguments); }

   // Adding useful local functions to global object
   SFW.get_form_data        = _get_form_data;
   SFW.focus_on_first_field = _focus_on_first_field;

   function _find_first_editable_field(form)
   {
      var arr = SFW.collect_form_fields(form);
      for (var i=0,stop=arr.length; i<stop; ++i)
      {
         var el = arr[i];
         if (!("type" in el) || el.type!="button")
            return el;
      }
      return null;
   }

   function _focus_on_first_field(dlg)
   {
      var el = _find_first_editable_field(dlg);
      if (el)
      {
         el.focus();
         if (el.getAttribute("type")=="text")
         {
            el.selectionStart = 0;
            el.selectionEnd = el.value.length;
         }
      }
   }

   function _get_multiple_value(el)
   {
      var str = "";
      function f(n)
      {
         if (n.nodeType==1 && n.selected)
            str += (str.length?",":"") + n.value;
      }

      SFW.find_child_matches(el, f);

      return str;
   }

   function _get_form_data(form)
   {
      var el, els = form.elements;
      var arr = [];
      var noninputs = 'submit reset button';
      for (var i=0, stop=els.length; i<stop; i++)
      {
         el = els[i];
         if (noninputs.search(el.type)==-1 && el.name.length)
         {
            if (el.type=="checkbox")
            {
               if (el.checked)
                  arr.push(el.name + "=1");
            }
            else if ('value' in el && el.value.length>0)
            {
               if ("multiple" in el && el.multiple)
                  arr.push(el.name + "=" + _get_multiple_value(el));
               else
                  arr.push(el.name + "=" + encodeURIComponent(el.value));
            }
         }
      }
      return arr;
   }

   _form.prototype.focus_on_first_field = function()
   {
      var top = this.top();
      _focus_on_first_field(this.top());
   };

   _form.prototype.get_first_editable_form_field = function()
   {
      var top = this.top();
      return top?_find_first_editable_field(top):null;
   };

   _form.prototype.get_data_path_row = function()
   {
      var xpath,form = this.top();

      if ((xpath=form.getAttribute("data-path")))
         return SFW.xmldoc.selectSingleNode(xpath);
      else
         return null;
   };

   _form.prototype.get_context_row = function()
   {
      return SFW.get_property(this,"host","data","xrow");
   };

   _form.prototype.get_saved_os = function()
   {
      return SFW.get_property(this,"host","data","os");
   };

   _form.prototype.find_form_input = function(name)
   {
      var labels = this.top().getElementsByTagName("label");
      for (var i=0, stop=labels.length; i<stop; ++i)
      {
         var f = labels[i];
         if (f.getAttribute("for")==name)
         {
            f = f.nextSibling;
            while (f)
            {
               if (f.nodeType==1 && f.getAttribute("name")==name)
                  return f;
               else
                  f = f.nextSibling;
            }
         }
      }
      return null;
   };

/**
   This function should branch based on the action taken
   by the returned_doc.
   1. Delete document: remove the row from any field views
   2. Add document: add new data to any field views
   3. Otherwise, it's an edit, just update.

   Note that the returned_doc will have already been incorporated
   into (or out of) the XML document.  This function is only
   responsible for updating the display to conform to the existing data.
*/
   _form.prototype.update_contents = function(returned_doc, type, ichild)
   {
      var row, schema, fields;
      if ((row=this.get_data_path_row() || this.get_context_row()) &&
          (schema=this.schema())
          && (fields=schema.selectNodes("field[@type='linked']")))
      {
         for (var i=0, stop=fields.length; i<stop; ++i)
         {
            var fld = fields[i];
            var name = fld.getAttribute("name");
            var input = this.find_form_input(name);
            if (input)
            {
               row.setAttribute("lookup-field-match", name);
               SFW.xslobj.transformFill(input, row);
               row.removeAttribute("lookup-field-match");
            }
         }
      }
   };

   _form.prototype.preview_result = _form.prototype.update_contents;

   _form.prototype.process_submit = function _process_submit()
   {
      var form = this.top();
      var type = form.getAttribute("data-sfw-class");
      var arr = _get_form_data(form);
      var url = form.getAttribute("action");
      var enctype = form.getAttribute("enctype");
      var headers = enctype ? [{name:"enctype", value:enctype}] : null;

      var ths = this;
      function cb_good(doc)
      {
         if (SFW.check_for_preempt(doc))
         {
            SFW.update_xmldoc(doc, ths);

            var caller = ths.caller();
            if (caller)
            {
               // caller.preview_result(doc, ths);
               // caller.child_finished(ths);

               caller.cascade_updates(doc,type,ths);
               caller.restart(ths);
               ths.dismantle();
            }
         }
      }

      function cb_bad(xhr)
      {
         console.error(xhr.ResponseText);
      }

      xhr_post(url,arr.join("&"),
               cb_good, cb_bad, headers);
   };

   _form.prototype.process_button_delete = function(t,cb)
   {
      var url = t.getAttribute("data-task") || t.getAttribute("data-url");
      xhr_get(url,cb);
   };

   _form.prototype.set_field = function _set_field(name,value)
   {
      var form = this.top();
      var el, els = form.elements;
      for (var i=0, stop=els.length; i<stop; i++)
      {
         el = els[i];
         if (el.name == name)
         {
            el.value = value;
            break;
         }
      }
   };

   _form.prototype.process_button = function _process_button(e,t)
   {
      var ths = this;
      function fdone(cmd)
      {
         var attr, caller;

         // Look at _base.prototype.process_clicked_button()...

         // fdone is called with string=='cancel' is a confirmation
         // message was NOT confirmed.  We will need to rework this
         // code if fdone gets a string argument in a context other
         // than a declined confirmation dialog.
         if (typeof(cmd)==="string" && cmd=="cancel")
            return; // without closing the dialog

         var is_xmldoc = SFW.is_xmldoc(cmd);

         if (is_xmldoc)
         {
            if (SFW.check_for_preempt(cmd))
            {
               var is_del_mode = cmd.documentElement.getAttribute("mode-type")=="delete";
               var update_ok = SFW.update_xmldoc(cmd,ths);

               if (is_del_mode && !update_ok)
               {
                  SFW.alert("Delete operation failed");
                  return;  // return without closing the dialog
               }
            }
         }

         if ((caller=ths.caller()))
         {
            if (cmd!="cancel" && is_xmldoc)
               caller.cascade_updates(cmd,ths);
            ths.dismantle();
            // caller.child_finished(ths,cmd=="cancel");
        }
      }
      return this.process_clicked_button(t, fdone);
   };

   _form.prototype.initialize =
   _form.prototype.post_transform = function()
   {
      this.focus_on_first_field();
   };

   _form.prototype.closeable = function() { return true; };

   _form.prototype.process = function _form_process_message(e,t)
   {
      if (e.type!="click")
         return true;

      switch(t.type)
      {
      case "button":
         if (!this.process_button(e,t))
            return false;
      case "submit":
         if (this.caller())
         {
            this.process_submit();
            e.preventDefault();
            return false;
         }
         break;
      }

      return true;
   };

   // With _form prototype complete, we can derive other classes from it:
   if(!SFW.derive(_form_new, "form-new", "form") ||
      !SFW.derive(_form_edit, "form-edit", "form"))
      return;
})();
<commit_msg>Added update_context_row() to replace the xrow pointer after updating the document.<commit_after>
// sfw_form.js

(function _init()
{
   if ((!("SFW" in window) && setTimeout(_init,100))
       || SFW.delay_init("sfw_form",_init,"iclass"))
      return;

   if (!SFW.derive(_form, "form", "iclass"))
      return;

   // derive() is not called for _form_new or _form_edit until the prototypes
   // of their base classes are complete.  Look for those derives at the end
   // of the module.

   function _form(actors)
   {
      SFW.base.call(this, actors);
   }

   // Make copies of _form for prototype property "class_name" (RTTI).
   function _form_new(base, doc, caller, data) { _form.apply(this, arguments); }
   function _form_edit(base, doc, caller, data) { _form.apply(this, arguments); }

   // Adding useful local functions to global object
   SFW.get_form_data        = _get_form_data;
   SFW.focus_on_first_field = _focus_on_first_field;

   function _find_first_editable_field(form)
   {
      var arr = SFW.collect_form_fields(form);
      for (var i=0,stop=arr.length; i<stop; ++i)
      {
         var el = arr[i];
         if (!("type" in el) || el.type!="button")
            return el;
      }
      return null;
   }

   function _focus_on_first_field(dlg)
   {
      var el = _find_first_editable_field(dlg);
      if (el)
      {
         el.focus();
         if (el.getAttribute("type")=="text")
         {
            el.selectionStart = 0;
            el.selectionEnd = el.value.length;
         }
      }
   }

   function _get_multiple_value(el)
   {
      var str = "";
      function f(n)
      {
         if (n.nodeType==1 && n.selected)
            str += (str.length?",":"") + n.value;
      }

      SFW.find_child_matches(el, f);

      return str;
   }

   function _get_form_data(form)
   {
      var el, els = form.elements;
      var arr = [];
      var noninputs = 'submit reset button';
      for (var i=0, stop=els.length; i<stop; i++)
      {
         el = els[i];
         if (noninputs.search(el.type)==-1 && el.name.length)
         {
            if (el.type=="checkbox")
            {
               if (el.checked)
                  arr.push(el.name + "=1");
            }
            else if ('value' in el && el.value.length>0)
            {
               if ("multiple" in el && el.multiple)
                  arr.push(el.name + "=" + _get_multiple_value(el));
               else
                  arr.push(el.name + "=" + encodeURIComponent(el.value));
            }
         }
      }
      return arr;
   }

   _form.prototype.focus_on_first_field = function()
   {
      var top = this.top();
      _focus_on_first_field(this.top());
   };

   _form.prototype.get_first_editable_form_field = function()
   {
      var top = this.top();
      return top?_find_first_editable_field(top):null;
   };

   _form.prototype.get_data_path_row = function()
   {
      var xpath,form = this.top();

      if ((xpath=form.getAttribute("data-path")))
         return SFW.xmldoc.selectSingleNode(xpath);
      else
         return null;
   };

   _form.prototype.get_context_row = function()
   {
      return SFW.get_property(this,"host","data","xrow");
   };

   _form.prototype.update_context_row = function(newrow)
   {
      var data = SFW.get_property(this,"host","data");
      if (data)
         data.xrow = newrow;
   };

   _form.prototype.get_saved_os = function()
   {
      return SFW.get_property(this,"host","data","os");
   };

   _form.prototype.find_form_input = function(name)
   {
      var labels = this.top().getElementsByTagName("label");
      for (var i=0, stop=labels.length; i<stop; ++i)
      {
         var f = labels[i];
         if (f.getAttribute("for")==name)
         {
            f = f.nextSibling;
            while (f)
            {
               if (f.nodeType==1 && f.getAttribute("name")==name)
                  return f;
               else
                  f = f.nextSibling;
            }
         }
      }
      return null;
   };

/**
   This function should branch based on the action taken
   by the returned_doc.
   1. Delete document: remove the row from any field views
   2. Add document: add new data to any field views
   3. Otherwise, it's an edit, just update.

   Note that the returned_doc will have already been incorporated
   into (or out of) the XML document.  This function is only
   responsible for updating the display to conform to the existing data.
*/
   _form.prototype.update_contents = function(returned_doc, type, ichild)
   {
      var row, schema, fields;
      if ((row=this.get_data_path_row() || this.get_context_row()) &&
          (schema=this.schema())
          && (fields=schema.selectNodes("field[@type='linked']")))
      {
         for (var i=0, stop=fields.length; i<stop; ++i)
         {
            var fld = fields[i];
            var name = fld.getAttribute("name");
            var input = this.find_form_input(name);
            if (input)
            {
               row.setAttribute("lookup-field-match", name);
               SFW.xslobj.transformFill(input, row);
               row.removeAttribute("lookup-field-match");
            }
         }
      }
   };

   _form.prototype.preview_result = _form.prototype.update_contents;

   _form.prototype.process_submit = function _process_submit()
   {
      var form = this.top();
      var type = form.getAttribute("data-sfw-class");
      var arr = _get_form_data(form);
      var url = form.getAttribute("action");
      var enctype = form.getAttribute("enctype");
      var headers = enctype ? [{name:"enctype", value:enctype}] : null;

      var ths = this;
      function cb_good(doc)
      {
         if (SFW.check_for_preempt(doc))
         {
            SFW.update_xmldoc(doc, ths);

            var caller = ths.caller();
            if (caller)
            {
               // caller.preview_result(doc, ths);
               // caller.child_finished(ths);

               caller.cascade_updates(doc,type,ths);
               caller.restart(ths);
               ths.dismantle();
            }
         }
      }

      function cb_bad(xhr)
      {
         console.error(xhr.ResponseText);
      }

      xhr_post(url,arr.join("&"),
               cb_good, cb_bad, headers);
   };

   _form.prototype.process_button_delete = function(t,cb)
   {
      var url = t.getAttribute("data-task") || t.getAttribute("data-url");
      xhr_get(url,cb);
   };

   _form.prototype.set_field = function _set_field(name,value)
   {
      var form = this.top();
      var el, els = form.elements;
      for (var i=0, stop=els.length; i<stop; i++)
      {
         el = els[i];
         if (el.name == name)
         {
            el.value = value;
            break;
         }
      }
   };

   _form.prototype.process_button = function _process_button(e,t)
   {
      var ths = this;
      function fdone(cmd)
      {
         var attr, caller;

         // Look at _base.prototype.process_clicked_button()...

         // fdone is called with string=='cancel' is a confirmation
         // message was NOT confirmed.  We will need to rework this
         // code if fdone gets a string argument in a context other
         // than a declined confirmation dialog.
         if (typeof(cmd)==="string" && cmd=="cancel")
            return; // without closing the dialog

         var is_xmldoc = SFW.is_xmldoc(cmd);

         if (is_xmldoc)
         {
            if (SFW.check_for_preempt(cmd))
            {
               var is_del_mode = cmd.documentElement.getAttribute("mode-type")=="delete";
               var update_ok = SFW.update_xmldoc(cmd,ths);

               if (is_del_mode && !update_ok)
               {
                  SFW.alert("Delete operation failed");
                  return;  // return without closing the dialog
               }
            }
         }

         if ((caller=ths.caller()))
         {
            if (cmd!="cancel" && is_xmldoc)
               caller.cascade_updates(cmd,ths);
            ths.dismantle();
            // caller.child_finished(ths,cmd=="cancel");
        }
      }
      return this.process_clicked_button(t, fdone);
   };

   _form.prototype.initialize =
   _form.prototype.post_transform = function()
   {
      this.focus_on_first_field();
   };

   _form.prototype.closeable = function() { return true; };

   _form.prototype.process = function _form_process_message(e,t)
   {
      if (e.type!="click")
         return true;

      switch(t.type)
      {
      case "button":
         if (!this.process_button(e,t))
            return false;
      case "submit":
         if (this.caller())
         {
            this.process_submit();
            e.preventDefault();
            return false;
         }
         break;
      }

      return true;
   };

   // With _form prototype complete, we can derive other classes from it:
   if(!SFW.derive(_form_new, "form-new", "form") ||
      !SFW.derive(_form_edit, "form-edit", "form"))
      return;
})();
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2012-2013 Nicolas Froidure
 *
 * This file is free software;
 * you can redistribute it and/or modify it under the terms of the GNU
 * General Public License (GPL) as published by the Free Software
 * Foundation, in version 3. It is distributed in the
 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
 *
 */

// AMD + global + NodeJS : You can use this object by inserting a script
// or using an AMD loader (like RequireJS) or using NodeJS
(function(root,define){ define([], function() {
// START: Module logic start

  function VarStreamWriter(callback, options) {
    this.lastContext='';
    this.callback=callback; // Output stream callback
    this.options=options;
    this.imbricatedArrayEntries=new Array();
  }

  // Static consts
  VarStreamWriter.MORPH_CONTEXTS=2;
  VarStreamWriter.MERGE_ARRAYS=4;
  VarStreamWriter.OPTIONS=
    VarStreamWriter.MORPH_CONTEXTS|VarStreamWriter.MERGE_ARRAYS;

  VarStreamWriter.prototype.write = function (scope, context) {
    if(!context)
      context='';
    if(scope instanceof Array) {
      for(var i=0, j=scope.length; i<j; i++) {
        if(this.debug) {
          console.log('Reading array entry '+i+' in scope '+context);
        }
        this.imbricatedArrayEntries.push(true);
        this.write(scope[i],(context?context+'.':'')
          +(this.options&VarStreamWriter.MERGE_ARRAYS?'?':1));
        this.imbricatedArrayEntries.pop();
      }
    } else if(scope instanceof Object) {
      for (prop in scope) {
        if(this.debug) {
          console.log('Reading object property '+prop+' in scope '+context);
        }
        if (scope.hasOwnProperty(prop)&&(!(scope instanceof Function))
          &&/^([a-z0-9_]+)$/i.test(prop)) {
          this.write(scope[prop],(context?context+'.':'')+prop);
        }
      }
    } else {
      if(this.debug) {
        console.log('Writing value '+context);
      }
      // Changing context with imbricated arrays
      for(var i=this.imbricatedArrayEntries.length-1; i>=0; i--) {
        var index=context.lastIndexOf('?');
        if(this.imbricatedArrayEntries[i]) {
          context=context.substr(0,index)+'+'+context.substr(index+1);
          this.imbricatedArrayEntries[i]=false;
        } else {
          context=context.substr(0,index)+'*'+context.substr(index+1);
        }
      }
      // Trying to reduce context with ^
      var morphedContext=context;
      if(this.options&VarStreamWriter.MORPH_CONTEXTS&&this.lastContext
        &&morphedContext.indexOf(this.lastContext+'.')===0) {
        morphedContext=morphedContext.replace(this.lastContext,'^')
      }
      // Saving this context for later use
      var index=context.lastIndexOf('.');
      this.lastContext=(index!==false?context.substr(0,index):'');
      // Calling back
      this.callback(morphedContext+'='+(scope+'')
        .replace(/(\r?\n)/igm,'\\'+"\n")+"\n");
    }
  };

// END: Module logic end

  return VarStreamWriter;

});})(this,typeof define === 'function' && define.amd ?
  // AMD
  define :
  // NodeJS
  (typeof exports === 'object'?function (name, deps, factory) {
    var root=this;
    if(typeof name === 'object') {
      factory=deps; deps=name;
    }
    module.exports=factory.apply(this, deps.map(function(dep){
      return require(dep);
    }));
  }:
  // Global
  function (name, deps, factory) {
    var root=this;
    if(typeof name === 'object') {
      factory=deps; deps=name;
    }
    this.VarStreamWriter=factory.apply(this, deps.map(function(dep){
      return root[dep.substring(dep.lastIndexOf('/')+1)];
    }));
  }.bind(this)
  )
);

<commit_msg>Removed the prop memory leak<commit_after>/*
 * Copyright (C) 2012-2013 Nicolas Froidure
 *
 * This file is free software;
 * you can redistribute it and/or modify it under the terms of the GNU
 * General Public License (GPL) as published by the Free Software
 * Foundation, in version 3. It is distributed in the
 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
 *
 */

// AMD + global + NodeJS : You can use this object by inserting a script
// or using an AMD loader (like RequireJS) or using NodeJS
(function(root,define){ define([], function() {
// START: Module logic start

  function VarStreamWriter(callback, options) {
    this.lastContext='';
    this.callback=callback; // Output stream callback
    this.options=options;
    this.imbricatedArrayEntries=new Array();
  }

  // Static consts
  VarStreamWriter.MORPH_CONTEXTS=2;
  VarStreamWriter.MERGE_ARRAYS=4;
  VarStreamWriter.OPTIONS=
    VarStreamWriter.MORPH_CONTEXTS|VarStreamWriter.MERGE_ARRAYS;

  VarStreamWriter.prototype.write = function (scope, context) {
    if(!context)
      context='';
    if(scope instanceof Array) {
      for(var i=0, j=scope.length; i<j; i++) {
        if(this.debug) {
          console.log('Reading array entry '+i+' in scope '+context);
        }
        this.imbricatedArrayEntries.push(true);
        this.write(scope[i],(context?context+'.':'')
          +(this.options&VarStreamWriter.MERGE_ARRAYS?'?':1));
        this.imbricatedArrayEntries.pop();
      }
    } else if(scope instanceof Object) {
      for (var prop in scope) {
        if(this.debug) {
          console.log('Reading object property '+prop+' in scope '+context);
        }
        if (scope.hasOwnProperty(prop)&&(!(scope instanceof Function))
          &&/^([a-z0-9_]+)$/i.test(prop)) {
          this.write(scope[prop],(context?context+'.':'')+prop);
        }
      }
    } else {
      if(this.debug) {
        console.log('Writing value '+context);
      }
      // Changing context with imbricated arrays
      for(var i=this.imbricatedArrayEntries.length-1; i>=0; i--) {
        var index=context.lastIndexOf('?');
        if(this.imbricatedArrayEntries[i]) {
          context=context.substr(0,index)+'+'+context.substr(index+1);
          this.imbricatedArrayEntries[i]=false;
        } else {
          context=context.substr(0,index)+'*'+context.substr(index+1);
        }
      }
      // Trying to reduce context with ^
      var morphedContext=context;
      if(this.options&VarStreamWriter.MORPH_CONTEXTS&&this.lastContext
        &&morphedContext.indexOf(this.lastContext+'.')===0) {
        morphedContext=morphedContext.replace(this.lastContext,'^')
      }
      // Saving this context for later use
      var index=context.lastIndexOf('.');
      this.lastContext=(index!==false?context.substr(0,index):'');
      // Calling back
      this.callback(morphedContext+'='+(scope+'')
        .replace(/(\r?\n)/igm,'\\'+"\n")+"\n");
    }
  };

// END: Module logic end

  return VarStreamWriter;

});})(this,typeof define === 'function' && define.amd ?
  // AMD
  define :
  // NodeJS
  (typeof exports === 'object'?function (name, deps, factory) {
    var root=this;
    if(typeof name === 'object') {
      factory=deps; deps=name;
    }
    module.exports=factory.apply(this, deps.map(function(dep){
      return require(dep);
    }));
  }:
  // Global
  function (name, deps, factory) {
    var root=this;
    if(typeof name === 'object') {
      factory=deps; deps=name;
    }
    this.VarStreamWriter=factory.apply(this, deps.map(function(dep){
      return root[dep.substring(dep.lastIndexOf('/')+1)];
    }));
  }.bind(this)
  )
);

<|endoftext|>