<commit_before>/* global bootbox, PasswordSecurityTranslation, MenuTranslation, zenscroll, vanillaToast, GlobalTranslation */
// Internet Exploder caches AJAX requests by default
$(document).ready(() => {
    const body = $("body");

    $.ajaxSetup({cache: false});

    const token = $(body).data("csrf-token");
    $.ajaxPrefilter(function (options, originalOptions, jqXHR) {
        if (options.type.toLowerCase() === "post") {
            // initialize `data` to empty string if it does not exist
            options.data = options.data || "";
            // add leading ampersand if `data` is non-empty
            options.data += options.data ? "&" : "";
            // add _token entry
            options.data += "csrf_token=" + encodeURIComponent(token);
        }
    });
});

$(() => {
    const body = $("body");
    const language = $("html").data("select2-language");
    bootbox.setDefaults({
        locale: $("html").data("select2-language")
    });

    // toggle hamburger menu
    $("#menu-toggle").click(() =>
        $(".mainmenu").slideToggle()
    );

    bindAjaxLinks(body);

    // handle browser back events (Required for ajax loaded pages)
    $(window).bind('popstate', (event) => {
        const state = event.originalEvent.state;

        console.log('popstate', state);

        // if there is no state this history entry
        // was a full page reload
        if (!state) {
            ajaxLoadSpinner.show();
            location.replace(document.location);
            return;
        }
        // if this page was loaded by ajax
        // use ajax again
        if (typeof state.ajaxUrl === 'string') {
            console.log('go back to', state.ajaxUrl);
            ajaxGoTo(state.ajaxUrl);
            return;
        }
        // If there is a state but no ajax URL
        // then this history entry was a full page reload
        // Then reload page

        ajaxLoadSpinner.show();
        location.replace(window.location.href);
    });

    // clear-cache shortcut icon
    $("#menu-clear-cache").click((event) => {
        event.preventDefault();
        event.stopPropagation();
        $("#menu-clear-cache").hide();
        $("#menu-clear-cache-loading").show();
        const url = $("#menu-clear-cache").data("url");
        $.get(url, () => {
            $("#menu-clear-cache").show();
            $("#menu-clear-cache-loading").hide();
        });
    });

    // Add bootstrap css class to tablesorter
    $.extend($.fn.dataTableExt.oStdClasses, {
        sFilterInput: "form-control",
        sLengthSelect: "form-control"
    });

    // copy text from input to clipboard on click
    $(".select-on-click").click((event) => {
        const target = $(event.target);
        copyTextToClipboard(
                target.value,
                () => vanillaToast.success(GlobalTranslation.CopiedToClipboardSuccess),
                () => {
            vanillaToast.error(GlobalTranslation.CopiedToClipboardFailed);
            target.select();
        }
        );
    });

    // Disabled a link-buttons must not be clickable
    $("a").click((event) => {
        const target = $(event.currentTarget);
        if ((target.hasClass("disabled") || target.attr("disabled")) && target.attr("href").length > 1) {
            event.preventDefault();
        }
    });

    initDataTables(body);
    initPasswordSecurityCheck(body);

    // Links to upcoming features
    $(".coming-soon").click((event) => {
        event.preventDefault();
        bootbox.alert("Coming Soon!");
    });

    // Showing a link in an alert box
    initRemoteAlerts(body);

    addCssClassToInputs($(body));

    // override save shortcut to trigger submit button
    if ($("form button[type=submit], form input[type=submit]").length) {
        document.addEventListener(
                "keydown",
                (e) => {
            if ((window.navigator.platform.match("Mac") ? e.metaKey : e.ctrlKey)
                    && e.keyCode === 83) {
                e.preventDefault();
                $("form button[type=submit], form input[type=submit]")
                        .last()
                        .click();
            }
        }, false);
    }

    $("input[type=checkbox] .select-all").change(selectAllChecked);
    $("input[type=checkbox]").change(checkboxChecked);

    // check "Select All" checkbox if all checkboxes of this group are checked
    $("input[type=checkbox]").each((index, target) => {
        const item = $(target).data("select-all-checkbox");
        const group = $(target).data("checkbox-group");
        if (item !== null && group !== null) {
            checkSelectAllIfAllChecked(item, group);
        }
    });

    // scroll to the given anchor
    const params = new URLSearchParams(location.search);
    const jumpTo = params.get('jumpto');
    if (jumpTo && jumpTo.length > 0) {
        const anchor = document.querySelector(`#${jumpTo}`);
        if (anchor) {
            zenscroll.to(anchor);
        }
    }

    initSelect2(body);
    initBootstrapToggle(body);

    $.datetimepicker.setLocale(language);

    $(".datepicker").datetimepicker({
        format: "Y-m-d",
        timepicker: false
    });

    $(".datetimepicker").prop("readonly", true);
    $(".datetimepicker").datetimepicker({
        format: "Y-m-d H:i",
        timepicker: true,
        step: 5
    });

    // User has to confirm logout
    $("a.backend-menu-item-logout").click((event) => {
        event.preventDefault();
        $(".mainmenu").hide();
        const url = $(event.target).attr("href");
        bootbox.confirm(`${MenuTranslation.Logout}?`, (result) => {
            if (result) {
                location.href = url;
            }
        });
    });

    // show a scroll-to-top arrow
    // if the scroll viewport isn't at top of the page
    $(window).scroll(() => {
        if ($(window).scrollTop() > 0) {
            $("#scroll-to-top").fadeIn();
        } else {
            $("#scroll-to-top").fadeOut();
        }
    });

    // scroll to top arrow at bottom right
    $("#scroll-to-top").click((event) => {
        event.preventDefault();
        event.stopPropagation();
        zenscroll.toY(0);
    });

});<commit_msg>Update global.js<commit_after>/* global bootbox, PasswordSecurityTranslation, MenuTranslation, zenscroll, vanillaToast, GlobalTranslation */
// Internet Exploder caches AJAX requests by default
$(document).ready(() => {
    const body = $("body");

    $.ajaxSetup({cache: false});

    const token = $(body).data("csrf-token");
    $.ajaxPrefilter(function (options, originalOptions, jqXHR) {
        if (options.type.toLowerCase() === "post") {
            // initialize `data` to empty string if it does not exist
            options.data = options.data || "";
            // add leading ampersand if `data` is non-empty
            options.data += options.data ? "&" : "";
            // add _token entry
            options.data += "csrf_token=" + encodeURIComponent(token);
        }
    });
});

$(() => {
    const body = $("body");
    const language = $("html").data("select2-language");
    bootbox.setDefaults({
        locale: $("html").data("select2-language")
    });

    // toggle hamburger menu
    $("#menu-toggle").click(() =>
        $(".mainmenu").slideToggle()
    );

    bindAjaxLinks(body);

    // handle browser back events (Required for ajax loaded pages)
    $(window).bind('popstate', (event) => {
        const state = event.originalEvent.state;

        console.log('popstate', state);

        // if there is no state this history entry
        // was a full page reload
        if (!state) {
            ajaxLoadSpinner.show();
            location.replace(document.location);
            return;
        }
        // if this page was loaded by ajax
        // use ajax again
        if (typeof state.ajaxUrl === 'string') {
            console.log('go back to', state.ajaxUrl);
            ajaxGoTo(state.ajaxUrl);
            return;
        }
        // If there is a state but no ajax URL
        // then this history entry was a full page reload
        // Then reload page

        ajaxLoadSpinner.show();
        location.replace(window.location.href);
    });

    // clear-cache shortcut icon
    $("#menu-clear-cache").click((event) => {
        event.preventDefault();
        event.stopPropagation();
        $("#menu-clear-cache").hide();
        $("#menu-clear-cache-loading").show();
        const url = $("#menu-clear-cache").data("url");
        $.get(url, () => {
            $("#menu-clear-cache").show();
            $("#menu-clear-cache-loading").hide();
        });
    });

    // Add bootstrap css class to tablesorter
    $.extend($.fn.dataTableExt.oStdClasses, {
        sFilterInput: "form-control",
        sLengthSelect: "form-control"
    });

    // copy text from input to clipboard on click
    $(".select-on-click").click((event) => {
        const target = $(event.target);
        copyTextToClipboard(
                target.value,
                () => vanillaToast.success(GlobalTranslation.CopiedToClipboardSuccess),
                () => {
            vanillaToast.error(GlobalTranslation.CopiedToClipboardFailed);
            target.select();
        }
        );
    });

    // Disabled a link-buttons must not be clickable
    $("a").click((event) => {
        const target = $(event.currentTarget);
        if ((target.hasClass("disabled") || target.attr("disabled")) && target.attr("href").length > 1) {
            event.preventDefault();
        }
    });

    initDataTables(body);
    initPasswordSecurityCheck(body);

    // Links to upcoming features
    $(".coming-soon").click((event) => {
        event.preventDefault();
        bootbox.alert("Coming Soon!");
    });

    // Showing a link in an alert box
    initRemoteAlerts(body);

    addCssClassToInputs($(body));

    // override save shortcut to trigger submit button
    if ($("form button[type=submit], form input[type=submit]").length) {
        document.addEventListener(
                "keydown",
                (e) => {
            if ((window.navigator.platform.match("Mac") ? e.metaKey : e.ctrlKey)
                    && e.keyCode === 83) {
                e.preventDefault();
                $("form button[type=submit], form input[type=submit]")
                        .last()
                        .click();
            }
        }, false);
    }

    $("input[type=checkbox] .select-all").change(selectAllChecked);
    $("input[type=checkbox]").change(checkboxChecked);

    // check "Select All" checkbox if all checkboxes of this group are checked
    $("input[type=checkbox]").each((index, target) => {
        const item = $(target).data("select-all-checkbox");
        const group = $(target).data("checkbox-group");
        if (item !== null && group !== null) {
            checkSelectAllIfAllChecked(item, group);
        }
    });

    // scroll to the given anchor
    const params = new URLSearchParams(location.search);
    const jumpTo = params.get('jumpto');
    if (jumpTo && jumpTo.length > 0) {
        const anchor = document.querySelector(`#${jumpTo}`);
        if (anchor) {
            zenscroll.to(anchor);
        }
    }

    initSelect2(body);
    initBootstrapToggle(body);

    $.datetimepicker.setLocale(language);

    $(".datepicker").datetimepicker({
        format: "Y-m-d",
        timepicker: false
    });

    $(".datetimepicker").prop("readonly", true);
    $(".datetimepicker").datetimepicker({
        format: "Y-m-d H:i",
        timepicker: true,
        step: 30
    });

    // User has to confirm logout
    $("a.backend-menu-item-logout").click((event) => {
        event.preventDefault();
        $(".mainmenu").hide();
        const url = $(event.target).attr("href");
        bootbox.confirm(`${MenuTranslation.Logout}?`, (result) => {
            if (result) {
                location.href = url;
            }
        });
    });

    // show a scroll-to-top arrow
    // if the scroll viewport isn't at top of the page
    $(window).scroll(() => {
        if ($(window).scrollTop() > 0) {
            $("#scroll-to-top").fadeIn();
        } else {
            $("#scroll-to-top").fadeOut();
        }
    });

    // scroll to top arrow at bottom right
    $("#scroll-to-top").click((event) => {
        event.preventDefault();
        event.stopPropagation();
        zenscroll.toY(0);
    });

});<|endoftext|>


<commit_before>(function(global) {

  'use strict';

  var fabric = global.fabric || (global.fabric = {}),
      clone  = fabric.util.object.clone;

  /**
   * Textbox class, based on IText, allows the user to resize the text rectangle
   * and wraps lines automatically. Textboxes have their Y scaling locked, the
   * user can only change width. Height is adjusted automatically based on the
   * wrapping of lines.
   * @class fabric.Textbox
   * @extends fabric.IText
   * @mixes fabric.Observable
   * @return {fabric.Textbox} thisArg
   * @see {@link fabric.Textbox#initialize} for constructor definition
   */
  fabric.Textbox = fabric.util.createClass(fabric.IText, fabric.Observable, {
    /**
     * Type of an object
     * @type String
     * @default
     */
    type: 'textbox',
    /**
     * Minimum width of textbox, in pixels.
     * @type Number
     * @default
     */
    minWidth: 20,
    /**
     * Minimum calculated width of a textbox, in pixels.
     * @type Number
     * @default
     */
    dynamicMinWidth: 0,
    /**
     * Cached array of text wrapping.
     * @type Array
     */
    __cachedLines: null,
    /**
     * Constructor. Some scaling related property values are forced. Visibility
     * of controls is also fixed; only the rotation and width controls are
     * made available.
     * @param {String} text Text string
     * @param {Object} [options] Options object
     * @return {fabric.Textbox} thisArg
     */
    initialize: function(text, options) {
      this.ctx = fabric.util.createCanvasElement().getContext('2d');

      this.callSuper('initialize', text, options);
      this.set({
        lockUniScaling: false,
        lockScalingY: true,
        lockScalingFlip: true,
        hasBorders: true
      });
      this.setControlsVisibility(fabric.Textbox.getTextboxControlVisibility());

      // add width to this list of props that effect line wrapping.
      this._dimensionAffectingProps.width = true;
    },

    /**
     * Unlike superclass's version of this function, Textbox does not update
     * its width.
     * @param {CanvasRenderingContext2D} ctx Context to use for measurements
     * @private
     * @override
     */
    _initDimensions: function(ctx) {
      if (this.__skipDimension) {
        return;
      }

      if (!ctx) {
        ctx = fabric.util.createCanvasElement().getContext('2d');
        this._setTextStyles(ctx);
      }

      // clear dynamicMinWidth as it will be different after we re-wrap line
      this.dynamicMinWidth = 0;

      // wrap lines
      this._textLines = this._splitTextIntoLines();

      // if after wrapping, the width is smaller than dynamicMinWidth, change the width and re-wrap
      if (this.dynamicMinWidth > this.width) {
        this._set('width', this.dynamicMinWidth);
      }

      // clear cache and re-calculate height
      this._clearCache();
      this.height = this._getTextHeight(ctx);
    },

    /**
     * Generate an object that translates the style object so that it is
     * broken up by visual lines (new lines and automatic wrapping).
     * The original text styles object is broken up by actual lines (new lines only),
     * which is only sufficient for Text / IText
     * @private
     */
    _generateStyleMap: function() {
      var realLineCount     = 0,
          realLineCharCount = 0,
          charCount         = 0,
          map               = {};

      for (var i = 0; i < this._textLines.length; i++) {
        if (this.text[charCount] === '\n') {
          realLineCharCount = 0;
          charCount++;
          realLineCount++;
        }
        else if (this.text[charCount] === ' ') {
          // this case deals with space's that are removed from end of lines when wrapping
          realLineCharCount++;
          charCount++;
        }

        map[i] = { line: realLineCount, offset: realLineCharCount };

        charCount += this._textLines[i].length;
        realLineCharCount += this._textLines[i].length;
      }

      return map;
    },

    /**
     * @param {Number} lineIndex
     * @param {Number} charIndex
     * @param {Boolean} [returnCloneOrEmpty=false]
     * @private
     */
    _getStyleDeclaration: function(lineIndex, charIndex, returnCloneOrEmpty) {
      if (this._styleMap) {
        var map = this._styleMap[lineIndex];
        if (!map) {
          return returnCloneOrEmpty ? { } : null;
        }
        lineIndex = map.line;
        charIndex = map.offset + charIndex;
      }
      return this.callSuper('_getStyleDeclaration', lineIndex, charIndex, returnCloneOrEmpty);
    },

    /**
     * @param {Number} lineIndex
     * @param {Number} charIndex
     * @param {Object} style
     * @private
     */
    _setStyleDeclaration: function(lineIndex, charIndex, style) {
      var map = this._styleMap[lineIndex];
      lineIndex = map.line;
      charIndex = map.offset + charIndex;

      this.styles[lineIndex][charIndex] = style;
    },

    /**
     * @param {Number} lineIndex
     * @param {Number} charIndex
     * @private
     */
    _deleteStyleDeclaration: function(lineIndex, charIndex) {
      var map = this._styleMap[lineIndex];
      lineIndex = map.line;
      charIndex = map.offset + charIndex;

      delete this.styles[lineIndex][charIndex];
    },

    /**
     * @param {Number} lineIndex
     * @private
     */
    _getLineStyle: function(lineIndex) {
      var map = this._styleMap[lineIndex];
      return this.styles[map.line];
    },

    /**
     * @param {Number} lineIndex
     * @param {Object} style
     * @private
     */
    _setLineStyle: function(lineIndex, style) {
      var map = this._styleMap[lineIndex];
      this.styles[map.line] = style;
    },

    /**
     * @param {Number} lineIndex
     * @private
     */
    _deleteLineStyle: function(lineIndex) {
      var map = this._styleMap[lineIndex];
      delete this.styles[map.line];
    },

    /**
     * Wraps text using the 'width' property of Textbox. First this function
     * splits text on newlines, so we preserve newlines entered by the user.
     * Then it wraps each line using the width of the Textbox by calling
     * _wrapLine().
     * @param {CanvasRenderingContext2D} ctx Context to use for measurements
     * @param {String} text The string of text that is split into lines
     * @returns {Array} Array of lines
     */
    _wrapText: function(ctx, text) {
      var lines = text.split(this._reNewline), wrapped = [], i;

      for (i = 0; i < lines.length; i++) {
        wrapped = wrapped.concat(this._wrapLine(ctx, lines[i], i));
      }

      return wrapped;
    },

    /**
     * Helper function to measure a string of text, given its lineIndex and charIndex offset
     *
     * @param {CanvasRenderingContext2D} ctx
     * @param {String} text
     * @param {number} lineIndex
     * @param {number} charOffset
     * @returns {number}
     * @private
     */
    _measureText: function(ctx, text, lineIndex, charOffset) {
      var width = 0;
      charOffset = charOffset || 0;

      for (var i = 0, len = text.length; i < len; i++) {
        width += this._getWidthOfChar(ctx, text[i], lineIndex, i + charOffset);
      }

      return width;
    },

    /**
     * Wraps a line of text using the width of the Textbox and a context.
     * @param {CanvasRenderingContext2D} ctx Context to use for measurements
     * @param {String} text The string of text to split into lines
     * @param {Number} lineIndex
     * @returns {Array} Array of line(s) into which the given text is wrapped
     * to.
     */
    _wrapLine: function(ctx, text, lineIndex) {
      var lineWidth        = 0,
          lines            = [],
          line             = '',
          words            = text.split(' '),
          word             = '',
          offset           = 0,
          infix            = ' ',
          wordWidth        = 0,
          infixWidth       = 0,
          largestWordWidth = 0;

      for (var i = 0; i < words.length; i++) {
        word = words[0];
        wordWidth = this._measureText(ctx, word, lineIndex, offset);
        offset += word.length;

        lineWidth += infixWidth + wordWidth;

        if (lineWidth >= this.width && line !== '') {
          lines.push(line);
          line = '';
          lineWidth = wordWidth;
        }

        if (line !== '' || i === 1) {
          line += infix;
        }
        line += word;

        infixWidth = this._measureText(ctx, infix, lineIndex, offset);
        offset++;

        // keep track of largest word
        if (wordWidth > largestWordWidth) {
          largestWordWidth = wordWidth;
        }
      }

      i && lines.push(line);

      if (largestWordWidth > this.dynamicMinWidth) {
        this.dynamicMinWidth = largestWordWidth;
      }

      return lines;
    },

    /**
     * Gets lines of text to render in the Textbox. This function calculates
     * text wrapping on the fly everytime it is called.
     * @returns {Array} Array of lines in the Textbox.
     * @override
     */
    _splitTextIntoLines: function() {
      var originalAlign = this.textAlign;
      this.ctx.save();
      this._setTextStyles(this.ctx);
      this.textAlign = 'left';
      var lines = this._wrapText(this.ctx, this.text);
      this.textAlign = originalAlign;
      this.ctx.restore();
      this._textLines = lines;
      this._styleMap = this._generateStyleMap();
      return lines;
    },

    /**
     * When part of a group, we don't want the Textbox's scale to increase if
     * the group's increases. That's why we reduce the scale of the Textbox by
     * the amount that the group's increases. This is to maintain the effective
     * scale of the Textbox at 1, so that font-size values make sense. Otherwise
     * the same font-size value would result in different actual size depending
     * on the value of the scale.
     * @param {String} key
     * @param {Any} value
     */
    setOnGroup: function(key, value) {
      if (key === 'scaleX') {
        this.set('scaleX', Math.abs(1 / value));
        this.set('width', (this.get('width') * value) /
          (typeof this.__oldScaleX === 'undefined' ? 1 : this.__oldScaleX));
        this.__oldScaleX = value;
      }
    },

    /**
     * Returns 2d representation (lineIndex and charIndex) of cursor (or selection start).
     * Overrides the superclass function to take into account text wrapping.
     *
     * @param {Number} [selectionStart] Optional index. When not given, current selectionStart is used.
     */
    get2DCursorLocation: function(selectionStart) {
      if (typeof selectionStart === 'undefined') {
        selectionStart = this.selectionStart;
      }

      var numLines = this._textLines.length,
          removed  = 0;

      for (var i = 0; i < numLines; i++) {
        var line    = this._textLines[i],
            lineLen = line.length;

        if (selectionStart <= removed + lineLen) {
          return {
            lineIndex: i,
            charIndex: selectionStart - removed
          };
        }

        removed += lineLen;

        if (this.text[removed] === '\n' || this.text[removed] === ' ') {
          removed++;
        }
      }

      return {
        lineIndex: numLines - 1,
        charIndex: this._textLines[numLines - 1].length
      };
    },

    /**
     * Overrides superclass function and uses text wrapping data to get cursor
     * boundary offsets instead of the array of chars.
     * @param {Array} chars Unused
     * @param {String} typeOfBoundaries Can be 'cursor' or 'selection'
     * @returns {Object} Object with 'top', 'left', and 'lineLeft' properties set.
     */
    _getCursorBoundariesOffsets: function(chars, typeOfBoundaries) {
      var topOffset      = 0,
          leftOffset     = 0,
          cursorLocation = this.get2DCursorLocation(),
          lineChars      = this._textLines[cursorLocation.lineIndex].split(''),
          lineLeftOffset = this._getLineLeftOffset(this._getLineWidth(this.ctx, cursorLocation.lineIndex));

      for (var i = 0; i < cursorLocation.charIndex; i++) {
        leftOffset += this._getWidthOfChar(this.ctx, lineChars[i], cursorLocation.lineIndex, i);
      }

      for (i = 0; i < cursorLocation.lineIndex; i++) {
        topOffset += this._getHeightOfLine(this.ctx, i);
      }

      if (typeOfBoundaries === 'cursor') {
        topOffset += (1 - this._fontSizeFraction) * this._getHeightOfLine(this.ctx, cursorLocation.lineIndex)
          / this.lineHeight - this.getCurrentCharFontSize(cursorLocation.lineIndex, cursorLocation.charIndex)
          * (1 - this._fontSizeFraction);
      }

      return {
        top: topOffset,
        left: leftOffset,
        lineLeft: lineLeftOffset
      };
    },

    getMinWidth: function() {
      return Math.max(this.minWidth, this.dynamicMinWidth);
    },

    /**
     * Returns object representation of an instance
     * @method toObject
     * @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output
     * @return {Object} object representation of an instance
     */
    toObject: function(propertiesToInclude) {
      return fabric.util.object.extend(this.callSuper('toObject', propertiesToInclude), {
        minWidth: this.minWidth
      });
    }
  });
  /**
   * Returns fabric.Textbox instance from an object representation
   * @static
   * @memberOf fabric.Textbox
   * @param {Object} object Object to create an instance from
   * @return {fabric.Textbox} instance of fabric.Textbox
   */
  fabric.Textbox.fromObject = function(object) {
    return new fabric.Textbox(object.text, clone(object));
  };
  /**
   * Returns the default controls visibility required for Textboxes.
   * @returns {Object}
   */
  fabric.Textbox.getTextboxControlVisibility = function() {
    return {
      tl: false,
      tr: false,
      br: false,
      bl: false,
      ml: true,
      mt: false,
      mr: true,
      mb: false,
      mtr: true
    };
  };
  /**
   * Contains all fabric.Textbox objects that have been created
   * @static
   * @memberOf fabric.Textbox
   * @type Array
   */
  fabric.Textbox.instances = [];
})(typeof exports !== 'undefined' ? exports : this);
<commit_msg>Update textbox.class.js<commit_after>(function(global) {

  'use strict';

  var fabric = global.fabric || (global.fabric = {}),
      clone  = fabric.util.object.clone;

  /**
   * Textbox class, based on IText, allows the user to resize the text rectangle
   * and wraps lines automatically. Textboxes have their Y scaling locked, the
   * user can only change width. Height is adjusted automatically based on the
   * wrapping of lines.
   * @class fabric.Textbox
   * @extends fabric.IText
   * @mixes fabric.Observable
   * @return {fabric.Textbox} thisArg
   * @see {@link fabric.Textbox#initialize} for constructor definition
   */
  fabric.Textbox = fabric.util.createClass(fabric.IText, fabric.Observable, {
    /**
     * Type of an object
     * @type String
     * @default
     */
    type: 'textbox',
    /**
     * Minimum width of textbox, in pixels.
     * @type Number
     * @default
     */
    minWidth: 20,
    /**
     * Minimum calculated width of a textbox, in pixels.
     * @type Number
     * @default
     */
    dynamicMinWidth: 0,
    /**
     * Cached array of text wrapping.
     * @type Array
     */
    __cachedLines: null,
    /**
     * Constructor. Some scaling related property values are forced. Visibility
     * of controls is also fixed; only the rotation and width controls are
     * made available.
     * @param {String} text Text string
     * @param {Object} [options] Options object
     * @return {fabric.Textbox} thisArg
     */
    initialize: function(text, options) {
      this.ctx = fabric.util.createCanvasElement().getContext('2d');

      this.callSuper('initialize', text, options);
      this.set({
        lockUniScaling: false,
        lockScalingY: true,
        lockScalingFlip: true,
        hasBorders: true
      });
      this.setControlsVisibility(fabric.Textbox.getTextboxControlVisibility());

      // add width to this list of props that effect line wrapping.
      this._dimensionAffectingProps.width = true;
    },

    /**
     * Unlike superclass's version of this function, Textbox does not update
     * its width.
     * @param {CanvasRenderingContext2D} ctx Context to use for measurements
     * @private
     * @override
     */
    _initDimensions: function(ctx) {
      if (this.__skipDimension) {
        return;
      }

      if (!ctx) {
        ctx = fabric.util.createCanvasElement().getContext('2d');
        this._setTextStyles(ctx);
      }

      // clear dynamicMinWidth as it will be different after we re-wrap line
      this.dynamicMinWidth = 0;

      // wrap lines
      this._textLines = this._splitTextIntoLines();

      // if after wrapping, the width is smaller than dynamicMinWidth, change the width and re-wrap
      if (this.dynamicMinWidth > this.width) {
        this._set('width', this.dynamicMinWidth);
      }

      // clear cache and re-calculate height
      this._clearCache();
      this.height = this._getTextHeight(ctx);
    },

    /**
     * Generate an object that translates the style object so that it is
     * broken up by visual lines (new lines and automatic wrapping).
     * The original text styles object is broken up by actual lines (new lines only),
     * which is only sufficient for Text / IText
     * @private
     */
    _generateStyleMap: function() {
      var realLineCount     = 0,
          realLineCharCount = 0,
          charCount         = 0,
          map               = {};

      for (var i = 0; i < this._textLines.length; i++) {
        if (this.text[charCount] === '\n') {
          realLineCharCount = 0;
          charCount++;
          realLineCount++;
        }
        else if (this.text[charCount] === ' ') {
          // this case deals with space's that are removed from end of lines when wrapping
          realLineCharCount++;
          charCount++;
        }

        map[i] = { line: realLineCount, offset: realLineCharCount };

        charCount += this._textLines[i].length;
        realLineCharCount += this._textLines[i].length;
      }

      return map;
    },

    /**
     * @param {Number} lineIndex
     * @param {Number} charIndex
     * @param {Boolean} [returnCloneOrEmpty=false]
     * @private
     */
    _getStyleDeclaration: function(lineIndex, charIndex, returnCloneOrEmpty) {
      if (this._styleMap) {
        var map = this._styleMap[lineIndex];
        if (!map) {
          return returnCloneOrEmpty ? { } : null;
        }
        lineIndex = map.line;
        charIndex = map.offset + charIndex;
      }
      return this.callSuper('_getStyleDeclaration', lineIndex, charIndex, returnCloneOrEmpty);
    },

    /**
     * @param {Number} lineIndex
     * @param {Number} charIndex
     * @param {Object} style
     * @private
     */
    _setStyleDeclaration: function(lineIndex, charIndex, style) {
      var map = this._styleMap[lineIndex];
      lineIndex = map.line;
      charIndex = map.offset + charIndex;

      this.styles[lineIndex][charIndex] = style;
    },

    /**
     * @param {Number} lineIndex
     * @param {Number} charIndex
     * @private
     */
    _deleteStyleDeclaration: function(lineIndex, charIndex) {
      var map = this._styleMap[lineIndex];
      lineIndex = map.line;
      charIndex = map.offset + charIndex;

      delete this.styles[lineIndex][charIndex];
    },

    /**
     * @param {Number} lineIndex
     * @private
     */
    _getLineStyle: function(lineIndex) {
      var map = this._styleMap[lineIndex];
      return this.styles[map.line];
    },

    /**
     * @param {Number} lineIndex
     * @param {Object} style
     * @private
     */
    _setLineStyle: function(lineIndex, style) {
      var map = this._styleMap[lineIndex];
      this.styles[map.line] = style;
    },

    /**
     * @param {Number} lineIndex
     * @private
     */
    _deleteLineStyle: function(lineIndex) {
      var map = this._styleMap[lineIndex];
      delete this.styles[map.line];
    },

    /**
     * Wraps text using the 'width' property of Textbox. First this function
     * splits text on newlines, so we preserve newlines entered by the user.
     * Then it wraps each line using the width of the Textbox by calling
     * _wrapLine().
     * @param {CanvasRenderingContext2D} ctx Context to use for measurements
     * @param {String} text The string of text that is split into lines
     * @returns {Array} Array of lines
     */
    _wrapText: function(ctx, text) {
      var lines = text.split(this._reNewline), wrapped = [], i;

      for (i = 0; i < lines.length; i++) {
        wrapped = wrapped.concat(this._wrapLine(ctx, lines[i], i));
      }

      return wrapped;
    },

    /**
     * Helper function to measure a string of text, given its lineIndex and charIndex offset
     *
     * @param {CanvasRenderingContext2D} ctx
     * @param {String} text
     * @param {number} lineIndex
     * @param {number} charOffset
     * @returns {number}
     * @private
     */
    _measureText: function(ctx, text, lineIndex, charOffset) {
      var width = 0;
      charOffset = charOffset || 0;

      for (var i = 0, len = text.length; i < len; i++) {
        width += this._getWidthOfChar(ctx, text[i], lineIndex, i + charOffset);
      }

      return width;
    },

    /**
     * Wraps a line of text using the width of the Textbox and a context.
     * @param {CanvasRenderingContext2D} ctx Context to use for measurements
     * @param {String} text The string of text to split into lines
     * @param {Number} lineIndex
     * @returns {Array} Array of line(s) into which the given text is wrapped
     * to.
     */
    _wrapLine: function(ctx, text, lineIndex) {
      var lineWidth        = 0,
          lines            = [],
          line             = '',
          words            = text.split(' '),
          word             = '',
          offset           = 0,
          infix            = ' ',
          wordWidth        = 0,
          infixWidth       = 0,
          largestWordWidth = 0;

      for (var i = 0; i < words.length; i++) {
        word = words[i];
        wordWidth = this._measureText(ctx, word, lineIndex, offset);
        offset += word.length;

        lineWidth += infixWidth + wordWidth;

        if (lineWidth >= this.width && line !== '') {
          lines.push(line);
          line = '';
          lineWidth = wordWidth;
        }

        if (line !== '' || i === 1) {
          line += infix;
        }
        line += word;

        infixWidth = this._measureText(ctx, infix, lineIndex, offset);
        offset++;

        // keep track of largest word
        if (wordWidth > largestWordWidth) {
          largestWordWidth = wordWidth;
        }
      }

      i && lines.push(line);

      if (largestWordWidth > this.dynamicMinWidth) {
        this.dynamicMinWidth = largestWordWidth;
      }

      return lines;
    },

    /**
     * Gets lines of text to render in the Textbox. This function calculates
     * text wrapping on the fly everytime it is called.
     * @returns {Array} Array of lines in the Textbox.
     * @override
     */
    _splitTextIntoLines: function() {
      var originalAlign = this.textAlign;
      this.ctx.save();
      this._setTextStyles(this.ctx);
      this.textAlign = 'left';
      var lines = this._wrapText(this.ctx, this.text);
      this.textAlign = originalAlign;
      this.ctx.restore();
      this._textLines = lines;
      this._styleMap = this._generateStyleMap();
      return lines;
    },

    /**
     * When part of a group, we don't want the Textbox's scale to increase if
     * the group's increases. That's why we reduce the scale of the Textbox by
     * the amount that the group's increases. This is to maintain the effective
     * scale of the Textbox at 1, so that font-size values make sense. Otherwise
     * the same font-size value would result in different actual size depending
     * on the value of the scale.
     * @param {String} key
     * @param {Any} value
     */
    setOnGroup: function(key, value) {
      if (key === 'scaleX') {
        this.set('scaleX', Math.abs(1 / value));
        this.set('width', (this.get('width') * value) /
          (typeof this.__oldScaleX === 'undefined' ? 1 : this.__oldScaleX));
        this.__oldScaleX = value;
      }
    },

    /**
     * Returns 2d representation (lineIndex and charIndex) of cursor (or selection start).
     * Overrides the superclass function to take into account text wrapping.
     *
     * @param {Number} [selectionStart] Optional index. When not given, current selectionStart is used.
     */
    get2DCursorLocation: function(selectionStart) {
      if (typeof selectionStart === 'undefined') {
        selectionStart = this.selectionStart;
      }

      var numLines = this._textLines.length,
          removed  = 0;

      for (var i = 0; i < numLines; i++) {
        var line    = this._textLines[i],
            lineLen = line.length;

        if (selectionStart <= removed + lineLen) {
          return {
            lineIndex: i,
            charIndex: selectionStart - removed
          };
        }

        removed += lineLen;

        if (this.text[removed] === '\n' || this.text[removed] === ' ') {
          removed++;
        }
      }

      return {
        lineIndex: numLines - 1,
        charIndex: this._textLines[numLines - 1].length
      };
    },

    /**
     * Overrides superclass function and uses text wrapping data to get cursor
     * boundary offsets instead of the array of chars.
     * @param {Array} chars Unused
     * @param {String} typeOfBoundaries Can be 'cursor' or 'selection'
     * @returns {Object} Object with 'top', 'left', and 'lineLeft' properties set.
     */
    _getCursorBoundariesOffsets: function(chars, typeOfBoundaries) {
      var topOffset      = 0,
          leftOffset     = 0,
          cursorLocation = this.get2DCursorLocation(),
          lineChars      = this._textLines[cursorLocation.lineIndex].split(''),
          lineLeftOffset = this._getLineLeftOffset(this._getLineWidth(this.ctx, cursorLocation.lineIndex));

      for (var i = 0; i < cursorLocation.charIndex; i++) {
        leftOffset += this._getWidthOfChar(this.ctx, lineChars[i], cursorLocation.lineIndex, i);
      }

      for (i = 0; i < cursorLocation.lineIndex; i++) {
        topOffset += this._getHeightOfLine(this.ctx, i);
      }

      if (typeOfBoundaries === 'cursor') {
        topOffset += (1 - this._fontSizeFraction) * this._getHeightOfLine(this.ctx, cursorLocation.lineIndex)
          / this.lineHeight - this.getCurrentCharFontSize(cursorLocation.lineIndex, cursorLocation.charIndex)
          * (1 - this._fontSizeFraction);
      }

      return {
        top: topOffset,
        left: leftOffset,
        lineLeft: lineLeftOffset
      };
    },

    getMinWidth: function() {
      return Math.max(this.minWidth, this.dynamicMinWidth);
    },

    /**
     * Returns object representation of an instance
     * @method toObject
     * @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output
     * @return {Object} object representation of an instance
     */
    toObject: function(propertiesToInclude) {
      return fabric.util.object.extend(this.callSuper('toObject', propertiesToInclude), {
        minWidth: this.minWidth
      });
    }
  });
  /**
   * Returns fabric.Textbox instance from an object representation
   * @static
   * @memberOf fabric.Textbox
   * @param {Object} object Object to create an instance from
   * @return {fabric.Textbox} instance of fabric.Textbox
   */
  fabric.Textbox.fromObject = function(object) {
    return new fabric.Textbox(object.text, clone(object));
  };
  /**
   * Returns the default controls visibility required for Textboxes.
   * @returns {Object}
   */
  fabric.Textbox.getTextboxControlVisibility = function() {
    return {
      tl: false,
      tr: false,
      br: false,
      bl: false,
      ml: true,
      mt: false,
      mr: true,
      mb: false,
      mtr: true
    };
  };
  /**
   * Contains all fabric.Textbox objects that have been created
   * @static
   * @memberOf fabric.Textbox
   * @type Array
   */
  fabric.Textbox.instances = [];
})(typeof exports !== 'undefined' ? exports : this);
<|endoftext|>


<commit_before>/* global angular */
(function () {
    "use strict";
    var app = angular.module("helloApp.controllers", []);

    var controller = function ($scope, $http, $log) {
        $scope.doSignup = function (model) {
            $http.post("/signup", model)
                .then(function(data) {
                    $scope.allsOk = "yes";
                });
        }

        $scope.testFunction = function () {
            return true;
        };
    }
    controller.$inject = ["$scope", "$http", "$log"];
    app.controller("signUpCtrl", controller);
})();

<commit_msg>Futher code cleanup<commit_after>/* global angular */
(function () {
    "use strict";
    angular
        .module("helloApp.controllers", [])
        .controller("signUpCtrl", signUpController);

    signUpController.$inject = ["$scope", "$http", "$log"];
    function signUpController ($scope, $http, $log) {
        $scope.doSignup = function (model) {
            $http.post("/signup", model)
                .then(function(data) {
                    $scope.allsOk = "yes";
                });
        }

        $scope.testFunction = function () {
            return true;
        };
    }
})();<|endoftext|>


<commit_before>/*!
 * jQuery ajaxProgress Plugin v0.5.0
 * Requires jQuery v1.5.0 or later
 *
 * http://www.kpozin.net/ajaxprogress
 *
 * (c) 2011, Konstantin Pozin
 * Licensed under MIT license (https://github.com/kpozin/jquery-ajaxprogress/raw/master/MIT-LICENSE.txt)
 */
(function($) {

    // Test whether onprogress is supported
    var support = $.support.ajaxProgress = ("onprogress" in (new $.ajaxSettings.xhr()));

    // If it's not supported, we can't do anything
    if (!support) {
        return;
    }

    var NAMESPACE = ".ajaxprogress";

    // Create global "ajaxProgress" event
    $.fn.ajaxProgress = function (f) {
        return this.bind("ajaxProgress", f);
    };

    // Hold on to a reference to the jqXHR object so that we can pass it to the progress callback.
    // Namespacing the handler with ".ajaxprogress"
    $("html").bind("ajaxSend" + NAMESPACE, function(event, jqXHR, ajaxOptions) {
        ajaxOptions.__jqXHR = jqXHR;
    });

    /**
     * @param {XMLHttpRequestProgressEvent} evt
     * @param {Object} options jQuery AJAX options
     */
    function handleOnProgress(evt, options) {

        // Trigger the global event.
        // function handler(jqEvent, progressEvent, jqXHR) {}
        if (options.global) {
            $.event.trigger("ajaxProgress", [evt, options.__jqXHR]);
        }

        // Trigger the local event.
        // function handler(jqXHR, progressEvent)
        if (typeof options.progress === "function") {
            options.progress(options.__jqXHR, evt);
        }
    }

    // We'll work with the original factory method just in case
    var makeOriginalXhr = $.ajaxSettings.xhr.bind($.ajaxSettings);

    // Options to be passed into $.ajaxSetup;
    var newOptions = {};

    // Wrap the XMLHttpRequest factory method
    newOptions.xhr = function () {

        // Reference to the extended options object
        var s = this;

        var newXhr = makeOriginalXhr();
        if (newXhr) {
            newXhr.addEventListener("progress", function(evt) {
                handleOnProgress(evt, s);
            });
        }
        return newXhr;
    };

    $.ajaxSetup(newOptions);

})(jQuery);<commit_msg>Fixed the test page and the handler interfaces. Wrote more of the README.<commit_after>/*!
 * jQuery ajaxProgress Plugin v0.5.0
 * Requires jQuery v1.5.0 or later
 *
 * http://www.kpozin.net/ajaxprogress
 *
 * (c) 2011, Konstantin Pozin
 * Licensed under MIT license.
 */
(function($) {

    // Test whether onprogress is supported
    var support = $.support.ajaxProgress = ("onprogress" in (new $.ajaxSettings.xhr()));

    // If it's not supported, we can't do anything
    if (!support) {
        return;
    }

    var NAMESPACE = ".ajaxprogress";

    // Create global "ajaxProgress" event
    $.fn.ajaxProgress = function (f) {
        return this.bind("ajaxProgress", f);
    };

    // Hold on to a reference to the jqXHR object so that we can pass it to the progress callback.
    // Namespacing the handler with ".ajaxprogress"
    $("html").bind("ajaxSend" + NAMESPACE, function(event, jqXHR, ajaxOptions) {
        ajaxOptions.__jqXHR = jqXHR;
    });

    /**
     * @param {XMLHttpRequestProgressEvent} evt
     * @param {Object} options jQuery AJAX options
     */
    function handleOnProgress(evt, options) {

        // Trigger the global event.
        // function handler(jqEvent, progressEvent, jqXHR) {}
        if (options.global) {
            $.event.trigger("ajaxProgress", [evt, options.__jqXHR]);
        }

        // Trigger the local event.
        // function handler(jqXHR, progressEvent)
        if (typeof options.progress === "function") {
            options.progress(options.__jqXHR, evt);
        }
    }

    // We'll work with the original factory method just in case
    var makeOriginalXhr = $.ajaxSettings.xhr.bind($.ajaxSettings);

    // Options to be passed into $.ajaxSetup;
    var newOptions = {};

    // Wrap the XMLHttpRequest factory method
    newOptions.xhr = function () {

        // Reference to the extended options object
        var s = this;

        var newXhr = makeOriginalXhr();
        if (newXhr) {
            newXhr.addEventListener("progress", function(evt) {
                handleOnProgress(evt, s);
            });
        }
        return newXhr;
    };

    $.ajaxSetup(newOptions);

})(jQuery);<|endoftext|>


<commit_before>(function ($) {

'use strict';

var CONTROLLER_KEY = 'columnfreeze-controller';

/* Controller constructor */

var Controller = function ($table) {
	this.$table = $table;
};

/* Controller static functions */

Controller.getElementController = function (el) {
	var controller = $(el).data(CONTROLLER_KEY);
	return controller || null;
};

Controller.setElementController = function (el, controller) {
	$(el).data(CONTROLLER_KEY, controller);
};

Controller.ensureElementController = function (el) {
	var $el = $(el);
	var controller = Controller.getElementController($el);
	if (!controller) {
		controller = new Controller($el);
		Controller.setElementController($el, controller);
	}
	return controller;
};

/* Controller methods */

var $Controller = Controller.prototype = {
	$table: null,
	$tableFixed: null,
	$tableScroll: null,
	$containerFixed: null,
	$containerScroll: null,
	$wrapper: null
};

$Controller.config = function (key, value) {
	var options;
	if (!this.settings) {
		this.settings = $.extend({}, $.fn.columnFreeze.defaults);
	}
	// no-op
	if (typeof key === 'undefined') {
		return;
	}
	if (typeof key === 'string') {
		// get operation
		if (arguments.length < 2) {
			return this.settings[key];
		}
		// set operation
		else {
			this.settings[key] = value;
			return;
		}
	}
	else if ($.isPlainObject(key)) {
		options = key;
		$.extend(this.settings, options);
	}
	else {
		throw new Error('Unsupported configuration arguments');
	}
};

$Controller.freeze = function () {
	this.unfreeze();
	this.pin();
	this.fixRowHeights();
};

$Controller.unfreeze = function () {
	if (this.$wrapper) {
		this.$table.insertBefore(this.$wrapper);
		this.$wrapper.remove();
		this.$wrapper = null;
	}
};

/*
	DOM tree (replaces original table)

	$wrapper
		$containerFixed
			$tableFixed
		$containerScroll
			$tableScroll
*/

$Controller.pin = function () {
	var index = this.config('index');
	var headerSelector = this.config('headerSelector');
	var tableWidth = 0;
	var $split = this.splitTable(index);
	this.$containerFixed = this.createContainerFixed();
	this.$containerScroll = this.createContainerScroll();
	this.$tableFixed = $split.eq(0).appendTo(this.$containerFixed);
	this.$tableScroll = $split.eq(1).appendTo(this.$containerScroll);
	if (this.config('scrollWidth') !== 'auto') {
		tableWidth = this.copyColumnWidths();
	}
	this.$tableFixed.width(this.config('fixedWidth'))
	this.$tableScroll.width(this.config('scrollWidth') || tableWidth + 1);
	this.$wrapper = this.createWrapper()
		.append(this.$containerFixed)
		.append(this.$containerScroll)
		.insertBefore(this.$table);
	this.$table.detach();
	this.$containerScroll.css('left', this.$containerFixed.width() + 'px');
};

$Controller.createContainerFixed = function () {
	return $(document.createElement('div'))
		.addClass(this.config('containerFixedClass'))
		.css(this.config('containerFixedCSS'));
};

$Controller.createContainerScroll = function () {
	return $(document.createElement('div'))
		.addClass(this.config('containerScrollClass'))
		.css(this.config('containerScrollCSS'));
};

$Controller.createWrapper = function () {
	return $(document.createElement('div'))
		.addClass(this.config('wrapperClass'))
		.css(this.config('wrapperCSS'));
};

$Controller.splitTable = function () {
	var index = this.config('index');
	return this.$table.tableSplit(index);
};

$Controller.copyColumnWidths = function () {
	var index = this.config('index');
	var headerSelector = this.config('headerSelector');
	var $headers = this.$table.find(headerSelector);
	var controller = this;
	var totalWidth = 0;
	$headers.each(function (i) {
		var $table, $clone;
		var $original = $(this);
		var width = $original.outerWidth();
		if (i < index) {
			$table = controller.$tableFixed;
		}
		else {
			$table = controller.$tableScroll;
			i -= index;
			totalWidth += width;
		}
		$clone = $table.find(headerSelector).eq(i);
		$clone.width(width);
	});
	return totalWidth;
};

$Controller.fixRowHeights = function () {
	var rowSelector = this.config('rowSelector');
	var $fixedRows = this.$tableFixed.find(rowSelector);
	var $scrollRows = this.$tableScroll.find(rowSelector);
	this.$table.find(rowSelector).each(function (i) {
		var $fixedRow = $fixedRows.eq(i);
		var $scrollRow = $scrollRows.eq(i);
		var max = Math.max($fixedRow.height(), $scrollRow.height());
		$fixedRow.height(max);
		$scrollRow.height(max);
	});
};

$Controller.isFrozen = function () {
	return this.$wrapper !== null;
};

/* Expose jQuery plugin */

var returnable = {
	isFrozen: true,
	config: true
};

$.fn.columnFreeze = function (action, options) {
	var args = Array.prototype.slice.call(arguments, 1);
	// the default action if none is specified
	if (typeof action !== 'string') {
		options = action;
		args.unshift(options);
		action = 'freeze';
	}
	var results = this.map(function () {
		// get controller if it exists, create otherwise
		var controller = Controller.ensureElementController(this);
		switch (action) {
			case 'freeze':
				controller.config(options);
				return controller.freeze();
			case 'unfreeze':
				controller.config(options);
				return controller.unfreeze();
			case 'isFrozen':
				return controller.isFrozen();
			case 'config':
				return $Controller.config.apply(controller, args);
			default:
				throw new Error('Unrecognized action "' + action + '"');
		}
	});
	if (action in returnable) {
		return results[0];
	}
	return this;
};

$.fn.columnFreeze.Controller = Controller;

$.fn.columnFreeze.defaults = {
	// table is split before the column at this index (0-based)
	index: 1,
	// container for fixed portion of table
	containerFixedClass: 'columnfreeze-container columnfreeze-container-fixed',
	containerFixedCSS: {
		display: 'inline-block',
		float: 'left'
	},
	// container for scrollable portion of table
	containerScrollClass: 'columnfreeze-container columnfreeze-container-scroll',
	containerScrollCSS: {
		display: 'inline-block',
		position: 'absolute',
		overflowX: 'scroll',
		right: 0
	},
	// wrapper for above containers
	wrapperClass: 'columnfreeze-wrapper',
	wrapperCSS: {
		position: 'relative',
		overflow: 'hidden',
		paddingBottom: '20px'
	},
	// used to determine rows (when fixing row heights)
	rowSelector: 'tr',
	// used to determine headers (when fixing col widths)
	headerSelector: 'thead tr:last-child th, thead tr:last-child td',
	// width of fixed section
	fixedWidth: null,
	// width of scrollable section
	scrollWidth: null
};

})(jQuery);<commit_msg>Added license info to plugin file<commit_after>/*!
 * jQuery column freeze plugin
 * @author Jamie Hoeks <j@skeoh.com>
 * @license MIT
 */
(function ($) {

'use strict';

var CONTROLLER_KEY = 'columnfreeze-controller';

/* Controller constructor */

var Controller = function ($table) {
	this.$table = $table;
};

/* Controller static functions */

Controller.getElementController = function (el) {
	var controller = $(el).data(CONTROLLER_KEY);
	return controller || null;
};

Controller.setElementController = function (el, controller) {
	$(el).data(CONTROLLER_KEY, controller);
};

Controller.ensureElementController = function (el) {
	var $el = $(el);
	var controller = Controller.getElementController($el);
	if (!controller) {
		controller = new Controller($el);
		Controller.setElementController($el, controller);
	}
	return controller;
};

/* Controller methods */

var $Controller = Controller.prototype = {
	$table: null,
	$tableFixed: null,
	$tableScroll: null,
	$containerFixed: null,
	$containerScroll: null,
	$wrapper: null
};

$Controller.config = function (key, value) {
	var options;
	if (!this.settings) {
		this.settings = $.extend({}, $.fn.columnFreeze.defaults);
	}
	// no-op
	if (typeof key === 'undefined') {
		return;
	}
	if (typeof key === 'string') {
		// get operation
		if (arguments.length < 2) {
			return this.settings[key];
		}
		// set operation
		else {
			this.settings[key] = value;
			return;
		}
	}
	else if ($.isPlainObject(key)) {
		options = key;
		$.extend(this.settings, options);
	}
	else {
		throw new Error('Unsupported configuration arguments');
	}
};

$Controller.freeze = function () {
	this.unfreeze();
	this.pin();
	this.fixRowHeights();
};

$Controller.unfreeze = function () {
	if (this.$wrapper) {
		this.$table.insertBefore(this.$wrapper);
		this.$wrapper.remove();
		this.$wrapper = null;
	}
};

/*
	DOM tree (replaces original table)

	$wrapper
		$containerFixed
			$tableFixed
		$containerScroll
			$tableScroll
*/

$Controller.pin = function () {
	var index = this.config('index');
	var headerSelector = this.config('headerSelector');
	var tableWidth = 0;
	var $split = this.splitTable(index);
	this.$containerFixed = this.createContainerFixed();
	this.$containerScroll = this.createContainerScroll();
	this.$tableFixed = $split.eq(0).appendTo(this.$containerFixed);
	this.$tableScroll = $split.eq(1).appendTo(this.$containerScroll);
	if (this.config('scrollWidth') !== 'auto') {
		tableWidth = this.copyColumnWidths();
	}
	this.$tableFixed.width(this.config('fixedWidth'))
	this.$tableScroll.width(this.config('scrollWidth') || tableWidth + 1);
	this.$wrapper = this.createWrapper()
		.append(this.$containerFixed)
		.append(this.$containerScroll)
		.insertBefore(this.$table);
	this.$table.detach();
	this.$containerScroll.css('left', this.$containerFixed.width() + 'px');
};

$Controller.createContainerFixed = function () {
	return $(document.createElement('div'))
		.addClass(this.config('containerFixedClass'))
		.css(this.config('containerFixedCSS'));
};

$Controller.createContainerScroll = function () {
	return $(document.createElement('div'))
		.addClass(this.config('containerScrollClass'))
		.css(this.config('containerScrollCSS'));
};

$Controller.createWrapper = function () {
	return $(document.createElement('div'))
		.addClass(this.config('wrapperClass'))
		.css(this.config('wrapperCSS'));
};

$Controller.splitTable = function () {
	var index = this.config('index');
	return this.$table.tableSplit(index);
};

$Controller.copyColumnWidths = function () {
	var index = this.config('index');
	var headerSelector = this.config('headerSelector');
	var $headers = this.$table.find(headerSelector);
	var controller = this;
	var totalWidth = 0;
	$headers.each(function (i) {
		var $table, $clone;
		var $original = $(this);
		var width = $original.outerWidth();
		if (i < index) {
			$table = controller.$tableFixed;
		}
		else {
			$table = controller.$tableScroll;
			i -= index;
			totalWidth += width;
		}
		$clone = $table.find(headerSelector).eq(i);
		$clone.width(width);
	});
	return totalWidth;
};

$Controller.fixRowHeights = function () {
	var rowSelector = this.config('rowSelector');
	var $fixedRows = this.$tableFixed.find(rowSelector);
	var $scrollRows = this.$tableScroll.find(rowSelector);
	this.$table.find(rowSelector).each(function (i) {
		var $fixedRow = $fixedRows.eq(i);
		var $scrollRow = $scrollRows.eq(i);
		var max = Math.max($fixedRow.height(), $scrollRow.height());
		$fixedRow.height(max);
		$scrollRow.height(max);
	});
};

$Controller.isFrozen = function () {
	return this.$wrapper !== null;
};

/* Expose jQuery plugin */

var returnable = {
	isFrozen: true,
	config: true
};

$.fn.columnFreeze = function (action, options) {
	var args = Array.prototype.slice.call(arguments, 1);
	// the default action if none is specified
	if (typeof action !== 'string') {
		options = action;
		args.unshift(options);
		action = 'freeze';
	}
	var results = this.map(function () {
		// get controller if it exists, create otherwise
		var controller = Controller.ensureElementController(this);
		switch (action) {
			case 'freeze':
				controller.config(options);
				return controller.freeze();
			case 'unfreeze':
				controller.config(options);
				return controller.unfreeze();
			case 'isFrozen':
				return controller.isFrozen();
			case 'config':
				return $Controller.config.apply(controller, args);
			default:
				throw new Error('Unrecognized action "' + action + '"');
		}
	});
	if (action in returnable) {
		return results[0];
	}
	return this;
};

$.fn.columnFreeze.Controller = Controller;

$.fn.columnFreeze.defaults = {
	// table is split before the column at this index (0-based)
	index: 1,
	// container for fixed portion of table
	containerFixedClass: 'columnfreeze-container columnfreeze-container-fixed',
	containerFixedCSS: {
		display: 'inline-block',
		float: 'left'
	},
	// container for scrollable portion of table
	containerScrollClass: 'columnfreeze-container columnfreeze-container-scroll',
	containerScrollCSS: {
		display: 'inline-block',
		position: 'absolute',
		overflowX: 'scroll',
		right: 0
	},
	// wrapper for above containers
	wrapperClass: 'columnfreeze-wrapper',
	wrapperCSS: {
		position: 'relative',
		overflow: 'hidden',
		paddingBottom: '20px'
	},
	// used to determine rows (when fixing row heights)
	rowSelector: 'tr',
	// used to determine headers (when fixing col widths)
	headerSelector: 'thead tr:last-child th, thead tr:last-child td',
	// width of fixed section
	fixedWidth: null,
	// width of scrollable section
	scrollWidth: null
};

})(jQuery);<|endoftext|>


<commit_before>/* autogenerated by the update_version script */
exports.version=1591123038;
<commit_msg>new version<commit_after>/* autogenerated by the update_version script */
exports.version=1591123518;
<|endoftext|>


<commit_before>/**
 * jquery.i18Now
 *
 * Version:     1.2.1
 * Last Update: 2012/07/15
 * Manuel Bitto (manuel.bitto@gmail.com)
 *
 *
 * changes log:
 *
 * version 1.1.0 -> Added fallback date for custom date
 * version 1.1.1 -> Added control for null value as custom date input
 * version 1.2.0 -> Added customizable AM / PM strings in options (ampm)
 * version 1.2.1 -> Corrected AM/PM 12 hour logic as in http://goo.gl/IUVXP
 *                  Added form inputs support
 *
 * This plugin is intended to help formatting date and time according to the user preferences
 * or the most used format in a specific country.
 * Here there are some examples of formatted date and times for different country and languages:
 *
 *
 * English (United States):     Wednesday, May 23, 2012 9:00:00 AM
 * English (United Kingdom):    Wednesday, 23 May 2012 09:00:00
 * German (Germany):            Mittwoch, 23. Mai 2012 09:00:00
 * Italian (Italy):             mercoledì 23 maggio 2012 09:00:00
 * French (France):             mercredi 23 mai 2012 09:00:00
 * Spanish (Spain):             miércoles 23 de mayo de 2012 09:00:00
 * Hebrew (Israel):             יום רביעי, 23 במאי 2012 09:00:00
 * Japanese (Japan):            2012年5月23日水曜日9時00分00秒
 * Chinese (China):             2012年5月23日星期午9时00分00秒
 * Russian (Russia):            среда, 23 мая 2012 г. 9:00:00
 *
 * See here for more examples: http://demo.icu-project.org/icu-bin/locexp/locexp
 *
 *
 * We use the same format characters of PHP (see: http://php.net/manual/en/function.date.php)
 *
 * j 	Day of the month without leading zeros 	                                1 to 31
 * d 	Day of the month, 2 digits with leading zeros 	                        01 to 31
 * l 	A full textual representation of the day of the week 	                Sunday through Saturday
 * D 	A textual representation of a day, three letters 	                    Sun through Sat
 * F 	A full textual representation of a month, such as January or March 	    January through December
 * M 	A short textual representation of a month, three letters 	            Jan through Dec
 * n	Numeric representation of a month, without leading zeros	            1 through 12
 * m 	Numeric representation of a month, with leading zeros 	                01 through 12
 * Y 	A full numeric representation of a year, 4 digits 	                    Examples: 1999 or 2003
 * y 	A two digit representation of a year 	                                Examples: 99 or 03
 * a 	Lowercase Ante meridiem and Post meridiem 	                            am or pm
 * A 	Uppercase Ante meridiem and Post meridiem 	                            AM or PM
 * g 	12-hour format of an hour without leading zeros 	                    1 through 12
 * G 	24-hour format of an hour without leading zeros 	                    0 through 23
 * h 	12-hour format of an hour with leading zeros 	                        01 through 12
 * H 	24-hour format of an hour with leading zeros 	                        00 through 23
 * i 	Minutes with leading zeros 	                                            00 to 59
 * s 	Seconds, with leading zeros 	                                        00 through 59
 *
 *
 */
(function($) {

    var init = function(options) {
        options = $.extend({
            // Names of weekdays and months (english - UK)
            D : ['Sun', 'Mon', 'Tue', 'Wen', 'Thu', 'Fri', 'Sat'],
            l : ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'],
            M : ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
            F : ['January', 'February', 'March', 'April', 'May', 'June', 'July',
                'August', 'September', 'October', 'November', 'December'],
            // Format for AM / PM (english - USA)
            ampm : ['AM', 'PM'],
            // Format as the default language (english - UK)
            format : "%l, %d %F %Y %H:%i:%s"
        }, options);

        // Initialize all i18Now elements
        return this.each(function(i, el){
            var $el = $(el);
            $el.data('i18Now_options', options);
            $el.text(parseFormat(options, new Date()));
            $el.val(parseFormat(options, new Date()));
        });
    };

    var parseFormat = function(options, date){
        var day = date.getDate(),
            month = date.getMonth(),
            hours24 = date.getHours(),
            hours12 = date.getHours() % 12,
            minutes = date.getMinutes(),
            seconds = date.getSeconds(),
            formatChars = options["format"].split("%"),
            substitute = '',
            parsedTimeString = '';

        if (hours12 === 0) {
            hours12 = 12;
        }

        if (hours12 === 0) {
            hours12 = 12;
        }

        for(var i=0; i<formatChars.length; i++){
            var formatChar = formatChars[i].charAt(0);
            switch(formatChar){
                case 'D': case 'l': substitute = options[formatChar][date.getDay()]; break;
                case 'M': case 'F': substitute = options[formatChar][month]; break;
                case 'n': substitute = month + 1; break;
                case 'm': substitute = setTwoDigits(month + 1); break;
                case 'j': substitute = day; break;
                case 'd': substitute = setTwoDigits(day); break;
                case 'Y': substitute = date.getFullYear(); break;
                case 'y': substitute = date.getFullYear().toString().substring(2); break;
                case 'a': substitute = (hours24 < 12) ? options['ampm'][0].toLowerCase() :
                                                         options['ampm'][1].toLowerCase() ; break;
                case 'A': substitute = (hours24 < 12) ? options['ampm'][0] : options['ampm'][1] ; break;
                case 'g': substitute = hours12; break;
                case 'G': substitute = hours24; break;
                case 'h': substitute = setTwoDigits(hours12); break;
                case 'H': substitute = setTwoDigits(hours24); break;
                case 'i': substitute = setTwoDigits(minutes); break;
                case 's': substitute = setTwoDigits(seconds); break;
                default : if(i!==0) substitute = '%'; substitute += formatChar;
            }
            parsedTimeString += formatChars[i].replace(formatChar, substitute);
        }
        return parsedTimeString;
    };

    var setTwoDigits = function(number){
        return (String(number).length === 1) ? '0' + number : number;
    };

    var publicMethods = {
        // Update date strings every n seconds
        update : function(every){
            every = every >= 1 ? parseInt(every) : 1;
            this.each(function(i, el){
                var $el = $(el);
                if($el.data('i18Now_interval') !== 'undefined'){
                    clearInterval($el.data('i18Now_interval'));
                }
                var interval = setInterval(function(){
                    $el.text(parseFormat($el.data('i18Now_options'), new Date()));
                    $el.val(parseFormat($el.data('i18Now_options'), new Date()));
                }, every * 1000);
                $el.data('i18Now_interval', interval);
            });
        },

        // Set a custom static date. Param must be a Date object
        setCustomDate : function(dateObj, fallback){
            if(typeof fallback === "undefined"){
                fallback = 'Invalid Date';
            }
            if(dateObj.getTime() === 0 || isNaN(dateObj.getTime())){
                this.text(fallback);
                this.val(fallback);
            }
            else{
                this.text(parseFormat(this.data('i18Now_options'), dateObj));
                this.val(parseFormat(this.data('i18Now_options'), dateObj));
            }
        }
    };

    // Plug i18Now in
    $.fn.i18Now = function(method){

        // We have a method like $('.class').plugin("doThis");
        if (publicMethods[method]) {
            return publicMethods[method].apply( this, Array.prototype.slice.call( arguments, 1 ));
        }
        // We have the initialization of plugin
        else if (typeof method === 'object' || ! method) {
            return init.apply(this, arguments);
        }
        // We've done something wrong here
        else {
            $.error('Method ' +  method + ' does not exist in i18Now plugin');
        }
    };
})(jQuery);
<commit_msg>Fixed erroneus double if statement<commit_after>/**
 * jquery.i18Now
 *
 * Version:     1.2.1
 * Last Update: 2012/07/15
 * Manuel Bitto (manuel.bitto@gmail.com)
 *
 *
 * changes log:
 *
 * version 1.1.0 -> Added fallback date for custom date
 * version 1.1.1 -> Added control for null value as custom date input
 * version 1.2.0 -> Added customizable AM / PM strings in options (ampm)
 * version 1.2.1 -> Corrected AM/PM 12 hour logic as in http://goo.gl/IUVXP
 *                  Added form inputs support
 *
 * This plugin is intended to help formatting date and time according to the user preferences
 * or the most used format in a specific country.
 * Here there are some examples of formatted date and times for different country and languages:
 *
 *
 * English (United States):     Wednesday, May 23, 2012 9:00:00 AM
 * English (United Kingdom):    Wednesday, 23 May 2012 09:00:00
 * German (Germany):            Mittwoch, 23. Mai 2012 09:00:00
 * Italian (Italy):             mercoledì 23 maggio 2012 09:00:00
 * French (France):             mercredi 23 mai 2012 09:00:00
 * Spanish (Spain):             miércoles 23 de mayo de 2012 09:00:00
 * Hebrew (Israel):             יום רביעי, 23 במאי 2012 09:00:00
 * Japanese (Japan):            2012年5月23日水曜日9時00分00秒
 * Chinese (China):             2012年5月23日星期午9时00分00秒
 * Russian (Russia):            среда, 23 мая 2012 г. 9:00:00
 *
 * See here for more examples: http://demo.icu-project.org/icu-bin/locexp/locexp
 *
 *
 * We use the same format characters of PHP (see: http://php.net/manual/en/function.date.php)
 *
 * j 	Day of the month without leading zeros 	                                1 to 31
 * d 	Day of the month, 2 digits with leading zeros 	                        01 to 31
 * l 	A full textual representation of the day of the week 	                Sunday through Saturday
 * D 	A textual representation of a day, three letters 	                    Sun through Sat
 * F 	A full textual representation of a month, such as January or March 	    January through December
 * M 	A short textual representation of a month, three letters 	            Jan through Dec
 * n	Numeric representation of a month, without leading zeros	            1 through 12
 * m 	Numeric representation of a month, with leading zeros 	                01 through 12
 * Y 	A full numeric representation of a year, 4 digits 	                    Examples: 1999 or 2003
 * y 	A two digit representation of a year 	                                Examples: 99 or 03
 * a 	Lowercase Ante meridiem and Post meridiem 	                            am or pm
 * A 	Uppercase Ante meridiem and Post meridiem 	                            AM or PM
 * g 	12-hour format of an hour without leading zeros 	                    1 through 12
 * G 	24-hour format of an hour without leading zeros 	                    0 through 23
 * h 	12-hour format of an hour with leading zeros 	                        01 through 12
 * H 	24-hour format of an hour with leading zeros 	                        00 through 23
 * i 	Minutes with leading zeros 	                                            00 to 59
 * s 	Seconds, with leading zeros 	                                        00 through 59
 *
 *
 */
(function($) {

    var init = function(options) {
        options = $.extend({
            // Names of weekdays and months (english - UK)
            D : ['Sun', 'Mon', 'Tue', 'Wen', 'Thu', 'Fri', 'Sat'],
            l : ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'],
            M : ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
            F : ['January', 'February', 'March', 'April', 'May', 'June', 'July',
                'August', 'September', 'October', 'November', 'December'],
            // Format for AM / PM (english - USA)
            ampm : ['AM', 'PM'],
            // Format as the default language (english - UK)
            format : "%l, %d %F %Y %H:%i:%s"
        }, options);

        // Initialize all i18Now elements
        return this.each(function(i, el){
            var $el = $(el);
            $el.data('i18Now_options', options);
            $el.text(parseFormat(options, new Date()));
            $el.val(parseFormat(options, new Date()));
        });
    };

    var parseFormat = function(options, date){
        var day = date.getDate(),
            month = date.getMonth(),
            hours24 = date.getHours(),
            hours12 = date.getHours() % 12,
            minutes = date.getMinutes(),
            seconds = date.getSeconds(),
            formatChars = options["format"].split("%"),
            substitute = '',
            parsedTimeString = '';

        if (hours12 === 0) {
            hours12 = 12;
        }

        for(var i=0; i<formatChars.length; i++){
            var formatChar = formatChars[i].charAt(0);
            switch(formatChar){
                case 'D': case 'l': substitute = options[formatChar][date.getDay()]; break;
                case 'M': case 'F': substitute = options[formatChar][month]; break;
                case 'n': substitute = month + 1; break;
                case 'm': substitute = setTwoDigits(month + 1); break;
                case 'j': substitute = day; break;
                case 'd': substitute = setTwoDigits(day); break;
                case 'Y': substitute = date.getFullYear(); break;
                case 'y': substitute = date.getFullYear().toString().substring(2); break;
                case 'a': substitute = (hours24 < 12) ? options['ampm'][0].toLowerCase() :
                                                         options['ampm'][1].toLowerCase() ; break;
                case 'A': substitute = (hours24 < 12) ? options['ampm'][0] : options['ampm'][1] ; break;
                case 'g': substitute = hours12; break;
                case 'G': substitute = hours24; break;
                case 'h': substitute = setTwoDigits(hours12); break;
                case 'H': substitute = setTwoDigits(hours24); break;
                case 'i': substitute = setTwoDigits(minutes); break;
                case 's': substitute = setTwoDigits(seconds); break;
                default : if(i!==0) substitute = '%'; substitute += formatChar;
            }
            parsedTimeString += formatChars[i].replace(formatChar, substitute);
        }
        return parsedTimeString;
    };

    var setTwoDigits = function(number){
        return (String(number).length === 1) ? '0' + number : number;
    };

    var publicMethods = {
        // Update date strings every n seconds
        update : function(every){
            every = every >= 1 ? parseInt(every) : 1;
            this.each(function(i, el){
                var $el = $(el);
                if($el.data('i18Now_interval') !== 'undefined'){
                    clearInterval($el.data('i18Now_interval'));
                }
                var interval = setInterval(function(){
                    $el.text(parseFormat($el.data('i18Now_options'), new Date()));
                    $el.val(parseFormat($el.data('i18Now_options'), new Date()));
                }, every * 1000);
                $el.data('i18Now_interval', interval);
            });
        },

        // Set a custom static date. Param must be a Date object
        setCustomDate : function(dateObj, fallback){
            if(typeof fallback === "undefined"){
                fallback = 'Invalid Date';
            }
            if(dateObj.getTime() === 0 || isNaN(dateObj.getTime())){
                this.text(fallback);
                this.val(fallback);
            }
            else{
                this.text(parseFormat(this.data('i18Now_options'), dateObj));
                this.val(parseFormat(this.data('i18Now_options'), dateObj));
            }
        }
    };

    // Plug i18Now in
    $.fn.i18Now = function(method){

        // We have a method like $('.class').plugin("doThis");
        if (publicMethods[method]) {
            return publicMethods[method].apply( this, Array.prototype.slice.call( arguments, 1 ));
        }
        // We have the initialization of plugin
        else if (typeof method === 'object' || ! method) {
            return init.apply(this, arguments);
        }
        // We've done something wrong here
        else {
            $.error('Method ' +  method + ' does not exist in i18Now plugin');
        }
    };
})(jQuery);
<|endoftext|>


<commit_before>/*

* Price Format jQuery Plugin
* Created By Eduardo Cuducos cuducos [at] gmail [dot] com
* Currently maintained by Flavio Silveira flavio [at] gmail [dot] com
* Version: 1.5
* Release: 2011-06-27

* original char limit by Flavio Silveira <http://flaviosilveira.com>
* original keydown event attachment by Kaihua Qi
* keydown fixes by Thasmo <http://thasmo.com>
* Clear Prefix on Blur suggest by Ricardo Mendes from PhonoWay
* original allow negative by Cagdas Ucar <http://carsinia.com>
* keypad fixes by Carlos Vinicius <http://www.kvinicius.com.br> and Rayron Victor

*/

(function($) {

	$.fn.priceFormat = function(options)
	{

		var defaults =
		{
			prefix: 'US$ ',
			centsSeparator: '.',
			thousandsSeparator: ',',
			limit: false,
			centsLimit: 2,
			clearPrefix: false,
			allowNegative: false
		};

		var options = $.extend(defaults, options);

		return this.each(function()
		{

			// pre defined options
			var obj = $(this);
			var is_number = /[0-9]/;

			// load the pluggings settings
			var prefix = options.prefix;
			var centsSeparator = options.centsSeparator;
			var thousandsSeparator = options.thousandsSeparator;
			var limit = options.limit;
			var centsLimit = options.centsLimit;
			var clearPrefix = options.clearPrefix;
			var allowNegative = options.allowNegative;

			// skip everything that isn't a number
			// and also skip the left zeroes
			function to_numbers (str)
			{
				var formatted = '';
				for (var i=0;i<(str.length);i++)
				{
					char_ = str.charAt(i);
					if (formatted.length==0 && char_==0) char_ = false;

					if (char_ && char_.match(is_number))
					{
						if (limit)
						{
							if (formatted.length < limit) formatted = formatted+char_;
						}
						else
						{
							formatted = formatted+char_;
						}
					}
				}

				return formatted;
			}

			// format to fill with zeros to complete cents chars
			function fill_with_zeroes (str)
			{
				while (str.length<(centsLimit+1)) str = '0'+str;
				return str;
			}

			// format as price
			function price_format (str)
			{
				// formatting settings
				var formatted = fill_with_zeroes(to_numbers(str));
				var thousandsFormatted = '';
				var thousandsCount = 0;

				// split integer from cents
				var centsVal = formatted.substr(formatted.length-centsLimit,centsLimit);
				var integerVal = formatted.substr(0,formatted.length-centsLimit);

				// apply cents pontuation
				formatted = integerVal+centsSeparator+centsVal;

				// apply thousands pontuation
				if (thousandsSeparator)
				{
					for (var j=integerVal.length;j>0;j--)
					{
						char_ = integerVal.substr(j-1,1);
						thousandsCount++;
						if (thousandsCount%3==0) char_ = thousandsSeparator+char_;
						thousandsFormatted = char_+thousandsFormatted;
					}
					if (thousandsFormatted.substr(0,1)==thousandsSeparator) thousandsFormatted = thousandsFormatted.substring(1,thousandsFormatted.length);
					formatted = thousandsFormatted+centsSeparator+centsVal;
				}

				// if the string contains a dash, it is negative - add it to the begining (except for zero)
				if (allowNegative && str.indexOf('-') != -1 && (integerVal != 0 || centsVal != 0)) formatted = '-' + formatted;

				// apply the prefix
				if (prefix) formatted = prefix+formatted;

				return formatted;
			}

			// filter what user type (only numbers and functional keys)
			function key_check (e)
			{
				var code = (e.keyCode ? e.keyCode : e.which);
				var typed = String.fromCharCode(code);
				var functional = false;
				var str = obj.val();
				var newValue = price_format(str+typed);

				// allow key numbers, 0 to 9
				if((code >= 48 && code <= 57) || (code >= 96 && code <= 105)) functional = true;

				// check Backspace, Tab, Enter, Delete, and left/right arrows
				if (code ==  8) functional = true;
				if (code ==  9) functional = true;
				if (code == 13) functional = true;
				if (code == 46) functional = true;
				if (code == 37) functional = true;
				if (code == 39) functional = true;
				if (allowNegative && (code == 189 || code == 109)) functional = true; // dash as well

				if (!functional)
				{
					e.preventDefault();
					e.stopPropagation();
					if (str!=newValue) obj.val(newValue);
				}

			}

			// inster formatted price as a value of an input field
			function price_it ()
			{
				var str = obj.val();
				var price = price_format(str);
				if (str != price) obj.val(price);
			}

			// Add prefix on focus
			function add_prefix()
			{
				var val = obj.val();
				obj.val(prefix + val);
			}

			// Clear prefix on blur if is set to true
			function clear_prefix()
			{
				if($.trim(prefix) != '' && clearPrefix)
				{
					var array = obj.val().split(prefix);
					obj.val(array[1]);
				}
			}

			// bind the actions
			$(this).bind('keydown', key_check);
			$(this).bind('keyup', price_it);

			// Clear Prefix and Add Prefix
			if(clearPrefix)
			{
				$(this).bind('focusout', function()
				{
					clear_prefix();
				});

				$(this).bind('focusin', function()
				{
					add_prefix();
				});
			}

			// If value has content
			if ($(this).val().length>0)
			{
				price_it();
				clear_prefix();
			}

		});

	};

})(jQuery);<commit_msg>adjust comments<commit_after>/*

* Price Format jQuery Plugin
* Created By Eduardo Cuducos cuducos [at] gmail [dot] com
* Currently maintained by Flavio Silveira flavio [at] gmail [dot] com
* Version: 1.6
* Release: 2011-12-05

* original char limit by Flavio Silveira <http://flaviosilveira.com>
* original keydown event attachment by Kaihua Qi
* keydown fixes by Thasmo <http://thasmo.com>
* Clear Prefix on Blur suggest by Ricardo Mendes from PhonoWay
* original allow negative by Cagdas Ucar <http://carsinia.com>
* keypad fixes by Carlos Vinicius <http://www.kvinicius.com.br> and Rayron Victor

*/

(function($) {

	$.fn.priceFormat = function(options)
	{

		var defaults =
		{
			prefix: 'US$ ',
			centsSeparator: '.',
			thousandsSeparator: ',',
			limit: false,
			centsLimit: 2,
			clearPrefix: false,
			allowNegative: false
		};

		var options = $.extend(defaults, options);

		return this.each(function()
		{

			// pre defined options
			var obj = $(this);
			var is_number = /[0-9]/;

			// load the pluggings settings
			var prefix = options.prefix;
			var centsSeparator = options.centsSeparator;
			var thousandsSeparator = options.thousandsSeparator;
			var limit = options.limit;
			var centsLimit = options.centsLimit;
			var clearPrefix = options.clearPrefix;
			var allowNegative = options.allowNegative;

			// skip everything that isn't a number
			// and also skip the left zeroes
			function to_numbers (str)
			{
				var formatted = '';
				for (var i=0;i<(str.length);i++)
				{
					char_ = str.charAt(i);
					if (formatted.length==0 && char_==0) char_ = false;

					if (char_ && char_.match(is_number))
					{
						if (limit)
						{
							if (formatted.length < limit) formatted = formatted+char_;
						}
						else
						{
							formatted = formatted+char_;
						}
					}
				}

				return formatted;
			}

			// format to fill with zeros to complete cents chars
			function fill_with_zeroes (str)
			{
				while (str.length<(centsLimit+1)) str = '0'+str;
				return str;
			}

			// format as price
			function price_format (str)
			{
				// formatting settings
				var formatted = fill_with_zeroes(to_numbers(str));
				var thousandsFormatted = '';
				var thousandsCount = 0;

				// split integer from cents
				var centsVal = formatted.substr(formatted.length-centsLimit,centsLimit);
				var integerVal = formatted.substr(0,formatted.length-centsLimit);

				// apply cents pontuation
				formatted = integerVal+centsSeparator+centsVal;

				// apply thousands pontuation
				if (thousandsSeparator)
				{
					for (var j=integerVal.length;j>0;j--)
					{
						char_ = integerVal.substr(j-1,1);
						thousandsCount++;
						if (thousandsCount%3==0) char_ = thousandsSeparator+char_;
						thousandsFormatted = char_+thousandsFormatted;
					}
					if (thousandsFormatted.substr(0,1)==thousandsSeparator) thousandsFormatted = thousandsFormatted.substring(1,thousandsFormatted.length);
					formatted = thousandsFormatted+centsSeparator+centsVal;
				}

				// if the string contains a dash, it is negative - add it to the begining (except for zero)
				if (allowNegative && str.indexOf('-') != -1 && (integerVal != 0 || centsVal != 0)) formatted = '-' + formatted;

				// apply the prefix
				if (prefix) formatted = prefix+formatted;

				return formatted;
			}

			// filter what user type (only numbers and functional keys)
			function key_check (e)
			{
				var code = (e.keyCode ? e.keyCode : e.which);
				var typed = String.fromCharCode(code);
				var functional = false;
				var str = obj.val();
				var newValue = price_format(str+typed);

				// allow key numbers, 0 to 9
				if((code >= 48 && code <= 57) || (code >= 96 && code <= 105)) functional = true;

				// check Backspace, Tab, Enter, Delete, and left/right arrows
				if (code ==  8) functional = true;
				if (code ==  9) functional = true;
				if (code == 13) functional = true;
				if (code == 46) functional = true;
				if (code == 37) functional = true;
				if (code == 39) functional = true;
				if (allowNegative && (code == 189 || code == 109)) functional = true; // dash as well

				if (!functional)
				{
					e.preventDefault();
					e.stopPropagation();
					if (str!=newValue) obj.val(newValue);
				}

			}

			// inster formatted price as a value of an input field
			function price_it ()
			{
				var str = obj.val();
				var price = price_format(str);
				if (str != price) obj.val(price);
			}

			// Add prefix on focus
			function add_prefix()
			{
				var val = obj.val();
				obj.val(prefix + val);
			}

			// Clear prefix on blur if is set to true
			function clear_prefix()
			{
				if($.trim(prefix) != '' && clearPrefix)
				{
					var array = obj.val().split(prefix);
					obj.val(array[1]);
				}
			}

			// bind the actions
			$(this).bind('keydown', key_check);
			$(this).bind('keyup', price_it);

			// Clear Prefix and Add Prefix
			if(clearPrefix)
			{
				$(this).bind('focusout', function()
				{
					clear_prefix();
				});

				$(this).bind('focusin', function()
				{
					add_prefix();
				});
			}

			// If value has content
			if ($(this).val().length>0)
			{
				price_it();
				clear_prefix();
			}

		});

	};

})(jQuery);<|endoftext|>