<commit_before>import { inject as service } from '@ember/service';
import { computed } from '@ember/object';
import Route from '@ember/routing/route';
import ENV from 'frontend/config/environment';

export default Route.extend({
  intl: service(),
  raven: service(),
  fastboot: service(),
  session: service(),
  auth0: service(),
  currentLocale: computed('fastboot', function() {
    if (this.get('fastboot.isFastBoot')) return 'de';
    const locale = navigator.language || navigator.userLanguage || 'en';
    const lang = locale.split('-')[0];
    return ['de', 'en'].includes(lang) ? lang : 'en';
  }),
  routeAfterAuthentication: 'authentication.callback', // for testing environment
  beforeModel() {
    // define the app's runtime locale
    // For example, here you would maybe do an API lookup to resolver
    // which locale the user should be targeted and perhaps lazily
    // load translations using XHR and calling intl's `addTranslation`/`addTranslations`
    // method with the results of the XHR request

    // whatever you do to pick a locale for the user:
    this._super(...arguments);
    return this.get('intl').setLocale(this.get('currentLocale'));

    // OR for those that sideload, an array is accepted to handle fallback lookups

    // en-ca is the primary locale, en-us is the fallback.
    // this is optional, and likely unnecessary if you define baseLocale (see below)
    // The primary usecase is if you side load all translations
    //
    // return this.get('intl').setLocale(['en-ca', 'en-us']);
  },
  actions: {
    login (afterLoginRoute) {
      // save the current route, assuming we're using the same browser and will have access to the old session
      this.get('session').set('data.afterLoginRoute', afterLoginRoute || this.get('router.url'));

      if (ENV.APP.authenticator === 'authenticator:auth0') {
        this.get('auth0.webAuth').authorize({ language: this.get('intl.locale.firstObject') });
      } else {
        this.get('session').authenticate(ENV.APP.authenticator);
      }
    },

    logout () {
      this.get('session').invalidate();
    },

    error(error){
      if(!ENV.sentry.development) {
        this.get('raven').captureException(error)
      }
      return true; // Let the route above this handle the error.
    }
  }
});

<commit_msg>Set language on server based on `Accept-Language`<commit_after>import { inject as service } from '@ember/service';
import { computed } from '@ember/object';
import Route from '@ember/routing/route';
import ENV from 'frontend/config/environment';

export default Route.extend({
  intl: service(),
  raven: service(),
  fastboot: service(),
  session: service(),
  auth0: service(),
  currentLocale: computed('fastboot', function() {
    let lang;
    if (this.get('fastboot.isFastBoot')) {
      let headers = this.get('fastboot.request.headers');
      lang = headers.get('Accept-Language');
    } else {
      const locale = navigator.language || navigator.userLanguage || 'en';
      lang = locale.split('-')[0];
    }
    return ['de', 'en'].includes(lang) ? lang : 'en';
  }),
  routeAfterAuthentication: 'authentication.callback', // for testing environment
  beforeModel() {
    this._super(...arguments);
    this.get('intl').setLocale(this.get('currentLocale'));
  },
  actions: {
    login (afterLoginRoute) {
      // save the current route, assuming we're using the same browser and will have access to the old session
      this.get('session').set('data.afterLoginRoute', afterLoginRoute || this.get('router.url'));

      if (ENV.APP.authenticator === 'authenticator:auth0') {
        this.get('auth0.webAuth').authorize({ language: this.get('intl.locale.firstObject') });
      } else {
        this.get('session').authenticate(ENV.APP.authenticator);
      }
    },

    logout () {
      this.get('session').invalidate();
    },

    error(error){
      if(!ENV.sentry.development) {
        this.get('raven').captureException(error)
      }
      return true; // Let the route above this handle the error.
    }
  }
});

<|endoftext|>


<commit_before>var bind = require('lodash.bind')
var keys = require('lodash.keys')
var filter = require('lodash.filter')
var isArray = require('lodash.isarray')
var indexOf = require('lodash.indexof')
var forEach = require('lodash.foreach')

var RESERVED_DEPS = exports.RESERVED_DEPS = ['autoBind', 'emit',
  'setDep', 'getDep', 'set', 'get',
  'next', 'errPipeName', 'errPipeBody'
]

var CHANGE_EVENT_PREFIX = exports.CHANGE_EVENT_PREFIX = '__superpipe_change__'

/**
 * Static functions
 */

exports.setDep = function(emitter, injector, name, dep, props) {
  var normalized = normalizeSetDepArgs(name, dep, props)

  name = normalized.name
  dep = normalized.dep
  props = normalized.props

  var autoBind = injector.get('autoBind')()
  // check if any name of dependency is reserved
  if (name)
    isDepNameReserved(RESERVED_DEPS, name)
  else
    isDepNameReserved(RESERVED_DEPS, props)

  if (props && 'object' === typeof dep) {
    // setDep('name', value)
    // setDep('name', value, props)
    forEach(props, function(propName) {
      var _name = name ? [name, propName].join(':') : propName
      var depObject = dep[propName]
      if (autoBind && 'function' === typeof depObject)
        depObject = bind(depObject, dep)

      emitter && emitChange(emitter, _name, depObject, injector)
      injector.set(_name, depObject)
    })
  } else if (name) {
    // setDep(name, value)
    emitter && emitChange(emitter, name, dep, injector)
    injector.set(name, dep)
  } else {
    throw new Error('Unsupported function arguments for `setDep`')
  }
}

var emitChange = exports.emitChange = function(emitter, depName, newValue, injector) {
  if (emitter && emitter.listeners) {
    var eventName = CHANGE_EVENT_PREFIX + depName
    if (emitter.listeners(eventName).length > -1)
      emitter.emit(eventName, newValue, injector.get(depName))
  }
}

var isDepNameReserved = exports.isDepNameReserved = function(reservedDeps, name) {
  if ('string' === typeof name && indexOf(reservedDeps, name) > -1) {
    throw new Error('The name of your dependency is reserved: ' + name)
  } else if (isArray(name)) {
    forEach(reservedDeps, function(reserved) {
      if (indexOf(name, reserved) > -1)
        throw new Error('The name of your dependency is reserved: ' + reserved)
    })
  }
  return false
}

var normalizeSetDepArgs = exports.normalizeSetDepArgs = function(name, dep, props) {
  if ('string' !== typeof name) {
    props = dep
    dep = name
    name = null
  }

  if (!name && (!dep || 'object' !== typeof dep))
    throw new Error('Unsupported function arguments for `setDep`')

  if ('string' === typeof props && 'object' === typeof dep) {
    var allKeys = []
    for (var k in dep)
      allKeys.push(k)

    if (props) {
      switch (props) {
        case '*':
          props = filter(allKeys, function(prop) {
            return '_' !== prop[0]
          })
          break
        case '*^':
          props = filter(allKeys, function(prop) {
            return '_' !== prop[0] && 'function' === typeof dep[prop]
          })
          break
        case '*$':
          props = filter(allKeys, function(prop) {
            return '_' !== prop[0] && 'function' !== typeof dep[prop]
          })
          break
        default:
          props = [props]
      }
    } else if (!name) {
      props = allKeys
    }
  }

  if (!name && !props)
    props = keys(dep)

  return {
    name: name,
    dep: dep,
    props: props
  }
}
<commit_msg>Emit change event after the dependency was actually set.<commit_after>var bind = require('lodash.bind')
var keys = require('lodash.keys')
var filter = require('lodash.filter')
var isArray = require('lodash.isarray')
var indexOf = require('lodash.indexof')
var forEach = require('lodash.foreach')

var RESERVED_DEPS = exports.RESERVED_DEPS = ['autoBind', 'emit',
  'setDep', 'getDep', 'set', 'get',
  'next', 'errPipeName', 'errPipeBody'
]

var CHANGE_EVENT_PREFIX = exports.CHANGE_EVENT_PREFIX = '__superpipe_change__'

/**
 * Static functions
 */

exports.setDep = function(emitter, injector, name, dep, props) {
  var normalized = normalizeSetDepArgs(name, dep, props)

  name = normalized.name
  dep = normalized.dep
  props = normalized.props

  var autoBind = injector.get('autoBind')()
  // check if any name of dependency is reserved
  if (name)
    isDepNameReserved(RESERVED_DEPS, name)
  else
    isDepNameReserved(RESERVED_DEPS, props)

  if (props && 'object' === typeof dep) {
    // setDep(value, props)
    // setDep('name', value)
    // setDep('name', value, props)
    forEach(props, function(propName) {
      var _name = name ? [name, propName].join(':') : propName
      var depObject = dep[propName]
      if (autoBind && 'function' === typeof depObject)
        depObject = bind(depObject, dep)

      injector.set(_name, depObject)
      emitter && emitChange(emitter, _name, depObject, injector)
    })
  } else if (name) {
    // setDep(name, value)
    injector.set(name, dep)
    emitter && emitChange(emitter, name, dep, injector)
  } else {
    throw new Error('Unsupported function arguments for `setDep`')
  }
}

var emitChange = exports.emitChange = function(emitter, depName, newValue, injector) {
  if (emitter && emitter.listeners) {
    var eventName = CHANGE_EVENT_PREFIX + depName
    if (emitter.listeners(eventName).length > -1)
      emitter.emit(eventName, newValue, injector.get(depName))
  }
}

var isDepNameReserved = exports.isDepNameReserved = function(reservedDeps, name) {
  if ('string' === typeof name && indexOf(reservedDeps, name) > -1) {
    throw new Error('The name of your dependency is reserved: ' + name)
  } else if (isArray(name)) {
    forEach(reservedDeps, function(reserved) {
      if (indexOf(name, reserved) > -1)
        throw new Error('The name of your dependency is reserved: ' + reserved)
    })
  }
  return false
}

var normalizeSetDepArgs = exports.normalizeSetDepArgs = function(name, dep, props) {
  if ('string' !== typeof name) {
    props = dep
    dep = name
    name = null
  }

  if (!name && (!dep || 'object' !== typeof dep))
    throw new Error('Unsupported function arguments for `setDep`')

  if ('string' === typeof props && 'object' === typeof dep) {
    var allKeys = []
    for (var k in dep)
      allKeys.push(k)

    if (props) {
      switch (props) {
        case '*':
          props = filter(allKeys, function(prop) {
            return '_' !== prop[0]
          })
          break
        case '*^':
          props = filter(allKeys, function(prop) {
            return '_' !== prop[0] && 'function' === typeof dep[prop]
          })
          break
        case '*$':
          props = filter(allKeys, function(prop) {
            return '_' !== prop[0] && 'function' !== typeof dep[prop]
          })
          break
        default:
          props = [props]
      }
    } else if (!name) {
      props = allKeys
    }
  }

  if (!name && !props)
    props = keys(dep)

  return {
    name: name,
    dep: dep,
    props: props
  }
}
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2014 Adobe Systems Incorporated. All rights reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 */

/* global _playground */

define(function (require, exports, module) {
    "use strict";

    var EventEmitter = require("eventEmitter"),
        util = require("../util"),
        Promise = require("bluebird");

    /**
     * Promisified version of low-level keyboard focus functions
     */
    var _ui = Promise.promisifyAll(_playground.ps.ui);

    /* jshint bitwise: false */
    /**
     *
     */
    var ALL_NONWINDOW_WIDGETS_BITMASK =
        _ui.widgetTypes.CONTROLBAR |
        _ui.widgetTypes.DOCUMENT_TABS |
        _ui.widgetTypes.PALETTE |
        _ui.widgetTypes.TOOLBAR;
    /* jshint bitwise: true */

    /**
     * The UI object provides helper methods for dealing with the
     * low-level native binding to Photoshop. This object will typically
     * not be used by user-level code.
     *
     * Emits low-level Photoshop events such as "select" with
     * the following parameters:
     *    1. @param {?} info about the event, dependent on event type (Note:
     *           this should become more specific as the native interface is
     *           further defined.)
     *
     * @extends EventEmitter
     * @constructor
     * @private
     */
    var UI = function () {
        EventEmitter.call(this);
    };
    util.inherits(UI, EventEmitter);

    /**
     * Overscroll modes
     *
     * @const
     * @type{object.<number>}
     */
    UI.prototype.overscrollMode = _playground.ps.ui.overscrollMode;

    /**
     * Get the current Photoshop overscroll mode.
     *
     * @see UI.prototype.overscrollMode
     * @return {Promise.<number>} Resolves with the overscroll mode
     */
    UI.prototype.getOverscrollMode = function () {
        return _ui.getOverscrollModeAsync();
    };

    /**
     * Set the current Photoshop overscroll mode.
     *
     * @see UI.prototype.overscrollMode
     * @param {number} mode The desired overscroll model
     * @return {Promise.<number>} Resolves once the overscroll mode is set
     */
    UI.prototype.setOverscrollMode = function (mode) {
        var options = {
            mode: mode
        };

        return _ui.setOverscrollModeAsync(options);
    };

    /**
     * Determines whether the scrollbars are currently suppressed.
     *
     * @return {Promise.<boolean>}
     */
    UI.prototype.getSuppressScrollbars = function () {
        return _ui.getSuppressScrollbarsAsync();
    };

    /**
     * Sets whether or not the scrollbars should be suppressed.
     *
     * @param {boolean} suppress Whether or not the scrollbars should be suppressed
     * @return {Promise}
     */
    UI.prototype.setSuppressScrollbars = function (suppress) {
        return _ui.setSuppressScrollbarsAsync(suppress);
    };

    /**
     * Sets whether or not the Photoshop classic chrome is visible
     *
     * @param {boolean} visible Whether or not the chrome should be visible
     */
    UI.prototype.setClassicChromeVisibility = function (visible) {
        return _ui.setWidgetTypeVisibilityAsync(ALL_NONWINDOW_WIDGETS_BITMASK, visible);
    };

    /** @type {UI} The UI singleton */
    var theUI = new UI();

    module.exports = theUI;
});
<commit_msg>hide scrollbars when classic chrome is hidden<commit_after>/*
 * Copyright (c) 2014 Adobe Systems Incorporated. All rights reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 */

/* global _playground */

define(function (require, exports, module) {
    "use strict";

    var EventEmitter = require("eventEmitter"),
        util = require("../util"),
        Promise = require("bluebird");

    /**
     * Promisified version of low-level keyboard focus functions
     */
    var _ui = Promise.promisifyAll(_playground.ps.ui);

    /* jshint bitwise: false */
    /**
     *
     */
    var ALL_NONWINDOW_WIDGETS_BITMASK =
        _ui.widgetTypes.CONTROLBAR |
        _ui.widgetTypes.DOCUMENT_TABS |
        _ui.widgetTypes.PALETTE |
        _ui.widgetTypes.TOOLBAR;
    /* jshint bitwise: true */

    /**
     * The UI object provides helper methods for dealing with the
     * low-level native binding to Photoshop. This object will typically
     * not be used by user-level code.
     *
     * Emits low-level Photoshop events such as "select" with
     * the following parameters:
     *    1. @param {?} info about the event, dependent on event type (Note:
     *           this should become more specific as the native interface is
     *           further defined.)
     *
     * @extends EventEmitter
     * @constructor
     * @private
     */
    var UI = function () {
        EventEmitter.call(this);
    };
    util.inherits(UI, EventEmitter);

    /**
     * Overscroll modes
     *
     * @const
     * @type{object.<number>}
     */
    UI.prototype.overscrollMode = _playground.ps.ui.overscrollMode;

    /**
     * Get the current Photoshop overscroll mode.
     *
     * @see UI.prototype.overscrollMode
     * @return {Promise.<number>} Resolves with the overscroll mode
     */
    UI.prototype.getOverscrollMode = function () {
        return _ui.getOverscrollModeAsync();
    };

    /**
     * Set the current Photoshop overscroll mode.
     *
     * @see UI.prototype.overscrollMode
     * @param {number} mode The desired overscroll model
     * @return {Promise.<number>} Resolves once the overscroll mode is set
     */
    UI.prototype.setOverscrollMode = function (mode) {
        var options = {
            mode: mode
        };

        return _ui.setOverscrollModeAsync(options);
    };

    /**
     * Determines whether the scrollbars are currently suppressed.
     *
     * @return {Promise.<boolean>}
     */
    UI.prototype.getSuppressScrollbars = function () {
        return _ui.getSuppressScrollbarsAsync();
    };

    /**
     * Sets whether or not the scrollbars should be suppressed.
     *
     * @param {boolean} suppress Whether or not the scrollbars should be suppressed
     * @return {Promise}
     */
    UI.prototype.setSuppressScrollbars = function (suppress) {
        return _ui.setSuppressScrollbarsAsync(suppress);
    };

    /**
     * Sets whether or not the Photoshop classic chrome is visible
     *
     * @param {boolean} visible Whether or not the chrome should be visible
     */
    UI.prototype.setClassicChromeVisibility = function (visible) {
        return this.setSuppressScrollbars(!visible).then(function () {
            _ui.setWidgetTypeVisibilityAsync(ALL_NONWINDOW_WIDGETS_BITMASK, visible);
        });

    };

    /** @type {UI} The UI singleton */
    var theUI = new UI();

    module.exports = theUI;
});
<|endoftext|>


<commit_before>set_global('$', global.make_zjquery());
set_global('page_params', {
    emojiset: 'google',
});
set_global('upload_widget', {});

zrequire('emoji_codes', 'generated/emoji/emoji_codes');
zrequire('emoji');
zrequire('markdown');
zrequire('util');

(function test_build_emoji_upload_widget() {
    var build_widget_stub = false;
    upload_widget.build_widget = function (
        get_file_input,
        file_name_field,
        input_error,
        clear_button,
        upload_button
    ) {
        assert.deepEqual(get_file_input(), $('#emoji_file_input'));
        assert.deepEqual(file_name_field, $('#emoji-file-name'));
        assert.deepEqual(input_error, $('#emoji_file_input_error'));
        assert.deepEqual(clear_button, $('#emoji_image_clear_button'));
        assert.deepEqual(upload_button, $('#emoji_upload_button'));
        build_widget_stub = true;
    };
    emoji.build_emoji_upload_widget();
    assert(build_widget_stub);
}());

(function test_initialize() {
    var image_stub = false;
    class Image {
        set src(data) {
            assert.equal(data, '/static/generated/emoji/sheet_google_64.png');
            image_stub = true;
        }
    }
    set_global('Image', Image);
    emoji.initialize();
    assert(image_stub);
}());

(function test_get_canonical_name() {
    emoji.active_realm_emojis = {
        realm_emoji: 'TBD',
    };
    var canonical_name = emoji.get_canonical_name('realm_emoji');
    assert.equal(canonical_name, 'realm_emoji');

    global.emoji_codes = {
        name_to_codepoint: {
            '+1': '1f44d',
        },
        codepoint_to_name: {
            '1f44d': 'thumbs_up',
        },
    };
    canonical_name = emoji.get_canonical_name('+1');
    assert.equal(canonical_name, 'thumbs_up');

    emoji.active_realm_emojis = {
        '+1': 'TBD',
    };
    canonical_name = emoji.get_canonical_name('+1');
    assert.equal(canonical_name, '+1');

    var errored = false;
    set_global('blueslip', {
        error: function (error) {
            assert.equal(error, "Invalid emoji name: non_existent");
            errored = true;
        },
    });
    emoji.get_canonical_name('non_existent');
    assert(errored);
}());

(function test_translate_emoticons_to_names() {
    // Simple test
    var test_text = 'Testing :)';
    var expected = 'Testing :smiley:';
    var result = emoji.translate_emoticons_to_names(test_text);
    assert.equal(expected, result);

    // Extensive tests.
    // The following code loops over the test cases and each emoticon conversion
    // to generate multiple test cases.
    var testcases = [
        {name: 'only emoticon', original: '<original>', expected: '<converted>'},
        {name: 'space at start', original: ' <original>', expected: ' <converted>'},
        {name: 'space at end', original: '<original> ', expected: '<converted> '},
        {name: 'symbol at end', original: '<original>!', expected: '<converted>!'},
        {name: 'symbol at start', original: 'Hello,<original>', expected: 'Hello,<converted>'},
        {name: 'after a word', original: 'Hello<original>', expected: 'Hello<original>'},
        {name: 'between words', original: 'Hello<original>World', expected: 'Hello<original>World'},
        {name: 'end of sentence', original: 'End of sentence. <original>', expected: 'End of sentence. <converted>'},
        {name: 'between symbols', original: 'Hello.<original>! World.', expected: 'Hello.<original>! World.'},
        {name: 'before end of sentence', original: 'Hello <original>!', expected: 'Hello <converted>!'},
    ];
    Object.keys(emoji.EMOTICON_CONVERSIONS).forEach(key => {
        testcases.forEach(t => {
            var converted_value = `:${emoji.EMOTICON_CONVERSIONS[key]}:`;
            t = Object.assign({}, t); // circumvent copy by reference.
            t.original = t.original.replace(/(<original>)/g, key);
            t.expected = t.expected.replace(/(<original>)/g, key);
            t.expected = t.expected.replace(/(<converted>)/g, converted_value);
            var result = emoji.translate_emoticons_to_names(t.original);
            assert.equal(result, t.expected);
        });
    });
}());
<commit_msg>node tests: Clean up emoji tests.<commit_after>set_global('$', global.make_zjquery());
set_global('page_params', {
    emojiset: 'google',
});
set_global('upload_widget', {});

zrequire('emoji_codes', 'generated/emoji/emoji_codes');
zrequire('emoji');
zrequire('markdown');
zrequire('util');

(function test_build_emoji_upload_widget() {
    var build_widget_stub = false;
    upload_widget.build_widget = function (
        get_file_input,
        file_name_field,
        input_error,
        clear_button,
        upload_button
    ) {
        assert.deepEqual(get_file_input(), $('#emoji_file_input'));
        assert.deepEqual(file_name_field, $('#emoji-file-name'));
        assert.deepEqual(input_error, $('#emoji_file_input_error'));
        assert.deepEqual(clear_button, $('#emoji_image_clear_button'));
        assert.deepEqual(upload_button, $('#emoji_upload_button'));
        build_widget_stub = true;
    };
    emoji.build_emoji_upload_widget();
    assert(build_widget_stub);
}());

(function test_initialize() {
    var image_stub = false;
    class Image {
        set src(data) {
            assert.equal(data, '/static/generated/emoji/sheet_google_64.png');
            image_stub = true;
        }
    }
    set_global('Image', Image);
    emoji.initialize();
    assert(image_stub);
}());

(function test_get_canonical_name() {
    emoji.active_realm_emojis = {
        realm_emoji: 'TBD',
    };
    var canonical_name = emoji.get_canonical_name('realm_emoji');
    assert.equal(canonical_name, 'realm_emoji');

    global.emoji_codes = {
        name_to_codepoint: {
            '+1': '1f44d',
        },
        codepoint_to_name: {
            '1f44d': 'thumbs_up',
        },
    };
    canonical_name = emoji.get_canonical_name('+1');
    assert.equal(canonical_name, 'thumbs_up');

    emoji.active_realm_emojis = {
        '+1': 'TBD',
    };
    canonical_name = emoji.get_canonical_name('+1');
    assert.equal(canonical_name, '+1');

    var errored = false;
    set_global('blueslip', {
        error: function (error) {
            assert.equal(error, "Invalid emoji name: non_existent");
            errored = true;
        },
    });
    emoji.get_canonical_name('non_existent');
    assert(errored);
}());

(function test_translate_emoticons_to_names() {
    // Simple test
    var test_text = 'Testing :)';
    var expected = 'Testing :smiley:';
    var result = emoji.translate_emoticons_to_names(test_text);
    assert.equal(expected, result);

    // Extensive tests.
    // The following code loops over the test cases and each emoticon conversion
    // to generate multiple test cases.
    var testcases = [
        {name: 'only emoticon', original: '<original>', expected: '<converted>'},
        {name: 'space at start', original: ' <original>', expected: ' <converted>'},
        {name: 'space at end', original: '<original> ', expected: '<converted> '},
        {name: 'symbol at end', original: '<original>!', expected: '<converted>!'},
        {name: 'symbol at start', original: 'Hello,<original>', expected: 'Hello,<converted>'},
        {name: 'after a word', original: 'Hello<original>', expected: 'Hello<original>'},
        {name: 'between words', original: 'Hello<original>World', expected: 'Hello<original>World'},
        {name: 'end of sentence', original: 'End of sentence. <original>', expected: 'End of sentence. <converted>'},
        {name: 'between symbols', original: 'Hello.<original>! World.', expected: 'Hello.<original>! World.'},
        {name: 'before end of sentence', original: 'Hello <original>!', expected: 'Hello <converted>!'},
    ];
    _.each(emoji.EMOTICON_CONVERSIONS, (full_name, shortcut) => {
        _.each(testcases, (t) => {
            var converted_value = ':' + full_name + ':';
            var original = t.original;
            var expected = t.expected;
            original = original.replace(/(<original>)/g, shortcut);
            expected = expected.replace(/(<original>)/g, shortcut)
                               .replace(/(<converted>)/g, converted_value);
            var result = emoji.translate_emoticons_to_names(original);
            assert.equal(result, expected);
        });
    });
}());
<|endoftext|>


<commit_before>
joint.mvc.View = Backbone.View.extend({

    options: {},
    theme: null,
    themeClassNamePrefix: joint.util.addClassNamePrefix('theme-'),
    requireSetThemeOverride: false,
    defaultTheme: joint.config.defaultTheme,

    constructor: function(options) {

        Backbone.View.call(this, options);
    },

    initialize: function(options) {

        this.requireSetThemeOverride = options && !!options.theme;

        this.options = joint.util.assign({}, this.options, options);

        joint.util.bindAll(this, 'setTheme', 'onSetTheme', 'remove', 'onRemove');

        joint.mvc.views[this.cid] = this;

        this.setTheme(this.options.theme || this.defaultTheme);
        this.init();
    },

    // Override the Backbone `_ensureElement()` method in order to create an
    // svg element (e.g., `<g>`) node that wraps all the nodes of the Cell view.
    // Expose class name setter as a separate method.
    _ensureElement: function() {
        if (!this.el) {
            var tagName = joint.util.result(this, 'tagName');
            var attrs = joint.util.assign({}, joint.util.result(this, 'attributes'));
            if (this.id) attrs.id = joint.util.result(this, 'id');
            this.setElement(this._createElement(tagName));
            this._setAttributes(attrs);
        } else {
            this.setElement(joint.util.result(this, 'el'));
        }
        this._ensureElClassName();
    },

    _setAttributes: function(attrs) {
        if (this.svgElement) {
            this.vel.attr(attrs);
        } else {
            this.$el.attr(attrs);
        }
    },

    _createElement: function(tagName) {
        if (this.svgElement) {
            return document.createElementNS(V.namespace.xmlns, tagName);
        } else {
            return document.createElement(tagName);
        }
    },

    // Utilize an alternative DOM manipulation API by
    // adding an element reference wrapped in Vectorizer.
    _setElement: function(el) {
        this.$el = el instanceof Backbone.$ ? el : Backbone.$(el);
        this.el = this.$el[0];
        if (this.svgElement) this.vel = V(this.el);
    },

    _ensureElClassName: function() {
        var className = joint.util.result(this, 'className');
        var prefixedClassName = joint.util.addClassNamePrefix(className);
        // Note: className removal here kept for backwards compatibility only
        if (this.svgElement) {
            this.vel.removeClass(className).addClass(prefixedClassName);
        } else {
            this.$el.removeClass(className).addClass(prefixedClassName);
        }
    },

    init: function() {
        // Intentionally empty.
        // This method is meant to be overriden.
    },

    onRender: function() {
        // Intentionally empty.
        // This method is meant to be overriden.
    },

    setTheme: function(theme, opt) {

        opt = opt || {};

        // Theme is already set, override is required, and override has not been set.
        // Don't set the theme.
        if (this.theme && this.requireSetThemeOverride && !opt.override) {
            return this;
        }

        this.removeThemeClassName();
        this.addThemeClassName(theme);
        this.onSetTheme(this.theme/* oldTheme */, theme/* newTheme */);
        this.theme = theme;

        return this;
    },

    addThemeClassName: function(theme) {

        theme = theme || this.theme;

        var className = this.themeClassNamePrefix + theme;

        this.$el.addClass(className);

        return this;
    },

    removeThemeClassName: function(theme) {

        theme = theme || this.theme;

        var className = this.themeClassNamePrefix + theme;

        this.$el.removeClass(className);

        return this;
    },

    onSetTheme: function(oldTheme, newTheme) {
        // Intentionally empty.
        // This method is meant to be overriden.
    },

    remove: function() {

        this.onRemove();

        joint.mvc.views[this.cid] = null;

        Backbone.View.prototype.remove.apply(this, arguments);

        return this;
    },

    onRemove: function() {
        // Intentionally empty.
        // This method is meant to be overriden.
    },

    getEventNamespace: function() {
        // Returns a per-session unique namespace
        return '.joint-event-ns-' + this.cid;
    }

}, {

    extend: function() {

        var args = Array.from(arguments);

        // Deep clone the prototype and static properties objects.
        // This prevents unexpected behavior where some properties are overwritten outside of this function.
        var protoProps = args[0] && joint.util.assign({}, args[0]) || {};
        var staticProps = args[1] && joint.util.assign({}, args[1]) || {};

        // Need the real render method so that we can wrap it and call it later.
        var renderFn = protoProps.render || (this.prototype && this.prototype.render) || null;

        /*
            Wrap the real render method so that:
                .. `onRender` is always called.
                .. `this` is always returned.
        */
        protoProps.render = function() {

            if (renderFn) {
                // Call the original render method.
                renderFn.apply(this, arguments);
            }

            // Should always call onRender() method.
            this.onRender();

            // Should always return itself.
            return this;
        };

        return Backbone.View.extend.call(this, protoProps, staticProps);
    }
});

<commit_msg>mvc.View: Options assigned in constructor (#726)<commit_after>
joint.mvc.View = Backbone.View.extend({

    options: {},
    theme: null,
    themeClassNamePrefix: joint.util.addClassNamePrefix('theme-'),
    requireSetThemeOverride: false,
    defaultTheme: joint.config.defaultTheme,

    constructor: function(options) {

        this.requireSetThemeOverride = options && !!options.theme;
        this.options = joint.util.assign({}, this.options, options);

        Backbone.View.call(this, options);
    },

    initialize: function(options) {

        joint.util.bindAll(this, 'setTheme', 'onSetTheme', 'remove', 'onRemove');

        joint.mvc.views[this.cid] = this;

        this.setTheme(this.options.theme || this.defaultTheme);
        this.init();
    },

    // Override the Backbone `_ensureElement()` method in order to create an
    // svg element (e.g., `<g>`) node that wraps all the nodes of the Cell view.
    // Expose class name setter as a separate method.
    _ensureElement: function() {
        if (!this.el) {
            var tagName = joint.util.result(this, 'tagName');
            var attrs = joint.util.assign({}, joint.util.result(this, 'attributes'));
            if (this.id) attrs.id = joint.util.result(this, 'id');
            this.setElement(this._createElement(tagName));
            this._setAttributes(attrs);
        } else {
            this.setElement(joint.util.result(this, 'el'));
        }
        this._ensureElClassName();
    },

    _setAttributes: function(attrs) {
        if (this.svgElement) {
            this.vel.attr(attrs);
        } else {
            this.$el.attr(attrs);
        }
    },

    _createElement: function(tagName) {
        if (this.svgElement) {
            return document.createElementNS(V.namespace.xmlns, tagName);
        } else {
            return document.createElement(tagName);
        }
    },

    // Utilize an alternative DOM manipulation API by
    // adding an element reference wrapped in Vectorizer.
    _setElement: function(el) {
        this.$el = el instanceof Backbone.$ ? el : Backbone.$(el);
        this.el = this.$el[0];
        if (this.svgElement) this.vel = V(this.el);
    },

    _ensureElClassName: function() {
        var className = joint.util.result(this, 'className');
        var prefixedClassName = joint.util.addClassNamePrefix(className);
        // Note: className removal here kept for backwards compatibility only
        if (this.svgElement) {
            this.vel.removeClass(className).addClass(prefixedClassName);
        } else {
            this.$el.removeClass(className).addClass(prefixedClassName);
        }
    },

    init: function() {
        // Intentionally empty.
        // This method is meant to be overriden.
    },

    onRender: function() {
        // Intentionally empty.
        // This method is meant to be overriden.
    },

    setTheme: function(theme, opt) {

        opt = opt || {};

        // Theme is already set, override is required, and override has not been set.
        // Don't set the theme.
        if (this.theme && this.requireSetThemeOverride && !opt.override) {
            return this;
        }

        this.removeThemeClassName();
        this.addThemeClassName(theme);
        this.onSetTheme(this.theme/* oldTheme */, theme/* newTheme */);
        this.theme = theme;

        return this;
    },

    addThemeClassName: function(theme) {

        theme = theme || this.theme;

        var className = this.themeClassNamePrefix + theme;

        this.$el.addClass(className);

        return this;
    },

    removeThemeClassName: function(theme) {

        theme = theme || this.theme;

        var className = this.themeClassNamePrefix + theme;

        this.$el.removeClass(className);

        return this;
    },

    onSetTheme: function(oldTheme, newTheme) {
        // Intentionally empty.
        // This method is meant to be overriden.
    },

    remove: function() {

        this.onRemove();

        joint.mvc.views[this.cid] = null;

        Backbone.View.prototype.remove.apply(this, arguments);

        return this;
    },

    onRemove: function() {
        // Intentionally empty.
        // This method is meant to be overriden.
    },

    getEventNamespace: function() {
        // Returns a per-session unique namespace
        return '.joint-event-ns-' + this.cid;
    }

}, {

    extend: function() {

        var args = Array.from(arguments);

        // Deep clone the prototype and static properties objects.
        // This prevents unexpected behavior where some properties are overwritten outside of this function.
        var protoProps = args[0] && joint.util.assign({}, args[0]) || {};
        var staticProps = args[1] && joint.util.assign({}, args[1]) || {};

        // Need the real render method so that we can wrap it and call it later.
        var renderFn = protoProps.render || (this.prototype && this.prototype.render) || null;

        /*
            Wrap the real render method so that:
                .. `onRender` is always called.
                .. `this` is always returned.
        */
        protoProps.render = function() {

            if (renderFn) {
                // Call the original render method.
                renderFn.apply(this, arguments);
            }

            // Should always call onRender() method.
            this.onRender();

            // Should always return itself.
            return this;
        };

        return Backbone.View.extend.call(this, protoProps, staticProps);
    }
});

<|endoftext|>


<commit_before>/*
 * jQuery Address Plugin v${version}
 * http://www.asual.com/jquery/address/
 *
 * Copyright (c) 2009 Rostislav Hristov
 * Dual licensed under the MIT and GPL licenses.
 * http://docs.jquery.com/License
 *
 * Date: ${timestamp}
 */
(function ($) {

    $.address = (function () {

        var _trigger = function(name) {
            $($.address).trigger(
                $.extend($.Event(name),
                    (function() {
                        var event = {
                            value: $.address.value(),
                            path: $.address.path(),
                            pathNames: $.address.pathNames(),
                            parameterNames: $.address.parameterNames(),
                            parameters: {},
                            queryString: $.address.queryString()
                        };
                        for (var i = 0, l = event.parameterNames.length; i < l; i++)
                            event.parameters[event.parameterNames[i]] = $.address.parameter(event.parameterNames[i]);
                        return event;
                    }).call($.address)
                )
            );
        };

        var _bind = function(value, data, fn) {
        	$($.address).bind(value, fn || data, fn && data);
        	return $.address;
        };

        var _init = function() {
            _trigger('init');
        };

        var _change = function() {
            _trigger('change');
        };

        var _getHash = function() {
            var index = _l.href.indexOf('#');
            return index != -1 ? _ec(_dc(_l.href.substr(index + 1))) : '';
        };

    	var _getWindow = function() {
    		try {
    			return top.document != undefined ? top : window;
    		} catch (e) {
    			return window;
    		}
    	};

        var _strictCheck = function(value, force) {
            if (_opts.strict)
                value = force ? (value.substr(0, 1) != '/' ? '/' + value : value) : (value == '' ? '/' : value);
            return value;
        };

        var _ieLocal = function(value, direction) {
            return (_msie && _l.protocol == 'file:') ?
                (direction ? _value.replace(/\?/, '%3F') : _value.replace(/%253F/, '?')) : value;
        };

        var _searchScript = function(el) {
            for (var i = 0, l = el.childNodes.length, s; i < l; i++) {
                if (el.childNodes[i].src)
                    _url = String(el.childNodes[i].src);
                if (s = _searchScript(el.childNodes[i]))
                    return s;
            }
        };

        var _listen = function() {
            if (!_silent) {
                var hash = _getHash(),
                	diff = !(_value == hash);
                if (_safari && _version < 523) {
                    if (_length != _h.length) {
                        _length = _h.length;
                        if (typeof _stack[_length - 1] != UNDEFINED)
                            _value = _stack[_length - 1];
                        _update(false);
                    }
                } else if (_msie && _version < 7 && diff) {
                    _l.reload();
                } else if (diff) {
                    _value = hash;
                    _update(false);
                }
            }
        };

        var _update = function(internal) {
            _change();
            if (internal) {
            	_trigger('internalChange');
            } else {
            	_trigger('externalChange');
            }
            _st(_track, 10);
        };

        var _track = function() {
            var value = (_l.pathname + (/\/$/.test(_l.pathname) ? '' : '/') + $.address.value()).replace(/\/\//, '/').replace(/^\/$/, ''),
            	fn = window[_opts.tracker];
            if (typeof fn == FUNCTION)
                fn(value);
            else if (typeof pageTracker != UNDEFINED && typeof pageTracker._trackPageview == FUNCTION)
                pageTracker._trackPageview(value);
            else if (typeof urchinTracker == FUNCTION)
                urchinTracker(value);
        };

        var _htmlWrite = function() {
            var doc = _frame.contentWindow.document;
            doc.open();
            doc.write('<title>' + _d.title + '</title>');
            doc.close();
        };

        var _load = function() {
            if (!_loaded) {
                _loaded = TRUE;
                if (_msie && _version < 8) {
                    var frameset = _d.getElementsByTagName('frameset')[0];
                    _frame = _d.createElement((frameset ? '' : 'i') + 'frame');
                    if (frameset) {
                        frameset.insertAdjacentElement('beforeEnd', _frame);
                        frameset[frameset.cols ? 'cols' : 'rows'] += ',0';
                        _frame.src = 'javascript:false';
                        _frame.noResize = true;
                        _frame.frameBorder = _frame.frameSpacing = 0;
                    } else {
                        _frame.src = 'javascript:false';
                        _frame.style.display = 'none';
                        _d.body.insertAdjacentElement('afterBegin', _frame);
                    }
                    _st(function() {
                        $(_frame).bind('load', function() {
                            var win = _frame.contentWindow;
                            var src = win.location.href;
                            _value = (typeof win[ID] != UNDEFINED ? win[ID] : '');
                            if (_value != _getHash()) {
                                _update(false);
                                _l.hash = _ieLocal(_value, TRUE);
                            }
                        });
                        if (typeof _frame.contentWindow[ID] == UNDEFINED)
                            _htmlWrite();
                    }, 50);
                } else if (_safari) {
                    if (_version < 418) {
                        $(_d.body).append('<form id="' + ID + '" style="position:absolute;top:-9999px;" method="get"></form>');
                        _form = _d.getElementById(ID);
                    }
                    if (typeof _l[ID] == UNDEFINED) _l[ID] = {};
                    if (typeof _l[ID][_l.pathname] != UNDEFINED) _stack = _l[ID][_l.pathname].split(',');
                }

                _st(function() {
                    _init();
                    _update(false);
                }, 1);

                if (_msie && _version >= 8)
                    _d.body.onhashchange = _listen;
                else
                    _si(_listen, 50);
                $('a[rel*=address:]').address();
            }
        };

        var ID = 'jQueryAddress',
            FUNCTION = 'function',
            UNDEFINED = 'undefined',
            TRUE = true,
            FALSE = false,
            _browser = $.browser,
            _version = parseFloat($.browser.version),
            _mozilla = _browser.mozilla,
            _msie = _browser.msie,
            _opera = _browser.opera,
            _safari = _browser.safari,
            _supported = FALSE,
            _t = _getWindow(),
            _d = _t.document,
            _h = _t.history,
            _l = _t.location,
            _si = setInterval,
            _st = setTimeout,
            _dc = decodeURI,
            _ec = encodeURI,
            _agent = navigator.userAgent,
            _frame,
            _form,
            _url,
            _title = _d.title,
            _length = _h.length,
            _silent = FALSE,
            _loaded = FALSE,
            _justset = TRUE,
            _juststart = TRUE,
            _stack = [],
            _listeners = {},
            _value = _getHash(),
            _api = {},
            _opts = {history: TRUE, strict: TRUE};

        if (_msie) {
            _version = parseFloat(_agent.substr(_agent.indexOf('MSIE') + 4));
            if (_d.documentMode && _d.documentMode != _version)
            	_version = _d.documentMode != 8 ? 7 : 8;
        }

        _supported =
            (_mozilla && _version >= 1) ||
            (_msie && _version >= 6) ||
            (_opera && _version >= 9.5) ||
            (_safari && _version >= 312);

        if (_supported) {

            for (var i = 1; i < _length; i++)
                _stack.push('');

            _stack.push(_getHash());

            if (_msie && _l.hash != _getHash())
                _l.hash = '#' + _ieLocal(_getHash(), TRUE);

            if (_opera)
                history.navigationMode = 'compatible';

            _searchScript(document);
            var _qi = _url.indexOf('?');
            if (_url && _qi > -1) {
                var param, params = _url.substr(_qi + 1).split('&');
                for (var i = 0, p; p = params[i]; i++) {
                    param = p.split('=');
                    if (/^(history|strict)$/.test(param[0])) {
                        _opts[param[0]] = (isNaN(param[1]) ? /^(true|yes)$/i.test(param[1]) : (parseInt(param[1]) != 0));
                    }
                    if (/^tracker$/.test(param[0]))
                        _opts[param[0]] = param[1];
                }
            }

            $(_load);

        } else if ((!_supported && _l.href.indexOf('#') != -1) ||
            (_safari && _version < 418 && _l.href.indexOf('#') != -1 && _l.search != '')){
            _d.open();
            _d.write('<meta http-equiv="refresh" content="0;url=' +
                _l.href.substr(0, _l.href.indexOf('#')) + '" />');
            _d.close();
        } else {
            _track();
        }

        return {
        	init: function(data, fn){
            	$(this).bind('init', fn || data, fn && data);
            	return this;
        	},
        	change: function(data, fn){
            	$(this).bind('change', fn || data, fn && data);
            	return this;
        	},
        	internalChange: function(data, fn){
            	$(this).bind('internalChange', fn || data, fn && data);
            	return this;
        	},
        	externalChange: function(data, fn){
            	$(this).bind('externalChange', fn || data, fn && data);
            	return this;
        	},
        	baseURL: function() {
                var url = _l.href;
                if (url.indexOf('#') != -1)
                    url = url.substr(0, url.indexOf('#'));
                if (url.substr(url.length - 1) == '/')
                    url = url.substr(0, url.length - 1);
                return url;
        	},
        	strict: function(value) {
        		if (value !== undefined) {
        			_opts.strict = value;
        			return this;
        		}
    			return _opts.strict;
            },
            history: function(value) {
        		if (value !== undefined) {
        			_opts.history = value;
        			return this;
        		}
    			return _opts.history;
            },
            tracker: function(value) {
        		if (value !== undefined) {
        			_opts.tracker = value;
        			return this;
        		}
    			return _opts.tracker;
            },
            title: function(value) {
            	if (value !== undefined) {
            		value = _dc(value);
                    _st(function() {
                        _title = _d.title = value;
                        if (_juststart && _frame && _frame.contentWindow && _frame.contentWindow.document) {
                            _frame.contentWindow.document.title = value;
                            _juststart = FALSE;
                        }
                        if (!_justset && _mozilla)
                            _l.replace(_l.href.indexOf('#') != -1 ? _l.href : _l.href + '#');
                        _justset = FALSE;
                    }, 50);
                    return this;
            	}
                return _d.title;
        	},
            value: function(value) {
        		if (value !== undefined) {
                    value = _ec(_dc(_strictCheck(value, TRUE)));
                    if (value == '/') value = '';
                    if (_value == value) return;
                    _justset = TRUE;
                    _value = value;
                    _silent = TRUE;
                    _update(true);
                    _stack[_h.length] = _value;
                    if (_safari) {
                        if (_opts.history) {
                            _l[ID][_l.pathname] = _stack.toString();
                            _length = _h.length + 1;
                            if (_version < 418) {
                                if (_l.search == '') {
                                    _form.action = '#' + _value;
                                    _form.submit();
                                }
                            } else if (_version < 523 || _value == '') {
                                var evt = _d.createEvent('MouseEvents');
                                evt.initEvent('click', TRUE, TRUE);
                                var anchor = _d.createElement('a');
                                anchor.href = '#' + _value;
                                anchor.dispatchEvent(evt);
                            } else {
                                _l.hash = '#' + _value;
                            }
                        } else {
                            _l.replace('#' + _value);
                        }
                    } else if (_value != _getHash()) {
                        if (_opts.history)
                            _l.hash = '#' + _ieLocal(_value, TRUE);
                        else
                            _l.replace('#' + _value);
                    }
                    if ((_msie && _version < 8) && _opts.history) {
                        _st(_htmlWrite, 50);
                    }
                    if (_safari)
                        _st(function(){ _silent = FALSE; }, 1);
                    else
                        _silent = FALSE;
                    return this;
        		}
                if (!_supported) return null;
                return _dc(_strictCheck(_ieLocal(_value, FALSE), FALSE));
            },
            path: function(value) {
            	if (value !== undefined) {
                    var qs = this.queryString();
                	this.value(value + (qs ? '?' + qs : ''));
                	return this;
            	}
                var value = this.value();
                return (value.indexOf('?') != -1) ? value.split('?')[0] : value;
            },
            queryString: function(value) {
            	if (value !== undefined) {
            		this.value(this.path() + (value ? '?' + value : ''));
            		return this;
            	}
                var value = this.value(),
                	index = value.indexOf('?');
                if (index != -1 && index < value.length)
                	return value.substr(index + 1);
            },
            parameter: function(name, value, append) {
            	if (value !== undefined) {
            		var names = this.parameterNames(),
            			params = [];
            		for (var i = 0; i < names.length; i++) {
            			var n = names[i],
            				v = this.parameter(n);
            			if (typeof v == 'string')
            				v = [v];
            			if (n == name)
            				v = (value === null || value == '') ? [] :
            					(append ? v.concat([value]) : [value]);
        				for (var j = 0; j < v.length; j++)
                			params.push(n + '=' + v[j]);
            		}
            		if ($.inArray(name, names) == -1)
            			params.push(name + '=' + value);
            		this.queryString(params.join('&'));
            		return this;
            	}
                var value = this.value(),
                	index = value.indexOf('?');
                if (index != -1) {
                    value = value.substr(index + 1);
                    var params = value.split('&'),
                    	r = [];
                    for (var i = 0; i < params.length; i++) {
                        var p = params[i].split('=');
                        if (p[0] == name)
                            r.push(p[1]);
                    }
                    if (r.length != 0)
                    	return r.length != 1 ? r : r[0];
                }
            },
            pathNames: function() {
                var path = this.path(),
                	names = path.split('/');
                if (path.substr(0, 1) == '/' || path.length == 0)
                    names.splice(0, 1);
                if (path.substr(path.length - 1, 1) == '/')
                    names.splice(names.length - 1, 1);
                return names;
            },
            parameterNames: function() {
                var value = this.value(),
	            	index = value.indexOf('?'),
                	names = [];
                if (index != -1) {
                    value = value.substr(index + 1);
                    if (value != '' && value.indexOf('=') != -1) {
                        var params = value.split('&');
                        for (var i = 0; i < params.length; i++) {
                        	var name = params[i].split('=')[0];
                        	if ($.inArray(name, names) == -1)
                        		names.push(name);
                        }
                    }
                }
                return names;
            }
        };

    })();

    $.fn.address = function (fn) {
        $(this).click(function() {
            var value = fn ? fn.call(this) :
                /address:/.test($(this).attr('rel')) ? $(this).attr('rel').split('address:')[1].split(' ')[0] :
                $(this).attr('href').replace(/^#/, '');
            $.address.value(value);
            return false;
        });
    };

}(jQuery));<commit_msg>Fix for future-matched elements by Johannes Schill. <commit_after>/*
 * jQuery Address Plugin v${version}
 * http://www.asual.com/jquery/address/
 *
 * Copyright (c) 2009 Rostislav Hristov
 * Dual licensed under the MIT and GPL licenses.
 * http://docs.jquery.com/License
 *
 * Date: ${timestamp}
 */
(function ($) {

    $.address = (function () {

        var _trigger = function(name) {
            $($.address).trigger(
                $.extend($.Event(name),
                    (function() {
                        var event = {
                            value: $.address.value(),
                            path: $.address.path(),
                            pathNames: $.address.pathNames(),
                            parameterNames: $.address.parameterNames(),
                            parameters: {},
                            queryString: $.address.queryString()
                        };
                        for (var i = 0, l = event.parameterNames.length; i < l; i++)
                            event.parameters[event.parameterNames[i]] = $.address.parameter(event.parameterNames[i]);
                        return event;
                    }).call($.address)
                )
            );
        };

        var _bind = function(value, data, fn) {
        	$($.address).bind(value, fn || data, fn && data);
        	return $.address;
        };

        var _init = function() {
            _trigger('init');
        };

        var _change = function() {
            _trigger('change');
        };

        var _getHash = function() {
            var index = _l.href.indexOf('#');
            return index != -1 ? _ec(_dc(_l.href.substr(index + 1))) : '';
        };

    	var _getWindow = function() {
    		try {
    			return top.document != undefined ? top : window;
    		} catch (e) {
    			return window;
    		}
    	};

        var _strictCheck = function(value, force) {
            if (_opts.strict)
                value = force ? (value.substr(0, 1) != '/' ? '/' + value : value) : (value == '' ? '/' : value);
            return value;
        };

        var _ieLocal = function(value, direction) {
            return (_msie && _l.protocol == 'file:') ?
                (direction ? _value.replace(/\?/, '%3F') : _value.replace(/%253F/, '?')) : value;
        };

        var _searchScript = function(el) {
            for (var i = 0, l = el.childNodes.length, s; i < l; i++) {
                if (el.childNodes[i].src)
                    _url = String(el.childNodes[i].src);
                if (s = _searchScript(el.childNodes[i]))
                    return s;
            }
        };

        var _listen = function() {
            if (!_silent) {
                var hash = _getHash(),
                	diff = !(_value == hash);
                if (_safari && _version < 523) {
                    if (_length != _h.length) {
                        _length = _h.length;
                        if (typeof _stack[_length - 1] != UNDEFINED)
                            _value = _stack[_length - 1];
                        _update(false);
                    }
                } else if (_msie && _version < 7 && diff) {
                    _l.reload();
                } else if (diff) {
                    _value = hash;
                    _update(false);
                }
            }
        };

        var _update = function(internal) {
            _change();
            if (internal) {
            	_trigger('internalChange');
            } else {
            	_trigger('externalChange');
            }
            _st(_track, 10);
        };

        var _track = function() {
            var value = (_l.pathname + (/\/$/.test(_l.pathname) ? '' : '/') + $.address.value()).replace(/\/\//, '/').replace(/^\/$/, ''),
            	fn = window[_opts.tracker];
            if (typeof fn == FUNCTION)
                fn(value);
            else if (typeof pageTracker != UNDEFINED && typeof pageTracker._trackPageview == FUNCTION)
                pageTracker._trackPageview(value);
            else if (typeof urchinTracker == FUNCTION)
                urchinTracker(value);
        };

        var _htmlWrite = function() {
            var doc = _frame.contentWindow.document;
            doc.open();
            doc.write('<title>' + _d.title + '</title>');
            doc.close();
        };

        var _load = function() {
            if (!_loaded) {
                _loaded = TRUE;
                if (_msie && _version < 8) {
                    var frameset = _d.getElementsByTagName('frameset')[0];
                    _frame = _d.createElement((frameset ? '' : 'i') + 'frame');
                    if (frameset) {
                        frameset.insertAdjacentElement('beforeEnd', _frame);
                        frameset[frameset.cols ? 'cols' : 'rows'] += ',0';
                        _frame.src = 'javascript:false';
                        _frame.noResize = true;
                        _frame.frameBorder = _frame.frameSpacing = 0;
                    } else {
                        _frame.src = 'javascript:false';
                        _frame.style.display = 'none';
                        _d.body.insertAdjacentElement('afterBegin', _frame);
                    }
                    _st(function() {
                        $(_frame).bind('load', function() {
                            var win = _frame.contentWindow;
                            var src = win.location.href;
                            _value = (typeof win[ID] != UNDEFINED ? win[ID] : '');
                            if (_value != _getHash()) {
                                _update(false);
                                _l.hash = _ieLocal(_value, TRUE);
                            }
                        });
                        if (typeof _frame.contentWindow[ID] == UNDEFINED)
                            _htmlWrite();
                    }, 50);
                } else if (_safari) {
                    if (_version < 418) {
                        $(_d.body).append('<form id="' + ID + '" style="position:absolute;top:-9999px;" method="get"></form>');
                        _form = _d.getElementById(ID);
                    }
                    if (typeof _l[ID] == UNDEFINED) _l[ID] = {};
                    if (typeof _l[ID][_l.pathname] != UNDEFINED) _stack = _l[ID][_l.pathname].split(',');
                }

                _st(function() {
                    _init();
                    _update(false);
                }, 1);

                if (_msie && _version >= 8)
                    _d.body.onhashchange = _listen;
                else
                    _si(_listen, 50);
                $('a[rel*=address:]').address();
            }
        };

        var ID = 'jQueryAddress',
            FUNCTION = 'function',
            UNDEFINED = 'undefined',
            TRUE = true,
            FALSE = false,
            _browser = $.browser,
            _version = parseFloat($.browser.version),
            _mozilla = _browser.mozilla,
            _msie = _browser.msie,
            _opera = _browser.opera,
            _safari = _browser.safari,
            _supported = FALSE,
            _t = _getWindow(),
            _d = _t.document,
            _h = _t.history,
            _l = _t.location,
            _si = setInterval,
            _st = setTimeout,
            _dc = decodeURI,
            _ec = encodeURI,
            _agent = navigator.userAgent,
            _frame,
            _form,
            _url,
            _title = _d.title,
            _length = _h.length,
            _silent = FALSE,
            _loaded = FALSE,
            _justset = TRUE,
            _juststart = TRUE,
            _stack = [],
            _listeners = {},
            _value = _getHash(),
            _api = {},
            _opts = {history: TRUE, strict: TRUE};

        if (_msie) {
            _version = parseFloat(_agent.substr(_agent.indexOf('MSIE') + 4));
            if (_d.documentMode && _d.documentMode != _version)
            	_version = _d.documentMode != 8 ? 7 : 8;
        }

        _supported =
            (_mozilla && _version >= 1) ||
            (_msie && _version >= 6) ||
            (_opera && _version >= 9.5) ||
            (_safari && _version >= 312);

        if (_supported) {

            for (var i = 1; i < _length; i++)
                _stack.push('');

            _stack.push(_getHash());

            if (_msie && _l.hash != _getHash())
                _l.hash = '#' + _ieLocal(_getHash(), TRUE);

            if (_opera)
                history.navigationMode = 'compatible';

            _searchScript(document);
            var _qi = _url.indexOf('?');
            if (_url && _qi > -1) {
                var param, params = _url.substr(_qi + 1).split('&');
                for (var i = 0, p; p = params[i]; i++) {
                    param = p.split('=');
                    if (/^(history|strict)$/.test(param[0])) {
                        _opts[param[0]] = (isNaN(param[1]) ? /^(true|yes)$/i.test(param[1]) : (parseInt(param[1]) != 0));
                    }
                    if (/^tracker$/.test(param[0]))
                        _opts[param[0]] = param[1];
                }
            }

            $(_load);

        } else if ((!_supported && _l.href.indexOf('#') != -1) ||
            (_safari && _version < 418 && _l.href.indexOf('#') != -1 && _l.search != '')){
            _d.open();
            _d.write('<meta http-equiv="refresh" content="0;url=' +
                _l.href.substr(0, _l.href.indexOf('#')) + '" />');
            _d.close();
        } else {
            _track();
        }

        return {
        	init: function(data, fn){
            	$(this).bind('init', fn || data, fn && data);
            	return this;
        	},
        	change: function(data, fn){
            	$(this).bind('change', fn || data, fn && data);
            	return this;
        	},
        	internalChange: function(data, fn){
            	$(this).bind('internalChange', fn || data, fn && data);
            	return this;
        	},
        	externalChange: function(data, fn){
            	$(this).bind('externalChange', fn || data, fn && data);
            	return this;
        	},
        	baseURL: function() {
                var url = _l.href;
                if (url.indexOf('#') != -1)
                    url = url.substr(0, url.indexOf('#'));
                if (url.substr(url.length - 1) == '/')
                    url = url.substr(0, url.length - 1);
                return url;
        	},
        	strict: function(value) {
        		if (value !== undefined) {
        			_opts.strict = value;
        			return this;
        		}
    			return _opts.strict;
            },
            history: function(value) {
        		if (value !== undefined) {
        			_opts.history = value;
        			return this;
        		}
    			return _opts.history;
            },
            tracker: function(value) {
        		if (value !== undefined) {
        			_opts.tracker = value;
        			return this;
        		}
    			return _opts.tracker;
            },
            title: function(value) {
            	if (value !== undefined) {
            		value = _dc(value);
                    _st(function() {
                        _title = _d.title = value;
                        if (_juststart && _frame && _frame.contentWindow && _frame.contentWindow.document) {
                            _frame.contentWindow.document.title = value;
                            _juststart = FALSE;
                        }
                        if (!_justset && _mozilla)
                            _l.replace(_l.href.indexOf('#') != -1 ? _l.href : _l.href + '#');
                        _justset = FALSE;
                    }, 50);
                    return this;
            	}
                return _d.title;
        	},
            value: function(value) {
        		if (value !== undefined) {
                    value = _ec(_dc(_strictCheck(value, TRUE)));
                    if (value == '/') value = '';
                    if (_value == value) return;
                    _justset = TRUE;
                    _value = value;
                    _silent = TRUE;
                    _update(true);
                    _stack[_h.length] = _value;
                    if (_safari) {
                        if (_opts.history) {
                            _l[ID][_l.pathname] = _stack.toString();
                            _length = _h.length + 1;
                            if (_version < 418) {
                                if (_l.search == '') {
                                    _form.action = '#' + _value;
                                    _form.submit();
                                }
                            } else if (_version < 523 || _value == '') {
                                var evt = _d.createEvent('MouseEvents');
                                evt.initEvent('click', TRUE, TRUE);
                                var anchor = _d.createElement('a');
                                anchor.href = '#' + _value;
                                anchor.dispatchEvent(evt);
                            } else {
                                _l.hash = '#' + _value;
                            }
                        } else {
                            _l.replace('#' + _value);
                        }
                    } else if (_value != _getHash()) {
                        if (_opts.history)
                            _l.hash = '#' + _ieLocal(_value, TRUE);
                        else
                            _l.replace('#' + _value);
                    }
                    if ((_msie && _version < 8) && _opts.history) {
                        _st(_htmlWrite, 50);
                    }
                    if (_safari)
                        _st(function(){ _silent = FALSE; }, 1);
                    else
                        _silent = FALSE;
                    return this;
        		}
                if (!_supported) return null;
                return _dc(_strictCheck(_ieLocal(_value, FALSE), FALSE));
            },
            path: function(value) {
            	if (value !== undefined) {
                    var qs = this.queryString();
                	this.value(value + (qs ? '?' + qs : ''));
                	return this;
            	}
                var value = this.value();
                return (value.indexOf('?') != -1) ? value.split('?')[0] : value;
            },
            queryString: function(value) {
            	if (value !== undefined) {
            		this.value(this.path() + (value ? '?' + value : ''));
            		return this;
            	}
                var value = this.value(),
                	index = value.indexOf('?');
                if (index != -1 && index < value.length)
                	return value.substr(index + 1);
            },
            parameter: function(name, value, append) {
            	if (value !== undefined) {
            		var names = this.parameterNames(),
            			params = [];
            		for (var i = 0; i < names.length; i++) {
            			var n = names[i],
            				v = this.parameter(n);
            			if (typeof v == 'string')
            				v = [v];
            			if (n == name)
            				v = (value === null || value == '') ? [] :
            					(append ? v.concat([value]) : [value]);
        				for (var j = 0; j < v.length; j++)
                			params.push(n + '=' + v[j]);
            		}
            		if ($.inArray(name, names) == -1)
            			params.push(name + '=' + value);
            		this.queryString(params.join('&'));
            		return this;
            	}
                var value = this.value(),
                	index = value.indexOf('?');
                if (index != -1) {
                    value = value.substr(index + 1);
                    var params = value.split('&'),
                    	r = [];
                    for (var i = 0; i < params.length; i++) {
                        var p = params[i].split('=');
                        if (p[0] == name)
                            r.push(p[1]);
                    }
                    if (r.length != 0)
                    	return r.length != 1 ? r : r[0];
                }
            },
            pathNames: function() {
                var path = this.path(),
                	names = path.split('/');
                if (path.substr(0, 1) == '/' || path.length == 0)
                    names.splice(0, 1);
                if (path.substr(path.length - 1, 1) == '/')
                    names.splice(names.length - 1, 1);
                return names;
            },
            parameterNames: function() {
                var value = this.value(),
	            	index = value.indexOf('?'),
                	names = [];
                if (index != -1) {
                    value = value.substr(index + 1);
                    if (value != '' && value.indexOf('=') != -1) {
                        var params = value.split('&');
                        for (var i = 0; i < params.length; i++) {
                        	var name = params[i].split('=')[0];
                        	if ($.inArray(name, names) == -1)
                        		names.push(name);
                        }
                    }
                }
                return names;
            }
        };

    })();

    $.fn.address = function (fn) {
        $(this).live('click', function() {
            var value = fn ? fn.call(this) :
                /address:/.test($(this).attr('rel')) ? $(this).attr('rel').split('address:')[1].split(' ')[0] :
                $(this).attr('href').replace(/^#/, '');
            $.address.value(value);
            return false;
        });
    };

}(jQuery));<|endoftext|>


<commit_before>/**
 * Mail autocomplete
 * Author: nuintun
 * $(selector).mailtip({
 *   mails: [], // mails
 *   onselected： function(mail){}, // callback on selected
 *   width: 'auto', // popup tip's width
 *   offsetTop: -1, // offset top relative default position
 *   offsetLeft: 0, // offset left relative default position
 *   zIndex: 10 // popup tip's z-index
 * });
 */

'use strict';

(function ($){
  // is ie 9
  var ISIE9 = /MSIE 9.0/i.test(window.navigator.userAgent);
  // is support oninput event
  var hasInputEvent = 'oninput' in document.createElement('input');

  /**
   * is a number
   * @param val
   * @returns {boolean}
   */
  function isNumber(val){
    return typeof val === 'number' && isFinite(val);
  }

  /**
   * parse string to regexp
   * @param pattern
   * @param attributes
   * @returns {RegExp}
   */
  function parseRegExp(pattern, attributes){
    var imp = /[\^\.\\\|\(\)\*\+\-\$\[\]\?]/igm;

    pattern = pattern.replace(imp, function (match){
      return '\\' + match;
    });

    return new RegExp(pattern, attributes);
  }

  /**
   * create popup tip
   * @param input
   * @param config
   * @returns {*}
   */
  function createTip(input, config){
    var tip = null;

    // only create tip and binding event once
    if (!input.data('data-mailtip')) {
      var wrap = input.parent();

      // set parent node position
      !/absolute|relative/i.test(wrap.css('position')) && wrap.css('position', 'relative');
      // off input autocomplete
      input.attr('autocomplete', 'off');

      var offset = input.offset();
      var wrapOffset = wrap.offset();

      tip = $('

');

      // insert tip after input
      input.after(tip);

      // set tip style
      tip.css({
        top: offset.top - wrapOffset.top + input.outerHeight() + config.offsetTop,
        left: offset.left - wrapOffset.left + config.offsetLeft,
        width: config.width === 'input' ? input.outerWidth() - tip.outerWidth() + tip.width() : config.width
      });

      // when width is auto, set min width equal input width
      if (config.width === 'auto') {
        tip.css('min-width', input.outerWidth() - tip.outerWidth() + tip.width());
      }

      // binding event
      tip.on('mouseenter mouseleave click', 'li', function (e){
        var selected = $(this);

        switch (e.type) {
          case 'mouseenter':
            selected.addClass('hover');
            break;
          case 'click':
            var mail = selected.attr('title');

            input.val(mail).focus();
            config.onselected.call(input[0], mail);
            break;
          case 'mouseleave':
            selected.removeClass('hover');
            break;
          default:
            break;
        }
      });

      // when on click if the target element not input, hide tip
      $(document).on('click', function (e){
        if (e.target === input[0]) return;

        tip.hide();
      });

      input.data('data-mailtip', tip);
    }

    return tip || input.data('data-mailtip');
  }

  /**
   * create mail list
   * @param value
   * @param mails
   * @returns {*}
   */
  function createLists(value, mails){
    var arr;
    var regx;
    var lists = '';
    var hasAt = /@/.test(value);

    if (hasAt) {
      arr = value.split('@');

      if (arr.length > 2) return lists;

      value = arr[0];
      regx = parseRegExp(arr[1], 'i');
    }

    value = hasAt ? value.split('@')[0] : value;

    for (var i = 0, len = mails.length; i < len; i++) {
      if (hasAt && !regx.test(mails[i])) continue;

      lists += '

'
        + value + '@' + mails[i] + '

';
    }

    return lists.replace(/^
]*)>/, '
');
  }

  /**
   * change list active state
   * @param panle
   * @param up
   */
  function changeActive(panle, up){
    // if tip is visible do nothing
    if (panle.css('display') === 'none') return;

    var liActive = panle.find('li.active');

    if (up) {
      var liPrev = liActive.prev();

      liPrev = liPrev.length ? liPrev : panle.find('li:last');
      liActive.removeClass('active');
      liPrev.addClass('active');
    } else {
      var liNext = liActive.next();

      liNext = liNext.length ? liNext : panle.find('li:first');
      liActive.removeClass('active');
      liNext.addClass('active');
    }
  }

  /**
   * toggle tip
   * @param val
   * @param tip
   * @param mails
   */
  function toggleTip(val, tip, mails){
    // if input text is empty or has space char, chinese char, comma or begin with @ or more than two @, hide tip
    //如果输入为空，带空格，中文字符，英文逗号，@开头，或者两个以上@直接隐藏提示
    if (!val || /[,]|[\u4e00-\u9fa5]|\s|^@/.test(val) || val.split('@').length > 2) {
      tip.hide();
    } else {
      var lists = createLists(val, mails);

      // if has match mails show tip
      if (lists) {
        tip.html(lists).show();
      } else {
        tip.hide();
      }
    }
  }

  /**
   * exports
   * @param config
   * @returns {*}
   */
  $.fn.mailtip = function (config){
    var defaults = {
      mails: [
        'qq.com', '163.com', 'sina.com',
        'gmail.com', '126.com', '139.com',
        '189.com', 'sohu.com', 'msn.com',
        'hotmail.com', 'yahoo.com', 'yahoo.com.cn'
      ],
      onselected: $.noop,
      width: 'auto',
      offsetTop: -1,
      offsetLeft: 0,
      zIndex: 10
    };

    config = $.extend({}, defaults, config);
    config.zIndex = isNumber(config.zIndex) ? config.zIndex : defaults.zIndex;
    config.offsetTop = isNumber(config.offsetTop) ? config.offsetTop : defaults.offsetTop;
    config.offsetLeft = isNumber(config.offsetLeft) ? config.offsetLeft : defaults.offsetLeft;
    config.onselected = $.isFunction(config.onselected) ? config.onselected : defaults.onselected;
    config.width = config.width === 'input' || isNumber(config.width) ? config.width : defaults.width;

    return this.each(function (){
      // input
      var input = $(this);
      // tip
      var tip = createTip(input, config);

      // binding event
      input.on('keydown input propertychange', function (e){
        if (e.type === 'keydown') {
          switch (e.keyCode) {
            // backspace
            case 8:
              // shit! ie9 input event has a bug, backspace do not trigger input event
              ISIE9 && input.trigger('input');
              break;
            case 9:
              tip.hide();
              break;
            // up
            case 38:
              changeActive(tip, true);
              break;
            // down
            case 40:
              changeActive(tip);
              break;
            // enter
            case 13:
              if (tip.css('display') === 'none') return;

              e.preventDefault();

              var mail = tip.find('li.active').attr('title');

              tip.hide();
              input.val(mail).focus();
              config.onselected.call(input[0], mail);
              break;
            default:
              break;
          }
        } else {
          if (hasInputEvent) {
            toggleTip(input.val(), tip, config.mails);
          } else if (e.originalEvent.propertyName === 'value') {
            toggleTip(input.val(), tip, config.mails);
          }
        }
      });

      // shit! ie9 input event has a bug, backspace do not trigger input event
      ISIE9 && input.on('keyup', function (e){
        e.keyCode === 8 && toggleTip(input.val(), tip, config.mails);
      });
    });
  };
}(jQuery));
<commit_msg>remove unused code<commit_after>/**
 * Mail autocomplete
 * Author: nuintun
 * $(selector).mailtip({
 *   mails: [], // mails
 *   onselected： function(mail){}, // callback on selected
 *   width: 'auto', // popup tip's width
 *   offsetTop: -1, // offset top relative default position
 *   offsetLeft: 0, // offset left relative default position
 *   zIndex: 10 // popup tip's z-index
 * });
 */

'use strict';

(function ($){
  // is ie 9
  var ISIE9 = /MSIE 9.0/i.test(window.navigator.userAgent);
  // is support oninput event
  var hasInputEvent = 'oninput' in document.createElement('input');

  /**
   * is a number
   * @param val
   * @returns {boolean}
   */
  function isNumber(val){
    return typeof val === 'number' && isFinite(val);
  }

  /**
   * parse string to regexp
   * @param pattern
   * @param attributes
   * @returns {RegExp}
   */
  function parseRegExp(pattern, attributes){
    var imp = /[\^\.\\\|\(\)\*\+\-\$\[\]\?]/igm;

    pattern = pattern.replace(imp, function (match){
      return '\\' + match;
    });

    return new RegExp(pattern, attributes);
  }

  /**
   * create popup tip
   * @param input
   * @param config
   * @returns {*}
   */
  function createTip(input, config){
    var tip = null;

    // only create tip and binding event once
    if (!input.data('data-mailtip')) {
      var wrap = input.parent();

      // set parent node position
      !/absolute|relative/i.test(wrap.css('position')) && wrap.css('position', 'relative');
      // off input autocomplete
      input.attr('autocomplete', 'off');

      var offset = input.offset();
      var wrapOffset = wrap.offset();

      tip = $('

');

      // insert tip after input
      input.after(tip);

      // set tip style
      tip.css({
        top: offset.top - wrapOffset.top + input.outerHeight() + config.offsetTop,
        left: offset.left - wrapOffset.left + config.offsetLeft,
        width: config.width === 'input' ? input.outerWidth() - tip.outerWidth() + tip.width() : config.width
      });

      // when width is auto, set min width equal input width
      if (config.width === 'auto') {
        tip.css('min-width', input.outerWidth() - tip.outerWidth() + tip.width());
      }

      // binding event
      tip.on('mouseenter mouseleave click', 'li', function (e){
        var selected = $(this);

        switch (e.type) {
          case 'mouseenter':
            selected.addClass('hover');
            break;
          case 'click':
            var mail = selected.attr('title');

            input.val(mail).focus();
            config.onselected.call(input[0], mail);
            break;
          case 'mouseleave':
            selected.removeClass('hover');
            break;
          default:
            break;
        }
      });

      // when on click if the target element not input, hide tip
      $(document).on('click', function (e){
        if (e.target === input[0]) return;

        tip.hide();
      });

      input.data('data-mailtip', tip);
    }

    return tip || input.data('data-mailtip');
  }

  /**
   * create mail list
   * @param value
   * @param mails
   * @returns {*}
   */
  function createLists(value, mails){
    var arr;
    var regx;
    var lists = '';
    var hasAt = /@/.test(value);

    if (hasAt) {
      arr = value.split('@');

      if (arr.length > 2) return lists;

      value = arr[0];
      regx = parseRegExp(arr[1], 'i');
    }

    for (var i = 0, len = mails.length; i < len; i++) {
      if (hasAt && !regx.test(mails[i])) continue;

      lists += '

'
        + value + '@' + mails[i] + '

';
    }

    return lists.replace(/^
]*)>/, '
');
  }

  /**
   * change list active state
   * @param panle
   * @param up
   */
  function changeActive(panle, up){
    // if tip is visible do nothing
    if (panle.css('display') === 'none') return;

    var liActive = panle.find('li.active');

    if (up) {
      var liPrev = liActive.prev();

      liPrev = liPrev.length ? liPrev : panle.find('li:last');
      liActive.removeClass('active');
      liPrev.addClass('active');
    } else {
      var liNext = liActive.next();

      liNext = liNext.length ? liNext : panle.find('li:first');
      liActive.removeClass('active');
      liNext.addClass('active');
    }
  }

  /**
   * toggle tip
   * @param val
   * @param tip
   * @param mails
   */
  function toggleTip(val, tip, mails){
    // if input text is empty or has space char, chinese char, comma or begin with @ or more than two @, hide tip
    //如果输入为空，带空格，中文字符，英文逗号，@开头，或者两个以上@直接隐藏提示
    if (!val || /[,]|[\u4e00-\u9fa5]|\s|^@/.test(val) || val.split('@').length > 2) {
      tip.hide();
    } else {
      var lists = createLists(val, mails);

      // if has match mails show tip
      if (lists) {
        tip.html(lists).show();
      } else {
        tip.hide();
      }
    }
  }

  /**
   * exports
   * @param config
   * @returns {*}
   */
  $.fn.mailtip = function (config){
    var defaults = {
      mails: [
        'qq.com', '163.com', 'sina.com',
        'gmail.com', '126.com', '139.com',
        '189.com', 'sohu.com', 'msn.com',
        'hotmail.com', 'yahoo.com', 'yahoo.com.cn'
      ],
      onselected: $.noop,
      width: 'auto',
      offsetTop: -1,
      offsetLeft: 0,
      zIndex: 10
    };

    config = $.extend({}, defaults, config);
    config.zIndex = isNumber(config.zIndex) ? config.zIndex : defaults.zIndex;
    config.offsetTop = isNumber(config.offsetTop) ? config.offsetTop : defaults.offsetTop;
    config.offsetLeft = isNumber(config.offsetLeft) ? config.offsetLeft : defaults.offsetLeft;
    config.onselected = $.isFunction(config.onselected) ? config.onselected : defaults.onselected;
    config.width = config.width === 'input' || isNumber(config.width) ? config.width : defaults.width;

    return this.each(function (){
      // input
      var input = $(this);
      // tip
      var tip = createTip(input, config);

      // binding event
      input.on('keydown input propertychange', function (e){
        if (e.type === 'keydown') {
          switch (e.keyCode) {
            // backspace
            case 8:
              // shit! ie9 input event has a bug, backspace do not trigger input event
              ISIE9 && input.trigger('input');
              break;
            case 9:
              tip.hide();
              break;
            // up
            case 38:
              changeActive(tip, true);
              break;
            // down
            case 40:
              changeActive(tip);
              break;
            // enter
            case 13:
              if (tip.css('display') === 'none') return;

              e.preventDefault();

              var mail = tip.find('li.active').attr('title');

              tip.hide();
              input.val(mail).focus();
              config.onselected.call(input[0], mail);
              break;
            default:
              break;
          }
        } else {
          if (hasInputEvent) {
            toggleTip(input.val(), tip, config.mails);
          } else if (e.originalEvent.propertyName === 'value') {
            toggleTip(input.val(), tip, config.mails);
          }
        }
      });

      // shit! ie9 input event has a bug, backspace do not trigger input event
      ISIE9 && input.on('keyup', function (e){
        e.keyCode === 8 && toggleTip(input.val(), tip, config.mails);
      });
    });
  };
}(jQuery));
<|endoftext|>


<commit_before>/*
 * onetree
 * https://github.com/xuejp/onetree
 *
 * Copyright (c) 2015 xuejp
 * Licensed under the APL license.
 */

(function (factory) {
	if (typeof define === 'function' && define.amd) {
		define('onetree', ['jquery'], factory);
	} else {
		factory(jQuery);
	}
}(function ($) {
	'use strict';

	$.fn.extend({
		onetree: function (options) {
			var element = this,
				dataList = [];
			options = $.extend(true, {}, $.onetree.options, options);
			dataList = $.onetree.generateLinkedList(options.data);
			if (options.root.append) {
				dataList = [{
					node: options.root.node,
					subnode: dataList
				}];
			}
			$(element)
				.empty()
				.append($.onetree.generateHtml(dataList,
					options.wrapOutTag,
					options.wrapInnerTag,
					options.contentTemplate,
					options.callback));

			return $.fn.menutree;
		}
	});

	$.extend({
		onetree: {
			options: {
				data: [],
				root: {
					append: false,
					node: {
						ID: 0,
						PID: -1
					}
				},
				wrapOutTag: 'ul',
				wrapInnerTag: 'li',
				contentTemplate: '',
				callback: null
			},
			generateLinkedList: function (data) {
				var result = [],
					i,
					rootNode = this.findRootNodes(data);
				for (i = 0; i < rootNode.length; i++) {
					result.push(this._generateSubTree(rootNode[i], data));
				}
				return result;
			},
			convertToDictionary: function (data) {
				var result = [],
					i;
				for (i = 0; i < data.length; i++) {
					result[data[i].ID.toString()] = data[i];
				}
				return result;
			},
			findRootNodes: function (data) {
				var result = [],
					key,
					dictionary = this.convertToDictionary(data);
				for (key in dictionary) {
					if (dictionary.hasOwnProperty(key) && dictionary[dictionary[key].PID.toString()] === undefined) {
						result.push(dictionary[key]);
					}
				}
				return result;
			},
			_generateSubTree: function (fNode, data) {
				var result = [],
					i;
				for (i = 0; i < data.length; i++) {
					if (data[i].PID === fNode.ID) {
						result.push(this._generateSubTree(data[i], data));
					}
				}
				return {
					node: fNode,
					subnode: result
				};
			},
			generateHtml: function (linkedList, wrapOutTag, wrapInnerTag, contentTemplate, callback) {
				var $html,
					i;
				$html = $(document.createElement(wrapOutTag));
				for (i = 0; i < linkedList.length; i++) {
					$html.append(this._generateNodeHtml(linkedList[i], wrapOutTag, wrapInnerTag, contentTemplate, callback));
				}
				return $html;
			},
			_generateNodeHtml: function (node, wrapOutTag, wrapInnerTag, contentTemplate, callback) {
				var $nodeHtml,
					$nodelistHtml,
					i;
				$nodeHtml = $(document.createElement(wrapInnerTag))
					.append(this._generateContentHtml(node.node, contentTemplate, callback));
				if (callback !== null) {
					callback($nodeHtml, node.node);
				}
				if (node.subnode.length > 0) {
					$nodelistHtml = $(document.createElement(wrapOutTag));
					for (i = 0; i < node.subnode.length; i++) {
						$nodelistHtml.append(this._generateNodeHtml(node.subnode[i], wrapOutTag, wrapInnerTag, contentTemplate, callback));
					}
					$nodeHtml.append($nodelistHtml);
				}
				return $nodeHtml;
			},
			_generateContentHtml: function (node, contentTemplate) {
				var strHtml = contentTemplate,
					key;
				for (key in node) {
					if (node.hasOwnProperty(key)) {
						strHtml = strHtml.replace(new RegExp('\\{' + key + '\\}', 'gi'), node[key]);
					}
				}
				return $(strHtml);
			},
			traverseParent: function (node, callback) {
				var parentNode = node.parent()
					.parent(this.options.wrapInnerTag);
				if (parentNode.html() === undefined) {
					return;
				}
				callback.apply(parentNode);
				this.traverseParent(parentNode, callback);
			},
			traverseChildren: function (node, callback) {
				var childNodes = $(node)
					.children(this.options.wrapOutTag)
					.children(),
					i;
				if (childNodes.html() === undefined) {
					return;
				}
				for (i = 0; i < childNodes.length; i++) {
					callback.apply(childNodes[i]);
					this.traverseChildren(childNodes[i], callback);
				}
			},
			traverseBrother: function (node, callback) {
				var brotherNodes = $(node)
					.parent()
					.children(this.options.wrapInnerTag),
					i;
				for (i = 0; i < brotherNodes.length; i++) {
					if (brotherNodes[i] === node) {
						continue;
					}
					callback.apply(brotherNodes[i]);
				}
			}
		}
	});
}));
<commit_msg>Add data sorting<commit_after>/*
 * onetree
 * https://github.com/xuejp/onetree
 *
 * Copyright (c) 2015 xuejp
 * Licensed under the APL license.
 */

( function ( factory ) {
	if ( typeof define === 'function' && define.amd ) {
		define( 'onetree', [ 'jquery' ], factory );
	} else {
		factory( jQuery );
	}
}( function ( $ ) {
	'use strict';

	$.fn.extend( {
		onetree: function ( options ) {
			var element = this,
				dataList = [];
			options = $.extend( true, {}, $.onetree.options, options );
			dataList = $.onetree.generateLinkedList( options.data, function ( result ) {
				result.sort( options.sortCallback );
			} );
			if ( options.root.append ) {
				dataList = [ {
					node: options.root.node,
					subnode: dataList
				} ];
			}
			$( element )
				.empty()
				.append( $.onetree.generateHtml( dataList,
					options.wrapOutTag,
					options.wrapInnerTag,
					options.contentTemplate,
					options.callback ) );

			return $.fn.menutree;
		}
	} );

	$.extend( {
		onetree: {
			options: {
				data: [],
				root: {
					append: false,
					node: {
						ID: 0,
						PID: -1
					}
				},
				sortCallback: null,
				wrapOutTag: 'ul',
				wrapInnerTag: 'li',
				contentTemplate: '',
				callback: null
			},
			generateLinkedList: function ( data, callback ) {
				var result = [],
					i,
					rootNode = this.findRootNodes( data );
				for ( i = 0; i < rootNode.length; i++ ) {
					result.push( this._generateSubTree( rootNode[ i ], data, callback ) );
				}
				if ( 'function' === typeof callback ) {
					callback( result );
				}
				return result;
			},
			convertToDictionary: function ( data ) {
				var result = [],
					i;
				for ( i = 0; i < data.length; i++ ) {
					result[ data[ i ].ID.toString() ] = data[ i ];
				}
				return result;
			},
			findRootNodes: function ( data ) {
				var result = [],
					key,
					dictionary = this.convertToDictionary( data );
				for ( key in dictionary ) {
					if ( dictionary.hasOwnProperty( key ) && dictionary[ dictionary[ key ].PID.toString() ] === undefined ) {
						result.push( dictionary[ key ] );
					}
				}
				return result;
			},
			_generateSubTree: function ( fNode, data, callback ) {
				var result = [],
					i;
				for ( i = 0; i < data.length; i++ ) {
					if ( data[ i ].PID === fNode.ID ) {
						result.push( this._generateSubTree( data[ i ], data ) );
					}
				}
				if ( 'function' === typeof callback ) {
					callback( result );
				}
				return {
					node: fNode,
					subnode: result
				};
			},
			generateHtml: function ( linkedList, wrapOutTag, wrapInnerTag, contentTemplate, callback ) {
				var $html,
					i;
				$html = $( document.createElement( wrapOutTag ) );
				for ( i = 0; i < linkedList.length; i++ ) {
					$html.append( this._generateNodeHtml( linkedList[ i ], wrapOutTag, wrapInnerTag, contentTemplate, callback ) );
				}
				return $html;
			},
			_generateNodeHtml: function ( node, wrapOutTag, wrapInnerTag, contentTemplate, callback ) {
				var $nodeHtml,
					$nodelistHtml,
					i;
				$nodeHtml = $( document.createElement( wrapInnerTag ) )
					.append( this._generateContentHtml( node.node, contentTemplate, callback ) );
				if ( callback !== null ) {
					callback( $nodeHtml, node.node );
				}
				if ( node.subnode.length > 0 ) {
					$nodelistHtml = $( document.createElement( wrapOutTag ) );
					for ( i = 0; i < node.subnode.length; i++ ) {
						$nodelistHtml.append( this._generateNodeHtml( node.subnode[ i ], wrapOutTag, wrapInnerTag, contentTemplate, callback ) );
					}
					$nodeHtml.append( $nodelistHtml );
				}
				return $nodeHtml;
			},
			_generateContentHtml: function ( node, contentTemplate ) {
				var strHtml = contentTemplate,
					key;
				for ( key in node ) {
					if ( node.hasOwnProperty( key ) ) {
						strHtml = strHtml.replace( new RegExp( '\\{' + key + '\\}', 'gi' ), node[ key ] );
					}
				}
				return $( strHtml );
			},
			traverseParent: function ( node, callback ) {
				var parentNode = node.parent()
					.parent( this.options.wrapInnerTag );
				if ( parentNode.html() === undefined ) {
					return;
				}
				callback.apply( parentNode );
				this.traverseParent( parentNode, callback );
			},
			traverseChildren: function ( node, callback ) {
				var childNodes = $( node )
					.children( this.options.wrapOutTag )
					.children(),
					i;
				if ( childNodes.html() === undefined ) {
					return;
				}
				for ( i = 0; i < childNodes.length; i++ ) {
					callback.apply( childNodes[ i ] );
					this.traverseChildren( childNodes[ i ], callback );
				}
			},
			traverseBrother: function ( node, callback ) {
				var brotherNodes = $( node )
					.parent()
					.children( this.options.wrapInnerTag ),
					i;
				for ( i = 0; i < brotherNodes.length; i++ ) {
					if ( brotherNodes[ i ] === node ) {
						continue;
					}
					callback.apply( brotherNodes[ i ] );
				}
			}
		}
	} );
} ) );
<|endoftext|>