<commit_before>/*!
 * tdigital-hybridge - v1.3.2
 * Bridge for mobile hybrid application between Javascript and native environment
 * (iOS & Android)
 *
 * Copyright 2013 Telefonica Investigación y Desarrollo, S.A.U
 * Licensed under MIT License.
 *
 * 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 NON INFRINGEMENT. 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.
 */

(function (root, factory) {
  if (typeof define === 'function' && define.amd) {
    // AMD. Register as an anonymous module.
    define(['jquery'], factory);
  } else {
    // Browser globals
    root.Hybridge = factory(root.jQuery);
  }
}(this, function ($) {
  'use strict';

  var READY_EVENT = 'ready';
  var INIT_ACTION = 'init';
  var CUSTOM_DATA_OBJ = 'customData';

  var version = 1, versionMinor = 3, initialized = false,
    method, logger, environment, debug, mockResponses, _events = {}, _actions = [], _errors,
    initModuleDef = $.Deferred(), initGlobalDef = $.Deferred(), initCustomDataDef = $.Deferred();

  /**
   * Sets init configuration (native environment, logger)
   * @param {Object} ( environment: ios | android, customData ).
   * @param {Function} customDataCb
   */
  function _init (conf, customDataCb) {
    environment = conf.environment || '';
    logger = conf.logger || null;
    debug = conf.debug || false;
    mockResponses = conf.mockResponses || null;
    /**
     * Sets up the bridge for debug mode (only browser)
     */
    if (debug) {
     _getLogger().info('Fixing bridge for debug mode');
     _mockHybridgeGlobal(conf);
    }
    /**
     * Sets up the bridge in iOS environment
     */
    else if (_isIos()) {
      _getLogger().info('Fixing bridge for iOS, XHR method used');
      method = _sendXHR;
    }
    /**
    * Sets up the bridge in Android environment
    */
    else if (_isAndroid()) {
      _getLogger().info('Fixing bridge for Android, prompt method used');
      method = _sendPrompt;
    }
    $.when(initCustomDataDef).then(customDataCb);

    return initModuleDef.resolve(conf).promise();
  }

  /**
   * Notify native of javascript initialization
   * @return {Object} Promise returned from send method
   */
  function _initNative (deferredModule, deferredGlobal) {
    _send({
      'action' : INIT_ACTION,
      'initialized' : deferredGlobal.initialized,
      'version' : version,
      'versionMinor' : versionMinor
    });
  }

  /**
   * Returns the logger object
   * @return {Object}
   */
  function _getLogger () {
    if (!logger) {
      logger = window.console;
    }
    return logger;
  }

  /**
   * Checks if the current native environment is native
   * @return {Boolean}
   */
  function _isNative () {
    return (_isIos() || _isAndroid());
  }

  /**
   * Checks if the current native environment is iOS
   * @return {Boolean}
   */
  function _isIos () {
    return environment === 'ios';
  }

  /**
   * Checks if the current native environment is Android
   * @return {Boolean}
   */
  function _isAndroid () {
    return environment === 'android';
  }

  /**
   * Checks if the bridge has been started from native
   * @return {Boolean}
   */
  function _isEnabled () {
    return !!(debug || (window.HybridgeGlobal && window.HybridgeGlobal.isReady && initialized));
  }

  /**
   * Checks if the current action is implemented in native
   * @param  {String}  action
   * @return {Boolean}
   */
  function _isActionImplemented (action) {
    return !!(window.HybridgeGlobal && window.HybridgeGlobal.actions &&
      $.inArray(action, window.HybridgeGlobal.actions) !== -1 );
  }

  /**
   * Checks if the current event is implemented in native
   * @param  {String}  eventType
   * @return {Boolean}
   */
  function _isEventImplemented (event) {
    return !!((event && event.type === READY_EVENT) ||
      (window.HybridgeGlobal && window.HybridgeGlobal.events && event && event.type &&
      $.inArray(event.type, window.HybridgeGlobal.events) !== -1));
  }

  /**
   * Checks the current environment and forwards to the proper bridge method
   * @param  {Object} data
   * @param  {Function} fallbackFn
   * @return {Promise}
   */
  function _send (data, fallbackFn) {
    var error, warning, details, mock;
    // Is mode debug on
    if (debug) {
      // Fire the ready event as a response for the init action
      if (data.action == INIT_ACTION) {
        _fireEvent(READY_EVENT, {});
      } else if (mockResponses && mockResponses[data.action]) {
        mock = $.extend({}, data, mockResponses[data.action]);
        try {
          return $.Deferred().resolve(
            JSON.parse(window.prompt('Hybridge Debug - JSON response:', JSON.stringify(mock)))
          ).promise();
        }
        catch (e) {
          error = _errors.MALFORMED_JSON;
          details = e.message;
        }
      }
      else {
        warning = _errors.DEBUG_MODE;
        details = data.action;
      }
    }
    // Is a native environment
    else if(_isNative()) {
      // Native bridge is enabled
      if (_isEnabled()) {
        if (data.action) {
          if (_isActionImplemented(data.action)) {
            return method(data);
          }
          else {
            error = _errors.ACTION_NOT_IMPLEMENTED;
            details = data.action;
          }
        }
        else {
          error = _errors.WRONG_PARAMS;
          details = JSON.stringify(data);
        }
      }
      // Native bridge is disabled, try fallback function
      else if (fallbackFn) {
        error = _errors.NO_NATIVE_ENABLED;
        var def = $.Deferred();
        def.then(null, fallbackFn);
        def.reject({'error' : error});
        return def.promise();
      }
      else {
        error = _errors.NO_FALLBACK;
      }
    }
    else {
      error =  _errors.NO_NATIVE;
    }
    return $.Deferred().reject({'error' : error, 'warning' : warning, 'details' : details}).promise();
  }

  /**
   * Provides prompt method override bridge method for Android environment
   * @param  {Object} data
   * @return {Promise}
   */
  function _sendPrompt (data) {
    var def = $.Deferred();
    var strJSON = JSON.stringify(data);
    var action = data.action;
    var result = null;
    setTimeout(function() {
      try {
        result = JSON.parse(window.prompt(action, strJSON) || '{}');
        def.resolve(result);
      }
      catch (e) {
        _getLogger().error('Hybridge: Error on prompt processing');
        def.reject({'error' : e.message});
      }
    });
    return def.promise();
  }

  /**
   * Provides XHR bridge method for iOS environment
   * Warning: Fixed to work with JQuery 1.10.2
   * Info: tested from 1.8.3, 1.10.2, ... 2.1.1 for beforeSend
   * @param  {Object} data
   * @return {Promise}
   */
  function _sendXHR (data) {
    var strJSON = JSON.stringify(data);
    var def = $.Deferred();
    var action = data.action;
    var id = data.id;
    $.ajax({
      url: 'http://hybridge/' + action + '/' + id + '/' + new Date().getTime(),
      type: 'HEAD',
      headers: { 'data': strJSON || '{}' },
      beforeSend: function (xhr) {
        xhr.done(function () {
          if (xhr.status === 200) {
            _getLogger().info('Hybridge: ' + action + ": " + xhr.statusText);
            def.resolve(JSON.parse(xhr.responseText || '{}'));
          }
          else {
            _getLogger().error('Hybridge: ' + xhr.statusText);
            def.reject({'error': 'HTTP error: ' + xhr.status});
          }
        }).fail(function (xhr, text, textError) {
          _getLogger().error('Error on bridge to native. Non native environment?',
              xhr, text, textError);
        });
      }
    });
    return def.promise();
  }

  /**
   * Method required for backwards compatibility
   * with DOM Level 2 event creation (Android Webkit)
   * @param  {String} name
   * @return {Event}
   */
  var _createEvent = function (name) {
    var ev = null, CE = window.CustomEvent;
    // DOM Level 3
    if (typeof CE !== 'undefined') {
      ev = new CE(name);
    }
    // DOM Level 2
    else {
      ev = document.createEvent('Event');
      ev.initEvent(name, true, true);
    }
    return ev;
  };

  /**
   * Add a handler to a Hybridge event if present
   * @param {Event} event
   * @param {Function} callback
   */
  var _addListener = function (event, callback) {
    if (_isEventImplemented(event)) {
      document.addEventListener(event.type, callback, false);
    }
    else if (debug) {
      _getLogger().log('Hybridge: ' + _errors.DEBUG_MODE);
    }
    else {
      _getLogger().error('Hybridge: ' + _errors.EVENT_NOT_IMPLEMENTED, event);
    }
  };

  /**
   * Removes a handler from a Hybridge event
   * @param {Event} event
   * @param {Function} callback
   */
  var _removeListener = function (event, callback) {
    if (_isEventImplemented(event)) {
      document.removeEventListener(event.type, callback, false);
    }
    else if (debug) {
      _getLogger().log('Hybridge: ' + _errors.DEBUG_MODE);
    }
    else {
      _getLogger().error('Hybridge: ' + _errors.EVENT_NOT_IMPLEMENTED, event);
    }
  };

  /**
   * Creates a mock for the HybridgeGlobal object, as created by the native app.
   */
  var _mockHybridgeGlobal = function (conf) {
    window.HybridgeGlobal || setTimeout(function() {
        window.HybridgeGlobal = {
          isReady: true,
          version: version,
          versionMinor: versionMinor,
          actions: [INIT_ACTION, 'message'],
          events: [READY_EVENT, 'message'],
          customData: conf.customData || {}
        };
      }, 0);
  };

  /**
   * Attach methods to global object in order to initialize the Hybridge properly
   * _events: Hybridge events triggered from native for client handling
   */
  var _attachToGlobal = function () {
    var event, globalEvents, globalActions;
    if (window.HybridgeGlobal && (globalEvents = window.HybridgeGlobal.events)) {
      for (var i = 0; i < globalEvents.length; i++) {
        event = globalEvents[i];
        if (!_events[event]) {
          _events[event] = _createEvent(event);
        }
      }
    }
    if (window.HybridgeGlobal && (globalActions = window.HybridgeGlobal.actions)) {
      for (var i in globalActions) {
        if (globalActions.hasOwnProperty(i)) {
         _actions.push(globalActions[i]);
        }
      }
    }
    if (window.HybridgeGlobal && (globalActions = window.HybridgeGlobal.customData)) {
      Hybridge[CUSTOM_DATA_OBJ] = $.extend({}, window.HybridgeGlobal.customData);
      initCustomDataDef.resolve(Hybridge[CUSTOM_DATA_OBJ]).promise();
    }

    initialized = true;
    window.HybridgeGlobal.fireEvent = _fireEvent;
    window.HybridgeGlobal.initialized = initialized;
    return initGlobalDef.resolve({'initialized':initialized}).promise();
  };

  /**
   * Global method used from native to trigger events (scope HybridgeGlobal)
   */
  var _fireEvent = function (type, data) {
    if (_events[type]) {
      _events[type].data = data;
      return document.dispatchEvent(_events[type]);
    }
    else {
      _getLogger().error('Hybridge event not defined: ' + type);
    }
  };

  /**
   * Function to notify whenever Hybridge becomes or is enabled/ready
   *
   * If hybridge is ready at calling time, the callback is inmediatelly executed
   *
   * @param {Function} cb  Callback to be called once Hybridge is ready
   */
  function _ready(cb) {
    if (_isEnabled()) {
      cb();
    } else {
      _addListener(_events.ready, function onReady() {
        _removeListener(_events.ready, onReady);
        cb();
      });
    }
  }

  /**
   * Object containing different error types on rejecting requests (promises)
   * @type {Array}
   */
  _errors = {};
  /**
   * Environment is not mobile native (ios or android)
   */
  _errors.NO_NATIVE = 'NO_NATIVE';
  /**
   * Native environment doesn't support native Hybridge
   */
  _errors.NO_NATIVE_ENABLE = 'NO_NATIVE_ENABLED';
  /**
   * Call to hybridge lacks of fallback function
   */
  _errors.NO_FALLBACK = 'NO_FALLBACK';
  /**
   * Call to hybridge action not implemented in native
   */
  _errors.ACTION_NOT_IMPLEMENTED = 'ACTION_NOT_IMPLEMENTED';
  /**
   * Call to hybridge doesn't have required parameters (action)
   */
  _errors.WRONG_PARAMS = 'WRONG_PARAMS';
  /**
   * Call to hybridge event not implemented in native
   */
  _errors.EVENT_NOT_IMPLEMENTED = 'EVENT_NOT_IMPLEMENTED';
  /**
   * Hybridge in debug mode, requested feature is unavailable
   */
  _errors.DEBUG_MODE = 'DEBUG_MODE';
  /**
   * Hybridge attempted to parse or stringify malformed JSON (debug mode)
   */
  _errors.MALFORMED_JSON = 'MALFORMED_JSON';

  /**
   * Public returned Hybridge object
   * @type {Object}
   */
  var Hybridge = {
    init: _init,
    version: version,
    versionMinor: versionMinor,
    isNative: _isNative,
    isEnabled: _isEnabled,
    addListener: _addListener,
    removeListener: _removeListener,
    isEventImplemented: _isEventImplemented,
    isActionImplemented: _isActionImplemented,
    send: _send,
    events: _events,
    actions: _actions,
    errors: _errors,
    ready: _ready
  };

  /**
   * Inits ready event
   */
  _events.ready = _createEvent(READY_EVENT);

  /**
   * window.HybridgeGlobal is set by the Native code in other thread outside
   * the browser event loop.
   * Lets going to poll for it and then we can be sure native has done
   * its part in the handshake
   *
   * TODO: Refactor the handshake protocol, forcing native to start once the
   * webview is DOMReady and a document the minimun initial DOM for using
   * the old CSS trigger from version 1.2.0
   */
  (function _checkForGlobal() {
    if (window.HybridgeGlobal) {
      _attachToGlobal();
    } else {
      setTimeout(_checkForGlobal, 10);
    }
  })();

  /**
   * Initialize both native and javascript
   */
  $.when(initModuleDef, initGlobalDef).then(_initNative);

  return Hybridge;
}));
<commit_msg>EDIT to fix initialization<commit_after>/*!
 * tdigital-hybridge - v1.3.2
 * Bridge for mobile hybrid application between Javascript and native environment
 * (iOS & Android)
 *
 * Copyright 2013 Telefonica Investigación y Desarrollo, S.A.U
 * Licensed under MIT License.
 *
 * 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 NON INFRINGEMENT. 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.
 */

(function (root, factory) {
  if (typeof define === 'function' && define.amd) {
    // AMD. Register as an anonymous module.
    define(['jquery'], factory);
  } else {
    // Browser globals
    root.Hybridge = factory(root.jQuery);
  }
}(this, function ($) {
  'use strict';

  var READY_EVENT = 'ready';
  var INIT_ACTION = 'init';
  var CUSTOM_DATA_OBJ = 'customData';

  var version = 1, versionMinor = 3, initialized = false,
    method, logger, environment, debug, mockResponses, _events = {}, _actions = [], _errors,
    initModuleDef = $.Deferred(), initGlobalDef = $.Deferred(), initCustomDataDef = $.Deferred();

  /**
   * Sets init configuration (native environment, logger)
   * @param {Object} ( environment: ios | android, customData ).
   * @param {Function} customDataCb
   */
  function _init (conf, customDataCb) {
    environment = conf.environment || '';
    logger = conf.logger || null;
    debug = conf.debug || false;
    mockResponses = conf.mockResponses || null;
    /**
     * Sets up the bridge for debug mode (only browser)
     */
    if (debug) {
     _getLogger().info('Fixing bridge for debug mode');
     _mockHybridgeGlobal(conf);
    }
    /**
     * Sets up the bridge in iOS environment
     */
    else if (_isIos()) {
      _getLogger().info('Fixing bridge for iOS, XHR method used');
      method = _sendXHR;
    }
    /**
    * Sets up the bridge in Android environment
    */
    else if (_isAndroid()) {
      _getLogger().info('Fixing bridge for Android, prompt method used');
      method = _sendPrompt;
    }
    $.when(initCustomDataDef).then(customDataCb);

    return initModuleDef.resolve(conf).promise();
  }

  /**
   * Notify native of javascript initialization
   * @return {Object} Promise returned from send method
   */
  function _initNative (deferredModule, deferredGlobal) {
    _send({
      'action' : INIT_ACTION,
      'initialized' : deferredGlobal.initialized,
      'version' : version,
      'versionMinor' : versionMinor
    });
  }

  /**
   * Returns the logger object
   * @return {Object}
   */
  function _getLogger () {
    if (!logger) {
      logger = window.console;
    }
    return logger;
  }

  /**
   * Checks if the current native environment is native
   * @return {Boolean}
   */
  function _isNative () {
    return (_isIos() || _isAndroid());
  }

  /**
   * Checks if the current native environment is iOS
   * @return {Boolean}
   */
  function _isIos () {
    return environment === 'ios';
  }

  /**
   * Checks if the current native environment is Android
   * @return {Boolean}
   */
  function _isAndroid () {
    return environment === 'android';
  }

  /**
   * Checks if the bridge has been started from native
   * @return {Boolean}
   */
  function _isEnabled () {
    return !!(debug || (window.HybridgeGlobal && window.HybridgeGlobal.isReady && initialized));
  }

  /**
   * Checks if the current action is implemented in native
   * @param  {String}  action
   * @return {Boolean}
   */
  function _isActionImplemented (action) {
    return !!(window.HybridgeGlobal && window.HybridgeGlobal.actions &&
      $.inArray(action, window.HybridgeGlobal.actions) !== -1 );
  }

  /**
   * Checks if the current event is implemented in native
   * @param  {String}  eventType
   * @return {Boolean}
   */
  function _isEventImplemented (event) {
    return !!((event && event.type === READY_EVENT) ||
      (window.HybridgeGlobal && window.HybridgeGlobal.events && event && event.type &&
      $.inArray(event.type, window.HybridgeGlobal.events) !== -1));
  }

  /**
   * Checks the current environment and forwards to the proper bridge method
   * @param  {Object} data
   * @param  {Function} fallbackFn
   * @return {Promise}
   */
  function _send (data, fallbackFn) {
    var error, warning, details, mock;
    // Is mode debug on
    if (debug) {
      // Fire the ready event as a response for the init action
      if (data.action == INIT_ACTION) {
        _fireEvent(READY_EVENT, {});
      } else if (mockResponses && mockResponses[data.action]) {
        mock = $.extend({}, data, mockResponses[data.action]);
        try {
          return $.Deferred().resolve(
            JSON.parse(window.prompt('Hybridge Debug - JSON response:', JSON.stringify(mock)))
          ).promise();
        }
        catch (e) {
          error = _errors.MALFORMED_JSON;
          details = e.message;
        }
      }
      else {
        warning = _errors.DEBUG_MODE;
        details = data.action;
      }
    }
    // Is a native environment
    else if(_isNative()) {
      // Native bridge is enabled
      if (_isEnabled()) {
        if (data.action) {
          if (_isActionImplemented(data.action)) {
            return method(data);
          }
          else {
            error = _errors.ACTION_NOT_IMPLEMENTED;
            details = data.action;
          }
        }
        else {
          error = _errors.WRONG_PARAMS;
          details = JSON.stringify(data);
        }
      }
      // Native bridge is disabled, try fallback function
      else if (fallbackFn) {
        error = _errors.NO_NATIVE_ENABLED;
        var def = $.Deferred();
        def.then(null, fallbackFn);
        def.reject({'error' : error});
        return def.promise();
      }
      else {
        error = _errors.NO_FALLBACK;
      }
    }
    else {
      error =  _errors.NO_NATIVE;
    }
    return $.Deferred().reject({'error' : error, 'warning' : warning, 'details' : details}).promise();
  }

  /**
   * Provides prompt method override bridge method for Android environment
   * @param  {Object} data
   * @return {Promise}
   */
  function _sendPrompt (data) {
    var def = $.Deferred();
    var strJSON = JSON.stringify(data);
    var action = data.action;
    var result = null;
    setTimeout(function() {
      try {
        result = JSON.parse(window.prompt(action, strJSON) || '{}');
        def.resolve(result);
      }
      catch (e) {
        _getLogger().error('Hybridge: Error on prompt processing');
        def.reject({'error' : e.message});
      }
    });
    return def.promise();
  }

  /**
   * Provides XHR bridge method for iOS environment
   * Warning: Fixed to work with JQuery 1.10.2
   * Info: tested from 1.8.3, 1.10.2, ... 2.1.1 for beforeSend
   * @param  {Object} data
   * @return {Promise}
   */
  function _sendXHR (data) {
    var strJSON = JSON.stringify(data);
    var def = $.Deferred();
    var action = data.action;
    var id = data.id;
    $.ajax({
      url: 'http://hybridge/' + action + '/' + id + '/' + new Date().getTime(),
      type: 'HEAD',
      headers: { 'data': strJSON || '{}' },
      beforeSend: function (xhr) {
        xhr.done(function () {
          if (xhr.status === 200) {
            _getLogger().info('Hybridge: ' + action + ": " + xhr.statusText);
            def.resolve(JSON.parse(xhr.responseText || '{}'));
          }
          else {
            _getLogger().error('Hybridge: ' + xhr.statusText);
            def.reject({'error': 'HTTP error: ' + xhr.status});
          }
        }).fail(function (xhr, text, textError) {
          _getLogger().error('Error on bridge to native. Non native environment?',
              xhr, text, textError);
        });
      }
    });
    return def.promise();
  }

  /**
   * Method required for backwards compatibility
   * with DOM Level 2 event creation (Android Webkit)
   * @param  {String} name
   * @return {Event}
   */
  var _createEvent = function (name) {
    var ev = null, CE = window.CustomEvent;
    // DOM Level 3
    if (typeof CE !== 'undefined') {
      ev = new CE(name);
    }
    // DOM Level 2
    else {
      ev = document.createEvent('Event');
      ev.initEvent(name, true, true);
    }
    return ev;
  };

  /**
   * Add a handler to a Hybridge event if present
   * @param {Event} event
   * @param {Function} callback
   */
  var _addListener = function (event, callback) {
    if (_isEventImplemented(event)) {
      document.addEventListener(event.type, callback, false);
    }
    else if (debug) {
      _getLogger().log('Hybridge: ' + _errors.DEBUG_MODE);
    }
    else {
      _getLogger().error('Hybridge: ' + _errors.EVENT_NOT_IMPLEMENTED, event);
    }
  };

  /**
   * Removes a handler from a Hybridge event
   * @param {Event} event
   * @param {Function} callback
   */
  var _removeListener = function (event, callback) {
    if (_isEventImplemented(event)) {
      document.removeEventListener(event.type, callback, false);
    }
    else if (debug) {
      _getLogger().log('Hybridge: ' + _errors.DEBUG_MODE);
    }
    else {
      _getLogger().error('Hybridge: ' + _errors.EVENT_NOT_IMPLEMENTED, event);
    }
  };

  /**
   * Creates a mock for the HybridgeGlobal object, as created by the native app.
   */
  var _mockHybridgeGlobal = function (conf) {
    window.HybridgeGlobal || setTimeout(function() {
        window.HybridgeGlobal = {
          isReady: true,
          version: version,
          versionMinor: versionMinor,
          actions: [INIT_ACTION, 'message'],
          events: [READY_EVENT, 'message'],
          customData: conf.customData || {}
        };
      }, 0);
  };

  /**
   * Attach methods to global object in order to initialize the Hybridge properly
   * _events: Hybridge events triggered from native for client handling
   */
  var _attachToGlobal = function () {
    var event, globalEvents, globalActions;
    if (window.HybridgeGlobal && (globalEvents = window.HybridgeGlobal.events)) {
      for (var i = 0; i < globalEvents.length; i++) {
        event = globalEvents[i];
        if (!_events[event]) {
          _events[event] = _createEvent(event);
        }
      }
    }
    if (window.HybridgeGlobal && (globalActions = window.HybridgeGlobal.actions)) {
      for (var i in globalActions) {
        if (globalActions.hasOwnProperty(i)) {
         _actions.push(globalActions[i]);
        }
      }
    }
    if (window.HybridgeGlobal && (window.HybridgeGlobal.customData)) {
      Hybridge[CUSTOM_DATA_OBJ] = $.extend({}, window.HybridgeGlobal.customData);
      initCustomDataDef.resolve(Hybridge[CUSTOM_DATA_OBJ]).promise();
    }

    initialized = true;
    window.HybridgeGlobal.fireEvent = _fireEvent;
    window.HybridgeGlobal.initialized = initialized;
    return initGlobalDef.resolve({'initialized':initialized}).promise();
  };

  /**
   * Global method used from native to trigger events (scope HybridgeGlobal)
   */
  var _fireEvent = function (type, data) {
    if (_events[type]) {
      _events[type].data = data;
      return document.dispatchEvent(_events[type]);
    }
    else {
      _getLogger().error('Hybridge event not defined: ' + type);
    }
  };

  /**
   * Function to notify whenever Hybridge becomes or is enabled/ready
   *
   * If hybridge is ready at calling time, the callback is inmediatelly executed
   *
   * @param {Function} cb  Callback to be called once Hybridge is ready
   */
  function _ready(cb) {
    if (_isEnabled()) {
      cb();
    } else {
      _addListener(_events.ready, function onReady() {
        _removeListener(_events.ready, onReady);
        cb();
      });
    }
  }

  /**
   * Object containing different error types on rejecting requests (promises)
   * @type {Array}
   */
  _errors = {};
  /**
   * Environment is not mobile native (ios or android)
   */
  _errors.NO_NATIVE = 'NO_NATIVE';
  /**
   * Native environment doesn't support native Hybridge
   */
  _errors.NO_NATIVE_ENABLE = 'NO_NATIVE_ENABLED';
  /**
   * Call to hybridge lacks of fallback function
   */
  _errors.NO_FALLBACK = 'NO_FALLBACK';
  /**
   * Call to hybridge action not implemented in native
   */
  _errors.ACTION_NOT_IMPLEMENTED = 'ACTION_NOT_IMPLEMENTED';
  /**
   * Call to hybridge doesn't have required parameters (action)
   */
  _errors.WRONG_PARAMS = 'WRONG_PARAMS';
  /**
   * Call to hybridge event not implemented in native
   */
  _errors.EVENT_NOT_IMPLEMENTED = 'EVENT_NOT_IMPLEMENTED';
  /**
   * Hybridge in debug mode, requested feature is unavailable
   */
  _errors.DEBUG_MODE = 'DEBUG_MODE';
  /**
   * Hybridge attempted to parse or stringify malformed JSON (debug mode)
   */
  _errors.MALFORMED_JSON = 'MALFORMED_JSON';

  /**
   * Public returned Hybridge object
   * @type {Object}
   */
  var Hybridge = {
    init: _init,
    version: version,
    versionMinor: versionMinor,
    isNative: _isNative,
    isEnabled: _isEnabled,
    addListener: _addListener,
    removeListener: _removeListener,
    isEventImplemented: _isEventImplemented,
    isActionImplemented: _isActionImplemented,
    send: _send,
    events: _events,
    actions: _actions,
    errors: _errors,
    ready: _ready
  };

  /**
   * Inits ready event
   */
  _events.ready = _createEvent(READY_EVENT);

  /**
   * window.HybridgeGlobal is set by the Native code in other thread outside
   * the browser event loop.
   * Lets going to poll for it and then we can be sure native has done
   * its part in the handshake
   *
   * TODO: Refactor the handshake protocol, forcing native to start once the
   * webview is DOMReady and a document the minimun initial DOM for using
   * the old CSS trigger from version 1.2.0
   */
  (function _checkForGlobal() {
    if (window.HybridgeGlobal) {
      _attachToGlobal();
    } else {
      setTimeout(_checkForGlobal, 10);
    }
  })();

  /**
   * Initialize both native and javascript
   */
  $.when(initModuleDef, initGlobalDef).then(_initNative);

  return Hybridge;
}));
<|endoftext|>


<commit_before>(function(window){

	if('function' === typeof window.highlight)
	{
		window.highlight.sql=[
			{
				'class':'string',
				'match':/([bn]?"(?:[^"]|[\\"]")*"|[bn]?'(?:[^']|[\\']')*')(?=[\b\s\(\),;\$#]|$)/g,
				'replace':window.highlight.default_replace
			},
			{
				'class':'comment',
				'match':/((?:\/\/|\-\-\s|#)[^\r\n]*|\/\*(?:[^*]|\*[^\/])*(?:\*\/|$))/g,
				'replace':window.highlight.default_replace,
				'patch':function(){
					return this.innerHTML.replace(
							/((?:\/\/|\-\-\s|#)[^\r\n]*|\/\*(?:[^*]|\*[^\/])*(?:\*\/|$))/g,
							'$1'
						).replace(
							/((?:#|-- )(?:.|<\/span>([^<])<\/span>)*)([\r\n]|$)/g,
							function(_,part1,part2,part3){
								return ''+
									((part1||'')+(part2||'')).replace(/<\/?span(?: class="[^"]+")?>/g,'')+
									''+
									(part3||'');
							}
						);
				}
			},
			{
				/*
				 * numbers aren't that 'regular' and many edge-cases were left behind
				 * with the help of @MLM (http://stackoverflow.com/users/796832/mlm),
				 * we were able to make this work.
				 * he took over the regex and patched it all up, I did the replace string
				 */
                'match':/((?:^|\b|\(|\s|,))(?![a-z_]+)([+\-]?\d+(?:\.\d+)?(?:[eE]-?\d+)?)((?=$|\b|\s|\(|\)|,|;))/g,
				'replace':'$1$2$3'
			},
			{
				'class':'name',
				'match':/(`[^`]+`)/g,
				'replace':window.highlight.default_replace
			},
			{
				'class':'var',
				'match':/(@@?[a-z_][a-z_\d]*)/g,
				'replace':window.highlight.default_replace
			},
			{
				'class':'keyword',
				//the keyword replace must have an aditional check (`(?!\()` after the name), due to the function replace()
				'match':/\b(accessible|add|all|alter|analyze|and|as|asc|asensitive|before|between|bigint|binary|blob|both|by|call|cascade|case|change|char|character|check|collate|column|condition|constraint|continue|convert|create|cross|current_date|current_time|current_timestamp|current_user|cursor|database|databases|day_hour|day_microsecond|day_minute|day_second|dec|decimal|declare|default|delayed|delete|desc|describe|deterministic|distinct|distinctrow|div|double|drop|dual|each|else|elseif|enclosed|escaped|exists|exit|explain|false|fetch|float|float4|float8|for|force|foreign|from|fulltext|generated|get|grant|group|having|high_priority|hour_microsecond|hour_minute|hour_second|if|ignore|in|index|infile|inner|inout|insensitive|insert|int|int1|int2|int3|int4|int8|integer|interval|into|io_after_gtids|io_before_gtids|is|iterate|join|key|keys|kill|leading|leave|left|like|limit|linear|lines|load|localtime|localtimestamp|lock|long|longblob|longtext|loop|low_priority|master_bind|master_ssl_verify_server_cert|match|maxvalue|mediumblob|mediumint|mediumtext|middleint|minute_microsecond|minute_second|mod|modifies|natural|nonblocking|not|no_write_to_binlog|null|numeric|on|optimize|optimizer_costs|option|optionally|or|order|out|outer|outfile|parse_gcol_expr|partition|precision|primary|procedure|purge|range|read|reads|read_write|real|references|regexp|release|rename|repeat|replace(?!\()|require|resignal|restrict|return|revoke|right|rlike|schema|schemas|second_microsecond|select|sensitive|separator|set|show|signal|smallint|spatial|specific|sql|sqlexception|sqlstate|sqlwarning|sql_big_result|sql_calc_found_rows|sql_small_result|ssl|starting|stored|straight_join|table|terminated|then|tinyblob|tinyint|tinytext|to|trailing|trigger|true|undo|union|unique|unlock|unsigned|update|usage|use|using|utc_date|utc_time|utc_timestamp|values|varbinary|varchar|varcharacter|varying|virtual|when|where|while|with|write|xor|year_month|zerofill)\b/gi,
				'replace':window.highlight.default_replace
			},
			{
				'class':'func',
				'match':/\b([a-z_][a-z_\d]*)\b(?=\()/gi,
				'replace':window.highlight.default_replace
			},
			{
				'class':'name',
				'match':/\b([a-z\_][a-z_\d]*)\b/gi,
				'replace':window.highlight.default_replace
			}
		];
	}

})(Function('return this')());
<commit_msg>Added patch for multi-line comments.<commit_after>(function(window){

	if('function' === typeof window.highlight)
	{
		window.highlight.sql=[
			{
				'class':'string',
				'match':/([bn]?"(?:[^"]|[\\"]")*"|[bn]?'(?:[^']|[\\']')*')(?=[\b\s\(\),;\$#]|$)/g,
				'replace':window.highlight.default_replace
			},
			{
				'class':'comment',
				'match':/((?:\/\/|\-\-\s|#)[^\r\n]*|\/\*(?:[^*]|\*[^\/])*(?:\*\/|$))/g,
				'replace':window.highlight.default_replace,
				'patch':function(){
					return this.innerHTML.replace(
							/((?:\/\/|\-\-\s|#)[^\r\n]*|\/\*(?:[^*]|\*[^\/])*(?:\*\/|$))/g,
							'$1'
						).replace(//matches single-line comments
							/((?:#|-- |\/\/)(?:.|<\/span>([^<])<\/span>)*)([\r\n]|$)/g,
							function(_,part1,part2,part3){
								return ''+
									//cleans up all spans
									((part1||'')+(part2||'')).replace(/<\/?span(?: class="[^"]+")?>/g,'')+
									''+
									(part3||'');
							}
						).replace(//matches multi-line comments
							/(\/\*(?:[^*]|\*[^\/])+(?:\*\/(?:<\/span>)?|$))/g,
							function(_,part1){
								return ''+
									//cleans up all spans
									((part1||'')).replace(/<\/?span(?: class="[^"]+")?>/g,'')+
									'';
							}
						);
				}
			},
			{
				/*
				 * numbers aren't that 'regular' and many edge-cases were left behind
				 * with the help of @MLM (http://stackoverflow.com/users/796832/mlm),
				 * we were able to make this work.
				 * he took over the regex and patched it all up, I did the replace string
				 */
				'match':/((?:^|\b|\(|\s|,))(?![a-z_]+)([+\-]?\d+(?:\.\d+)?(?:[eE]-?\d+)?)((?=$|\b|\s|\(|\)|,|;))/g,
				'replace':'$1$2$3'
			},
			{
				'class':'name',
				'match':/(`[^`]+`)/g,
				'replace':window.highlight.default_replace
			},
			{
				'class':'var',
				'match':/(@@?[a-z_][a-z_\d]*)/g,
				'replace':window.highlight.default_replace
			},
			{
				'class':'keyword',
				//the keyword replace must have an aditional check (`(?!\()` after the name), due to the function replace()
				'match':/\b(accessible|add|all|alter|analyze|and|as|asc|asensitive|before|between|bigint|binary|blob|both|by|call|cascade|case|change|char|character|check|collate|column|condition|constraint|continue|convert|create|cross|current_date|current_time|current_timestamp|current_user|cursor|database|databases|day_hour|day_microsecond|day_minute|day_second|dec|decimal|declare|default|delayed|delete|desc|describe|deterministic|distinct|distinctrow|div|double|drop|dual|each|else|elseif|enclosed|escaped|exists|exit|explain|false|fetch|float|float4|float8|for|force|foreign|from|fulltext|generated|get|grant|group|having|high_priority|hour_microsecond|hour_minute|hour_second|if|ignore|in|index|infile|inner|inout|insensitive|insert|int|int1|int2|int3|int4|int8|integer|interval|into|io_after_gtids|io_before_gtids|is|iterate|join|key|keys|kill|leading|leave|left|like|limit|linear|lines|load|localtime|localtimestamp|lock|long|longblob|longtext|loop|low_priority|master_bind|master_ssl_verify_server_cert|match|maxvalue|mediumblob|mediumint|mediumtext|middleint|minute_microsecond|minute_second|mod|modifies|natural|nonblocking|not|no_write_to_binlog|null|numeric|on|optimize|optimizer_costs|option|optionally|or|order|out|outer|outfile|parse_gcol_expr|partition|precision|primary|procedure|purge|range|read|reads|read_write|real|references|regexp|release|rename|repeat|replace(?!\()|require|resignal|restrict|return|revoke|right|rlike|schema|schemas|second_microsecond|select|sensitive|separator|set|show|signal|smallint|spatial|specific|sql|sqlexception|sqlstate|sqlwarning|sql_big_result|sql_calc_found_rows|sql_small_result|ssl|starting|stored|straight_join|table|terminated|then|tinyblob|tinyint|tinytext|to|trailing|trigger|true|undo|union|unique|unlock|unsigned|update|usage|use|using|utc_date|utc_time|utc_timestamp|values|varbinary|varchar|varcharacter|varying|virtual|when|where|while|with|write|xor|year_month|zerofill)\b/gi,
				'replace':window.highlight.default_replace
			},
			{
				'class':'func',
				'match':/\b([a-z_][a-z_\d]*)\b(?=\()/gi,
				'replace':window.highlight.default_replace
			},
			{
				'class':'name',
				'match':/\b([a-z\_][a-z_\d]*)\b/gi,
				'replace':window.highlight.default_replace
			}
		];
	}

})(Function('return this')());
<|endoftext|>


<commit_before>/*!

Copyright © 2014 Tolga HOŞGÖR

This file is part of chorrent.

    chorrent is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    chorrent is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with chorrent.  If not, see <http://www.gnu.org/licenses/>.
*/

console.log("linkgrab.js loaded.");

chrome.contextMenus.create({
  id: "linkGrab",
  title: "Download with Chorrent",
  contexts: ["all"]});

chrome.contextMenus.onClicked.addListener(function(info, tab) {
  if(info.menuItemId != "linkGrab")
    return;
  var magnetInfo = parseMagnet(info.linkUrl);
  console.log(magnetInfo);
  if(magnetInfo.status)
  {
    return;
  }
});

function parseMagnet(magnet)
{
  var parseRegex = /(?:\?|&)([^=&\?#]+)=?([^&#]*)/g;
  var match;
  var torrentInfo = { };
  while(match = parseRegex.exec(magnet))
  {
    if(!torrentInfo[match[1]])
      torrentInfo[match[1]] = new Array;
    torrentInfo[match[1]].push(match[2]);
  }

  /* test magnet for correct hash type */
  var bittorrentTest = /^urn:btih:/;
  if(!bittorrentTest.test(torrentInfo["xt"][0]))
    return {status: 1, text: "No hash BitTorrent hash found in this magnet link."};
  else /* extrach the sha1 hash */
    torrentInfo["xt"][0] = torrentInfo["xt"][0].substr(9, torrentInfo["xt"][0].length - 9);

  /* remove non-http(s) trackers */
  torrentInfo["tr"].forEach(function(tracker) {
    var idx = 0;
  });

  if(torrentInfo["tr"].length == 0)
    return {status: 2, text: "No trackers fonud in this magnet link."};

  torrentInfo.udpTrackerRequests = new Array;
  torrentInfo.httpTrackerRequests = new Array;

  torrentInfo["tr"].forEach(function(tracker) {
    var httpTest = /^https?:\/\//;
    var udpTest = /^udp:\/\//;
    if(httpTest.test(tracker))
    {
      /* url-encode the binary encoded hash */
      for(var i = torrentInfo["xt"][0].length - 2; i >= 0; i-=2)
      {
        torrentInfo["xt"][0] = torrentInfo["xt"][0].substr(0, i) + "%" + torrentInfo["xt"][0].substr(i, torrentInfo["xt"][0].length - i);
      }
      torrentInfo.httpTrackerRequests.push(tracker + "?info_hash=" + torrentInfo["xt"][0] + "&peer_id=" + peer_id
       + "&port=" + parseInt(Math.random() * 1000 + 8000) + "&event=started&numwant=150");
    } else

    /*
     * TODO: UDP Tracker Support
     */
    if(udpTest.test(tracker))
    {
    }
  });

  if(torrentInfo.httpTrackerRequests.length === 0 && torrentInfo.udpTrackerRequests.length === 0)
    return {status: 3, text: "No trackers found in the magnet link."};

  torrentInfo.status = 0;
  torrentInfo.text = "Magnet link successfully parsed.";

  return torrentInfo;
}
<commit_msg>removed old code<commit_after><|endoftext|>


<commit_before>/*
 *    mainmenu.js
 *    Handles main menu
 */

YINS.MainMenu = function(game) {
	this.music;
};

YINS.MainMenu.prototype = {
	create: function() {
		/* Our assets are preloaded, so here we just kick things off
		by playing some music */
		this.music = YINS.game.add.audio('menuMusic');
		this.music.loopFull(1);

		this.stage.backgroundColor = YINS.color.purple_light;

		var title = YINS.game.add.text(YINS.game.world.centerX, YINS.game.world.centerY, 'YINS', YINS.text.title);
		title.anchor.set(0.5);

		var subtitle = YINS.game.add.text(YINS.game.world.centerX, YINS.game.world.centerY + 80, '- Click to start -', YINS.text.subtitle);
		subtitle.anchor.set(0.5);

		YINS.game.input.onDown.add(this.startGame, this);
	},

	startGame: function(pointer) {
		this.music.stop();
 		YINS.game.state.start('game');
	}
};<commit_msg>Set this.music to empty object<commit_after>/*
 *    mainmenu.js
 *    Handles main menu
 */

YINS.MainMenu = function(game) {
	this.music = {};
};

YINS.MainMenu.prototype = {
	create: function() {
		/* Our assets are preloaded, so here we just kick things off
		by playing some music */
		this.music = YINS.game.add.audio('menuMusic');
		this.music.loopFull(1);

		this.stage.backgroundColor = YINS.color.purple_light;

		var title = YINS.game.add.text(YINS.game.world.centerX, YINS.game.world.centerY, 'YINS', YINS.text.title);
		title.anchor.set(0.5);

		var subtitle = YINS.game.add.text(YINS.game.world.centerX, YINS.game.world.centerY + 80, '- Click to start -', YINS.text.subtitle);
		subtitle.anchor.set(0.5);

		YINS.game.input.onDown.add(this.startGame, this);
	},

	startGame: function(pointer) {
		this.music.stop();
 		YINS.game.state.start('game');
	}
};<|endoftext|>


<commit_before>var Meetings = {
  debug: 0,
  debugApi: 0,
  debugThisApi: 'http://passoa.online.ntnu.no/notifier/DEBUG/meetings', // TODO: create that address server side
  debugString: 0,
  debugThisString: '08:00-10:00 arrKom\n14:00-16:00 triKom\n18:00-23:59 appKom',

  msgNone: 'Ledig resten av dagen',
  msgError: 'Frakoblet fra møtekalender',

  get: function(callback) {
    if (callback == undefined) {
      console.log('ERROR: Callback is required. In the callback you should insert the results into the DOM.');
      return;
    }
    if (!Affiliation.org[localStorage.affiliationKey1].hw) {
      if (this.debug) console.log('ERROR: affiliation without hw-features tried checking meetings');
      return;
    }

    var api = Affiliation.org[localStorage.affiliationKey1].hw.apis.meetings;

    // Receives the meeting plan for today
    var self = this;
    Ajaxer.getPlainText({
      url: (self.debugApi ? self.debugThisApi : api),
      success: function(meetings) {
        if (self.debug) console.log('Raw meetings:\n\n', meetings);

        // Debugging a particular string right now?
        if (self.debugString)
          meetings = self.debugThisString;

        if (meetings != '') {
          // Prettify todays meetings
          var prettyMeetings = self.prettifyTodaysMeetings(meetings);
          if (self.debug) console.log('Pretty meetings:', prettyMeetings);

          callback(prettyMeetings);
        }
        else {
          // Empty string returned from API
          callback(self.msgNone);
        }
      },
      error: function(jqXHR, text, err) {
        if (self.debug) console.log('ERROR: Failed to get todays meeting plan.');
        callback(self.msgError);
      },
    });
  },

  prettifyTodaysMeetings: function(meetings) {
    meetings = meetings.trim();
    // Change 00:00 to 24
    meetings = meetings.replace(/00:00/g, '24');
    if (this.debug) console.log('24\t::', meetings);
    // Remove unnecessarily specific time info 10:00 -> 10, including the academic fifteen minutes
    meetings = meetings.replace(/:(00|15)/g, '');
    if (this.debug) console.log(':00\t::', meetings);
    // Trim unnecessary zero in time 08 -> 8
    meetings = meetings.replace(/0(\d)/g, '$1');
    if (this.debug) console.log('08\t::', meetings);
    // Add spaces for...
    // ...times "10-16:30" -> "10 - 16:30"
    // ...days "Fredag-Søndag" -> "Fredag - Søndag"
    // ...dates "14.2-16.2" -> "14.2 - 16.2"
    meetings = meetings.replace(/(dag|\d) ?- ?(\d+[\.:]?\d*|[a-zæøå]+dag)/gi, '$1 - $2:');
    if (this.debug) console.log('_ \t::', meetings);
    // Change times like 23:30 and 23:59 to just 24
    meetings = meetings.replace(/22:(30|59)/g, '23');
    meetings = meetings.replace(/23:(30|59)/g, '24');
    if (this.debug) console.log(':30\t::', meetings);
    return meetings;
  },

}
<commit_msg>meetings: minor bugfix<commit_after>var Meetings = {
  debug: 0,
  debugApi: 0,
  debugThisApi: 'http://passoa.online.ntnu.no/notifier/DEBUG/meetings', // TODO: create that address server side
  debugString: 0,
  debugThisString: '08:00-10:00 arrKom\n14:00-16:00 triKom\n18:00-23:59 appKom',

  msgNone: 'Ledig resten av dagen',
  msgError: 'Frakoblet fra møtekalender',

  get: function(callback) {
    if (callback == undefined) {
      console.log('ERROR: Callback is required. In the callback you should insert the results into the DOM.');
      return;
    }
    if (!Affiliation.org[localStorage.affiliationKey1].hw) {
      if (this.debug) console.log('ERROR: affiliation without hw-features tried checking meetings');
      return;
    }

    var api = Affiliation.org[localStorage.affiliationKey1].hw.apis.meetings;

    // Receives the meeting plan for today
    var self = this;
    Ajaxer.getPlainText({
      url: (self.debugApi ? self.debugThisApi : api),
      success: function(meetings) {
        if (self.debug) console.log('Raw meetings:\n\n', meetings);

        // Debugging a particular string right now?
        if (self.debugString) {
          meetings = self.debugThisString;
        }

        if (meetings.indexOf('error') !== -1) {
          if (self.debug) console.log('ERROR:', meetings);
          callback(self.msgError);
        }
        else if (meetings != '') {
          // Prettify todays meetings
          var prettyMeetings = self.prettifyTodaysMeetings(meetings);
          if (self.debug) console.log('Pretty meetings:', prettyMeetings);

          callback(prettyMeetings);
        }
        else {
          // Empty string returned from API
          callback(self.msgNone);
        }
      },
      error: function(jqXHR, text, err) {
        if (self.debug) console.log('ERROR: Failed to get todays meeting plan.');
        callback(self.msgError);
      },
    });
  },

  prettifyTodaysMeetings: function(meetings) {
    meetings = meetings.trim();
    // Change 00:00 to 24
    meetings = meetings.replace(/00:00/g, '24');
    if (this.debug) console.log('24\t::', meetings);
    // Remove unnecessarily specific time info 10:00 -> 10, including the academic fifteen minutes
    meetings = meetings.replace(/:(00|15)/g, '');
    if (this.debug) console.log(':00\t::', meetings);
    // Trim unnecessary zero in time 08 -> 8
    meetings = meetings.replace(/0(\d)/g, '$1');
    if (this.debug) console.log('08\t::', meetings);
    // Add spaces for...
    // ...times "10-16:30" -> "10 - 16:30"
    // ...days "Fredag-Søndag" -> "Fredag - Søndag"
    // ...dates "14.2-16.2" -> "14.2 - 16.2"
    meetings = meetings.replace(/(dag|\d) ?- ?(\d+[\.:]?\d*|[a-zæøå]+dag)/gi, '$1 - $2:');
    if (this.debug) console.log('_ \t::', meetings);
    // Change times like 23:30 and 23:59 to just 24
    meetings = meetings.replace(/22:(30|59)/g, '23');
    meetings = meetings.replace(/23:(30|59)/g, '24');
    if (this.debug) console.log(':30\t::', meetings);
    return meetings;
  },

}
<|endoftext|>


<commit_before>/**
 * Nextcloud - nextant
 *
 * This file is licensed under the Affero General Public License version 3 or
 * later. See the COPYING file.
 *
 * @author Maxence Lange <maxence@pontapreta.net>
 * @copyright Maxence Lange 2016
 * @license GNU AGPL version 3 or any later version
 *
 * This program is free software: you can redistribute it and/or modify it under
 * the terms of the GNU Affero General Public License as published by the Free
 * Software Foundation, either version 3 of the License, or (at your option) any
 * later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
 * details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 *
 */

$(document)
		.ready(
				function() {

					(function(func) {
						$.fn.addClass = function() {
							func.apply(this, arguments);
							this.trigger('classChanged');
							return this;
						}
					})($.fn.addClass);

					(function(func) {
						$.fn.removeClass = function() {
							func.apply(this, arguments);
							this.trigger('classChanged');
							return this;
						}
					})($.fn.removeClass);

					$('#searchresults').on('classChanged', function() {
						if ($('#searchresults').attr('class') == 'hidden')
							$('#nextantList').hide();
						else
							$('#nextantList').show();
					});

					$('#searchbox').focusout(function() {
						nextantCurrentFocus = false;
						nextant.suggestShow();
					});

					$('#searchbox').focusin(function() {
						nextantCurrentFocus = true;
						nextant.suggestShow();
					});

					$(document).keypress(function(e) {
						if (e.which == 13 && nextantCurrentFocus) {
							nextant.search();
						}
					});

					var nextantCurrentSearch = '';
					var nextantCurrentFocus = false;
					var nextantSearchDelayTimer = null;
					var nextantSuggestDelayTimer = null;
					var nextantNoSpamSuggest = false;
					var nextant = {

						init : function() {
							$('#searchbox').on('input', function(e) {
								nextant.searchTimer();
								nextant.suggestTimer();
							});
						},

						searchTimer : function() {
							if (nextantSearchDelayTimer != null)
								clearTimeout(nextantSearchDelayTimer);

							nextantSearchDelayTimer = setTimeout(function() {
								nextant.search();
							}, 150);
						},

						suggestTimer : function() {
							if (nextantSuggestDelayTimer != null)
								clearTimeout(nextantSuggestDelayTimer);

							nextantSuggestDelayTimer = setTimeout(function() {
								nextant.suggest();
							}, 0);
						},

						search : function() {
							nextantSearchDelayTimer = null;

							var query = $('#searchbox').val();
							if (query == nextantCurrentSearch)
								return;
							nextantCurrentSearch = query;

							var data = {
								query : query,
								current_dir : nextant.get('dir')
							}

							nextant.searchRequest(data);
						},

						suggest : function() {
							nextantSuggestDelayTimer = null;
							var query = $('#searchbox').val();
							var data = {
								query : query
							}
							nextant.suggestRequest(data);
						},

						suggestRequest : function(data) {
							if (nextantNoSpamSuggest)
								return;
							$.post(
									OC.filePath('nextant', 'ajax',
											'suggest.php'), data,
									nextant.suggestResult);
						},

						suggestResult : function(response) {

							if (response == null || response.status > 0
									|| response.result.length == 0) {

								if (response.status > 0) {
									nextantNoSpamSuggest = true;
									setTimeout(function() {
										nextantNoSpamSuggest = false;
									}, 60000);
								}
								if ($('#nextantSugg_list').length)
									$('#nextantSugg_list').hide(200);
								return;
							}

							if (!$('#nextantSugg_list').length)
								$('#body-user').append(
										'

');

							nextant.suggestShow();

							var offset = $('#searchbox').offset();
							var height = $('#searchbox').height();
							var top = offset.top + height + "px";
							var left = offset.left + "px";

							$('#nextantSugg_list').css({
								'position' : 'absolute',
								'left' : left,
								'top' : top
							});

							$('#nextantSugg_list').empty();
							var result = response.result;
							for (var i = 0; i < result.length; i++) {
								var first = '';
								if (i == 0)
									first = 'nextantSugg_firstitem';

								$('#nextantSugg_list').append(
										'
'
												+ result[i].suggestion
												+ '
');
							}

							$('.nextantSugg_item').click(function() {
								nextant.suggestReplace($(this).text());
								nextant.search();
							});

						},

						suggestShow : function() {

							if (!$('#nextantSugg_list').length)
								return;
							if (nextantCurrentFocus)
								$('#nextantSugg_list').show(200);
							else
								$('#nextantSugg_list').hide(200);
						},

						suggestReplace : function(txt) {
							$('#searchbox').val(txt + ' ');
							$('#searchbox').focus();
						},

						searchRequest : function(data) {
							$.post(
									OC
											.filePath('nextant', 'ajax',
													'search.php'), data,
									nextant.searchResult);
						},

						searchResult : function(response) {
							if (response == null)
								return;

							if (!$('#nextantList').length)
								$('#fileList')
										.append(
												'

');

							$('#nextantList').empty();

							response
									.forEach(function(entry) {
										var row = nextant
												.template_entry()
												.replace(/%ID%/gi, entry.id)
												.replace(/%TYPE%/gi, entry.type)
												.replace(/%TITLE%/gi,
														entry.title)
												.replace(/%LINKMAIN%/gi,
														entry.link_main)
												.replace(/%FILENAME%/gi,
														entry.filename)
												.replace(/%DIRPATH%/gi,
														entry.dirpath)
												.replace(/%SIZE%/gi, entry.size)
												.replace(/%SIZEREAD%/gi,
														entry.size_readable)
												.replace(/%MIMETYPE%/gi,
														entry.mimetype)
												.replace(/%ICON%/gi, entry.icon)
												.replace(/%MTIME%/gi,
														entry.mtime).replace(
														/%HIGHLIGHT1%/gi,
														entry.highlight1)
												.replace(/%HIGHLIGHT2%/gi,
														entry.highlight2);

										row = row
												.replace(
														/%SHARED%/gi,
														(entry.shared != '') ? ' style="background-image:url('
																+ entry.shared
																+ ');"'
																: '');
										row = row
												.replace(
														/%DELETED%/gi,
														(entry.deleted != '') ? ' style="background-image:url('
																+ entry.deleted
																+ ');"'
																: '');

										$('#nextantList').append(row);
									});
						},

						template_entry : function() {

							$tmpl = '
';
							$tmpl += '';
							$tmpl += '';
							$tmpl += '';
							$tmpl += '
';
							$tmpl += '

';
							$tmpl += '
';
							$tmpl += 'Select';

							$tmpl += '';
							$tmpl += '
';
							$tmpl += '%TITLE%';
							$tmpl += '%HIGHLIGHT1%';
							$tmpl += '%HIGHLIGHT2%';
							$tmpl += '
';
							$tmpl += '	';
							$tmpl += '%SIZEREAD%	';
							$tmpl += '
';

							return $tmpl;

						},

						get : function(name, url) {
							if (!url)
								url = window.location.href;
							name = name.replace(/[\[\]]/g, "\\$&");
							var regex = new RegExp("[?&]" + name
									+ "(=([^&#]*)|&|#|$)"), results = regex
									.exec(url);
							if (!results)
								return null;
							if (!results[2])
								return '';
							return decodeURIComponent(results[2].replace(/\+/g,
									' '));
						}

					}

					nextant.init();

				});
<commit_msg>timer<commit_after>/**
 * Nextcloud - nextant
 *
 * This file is licensed under the Affero General Public License version 3 or
 * later. See the COPYING file.
 *
 * @author Maxence Lange <maxence@pontapreta.net>
 * @copyright Maxence Lange 2016
 * @license GNU AGPL version 3 or any later version
 *
 * This program is free software: you can redistribute it and/or modify it under
 * the terms of the GNU Affero General Public License as published by the Free
 * Software Foundation, either version 3 of the License, or (at your option) any
 * later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
 * details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 *
 */

$(document)
		.ready(
				function() {

					(function(func) {
						$.fn.addClass = function() {
							func.apply(this, arguments);
							this.trigger('classChanged');
							return this;
						}
					})($.fn.addClass);

					(function(func) {
						$.fn.removeClass = function() {
							func.apply(this, arguments);
							this.trigger('classChanged');
							return this;
						}
					})($.fn.removeClass);

					$('#searchresults').on('classChanged', function() {
						if ($('#searchresults').attr('class') == 'hidden')
							$('#nextantList').hide();
						else
							$('#nextantList').show();
					});

					$('#searchbox').focusout(function() {
						nextantCurrentFocus = false;
						nextant.suggestShow();
					});

					$('#searchbox').focusin(function() {
						nextantCurrentFocus = true;
						nextant.suggestShow();
					});

					$(document).keypress(function(e) {
						if (e.which == 13 && nextantCurrentFocus) {
							nextant.search();
						}
					});

					var nextantCurrentSearch = '';
					var nextantCurrentFocus = false;
					var nextantSearchDelayTimer = null;
					var nextantSuggestDelayTimer = null;
					var nextantNoSpamSuggest = false;
					var nextant = {

						init : function() {
							$('#searchbox').on('input', function(e) {
								nextant.searchTimer();
								nextant.suggestTimer();
							});
						},

						searchTimer : function() {
							if (nextantSearchDelayTimer != null)
								clearTimeout(nextantSearchDelayTimer);

							nextantSearchDelayTimer = setTimeout(function() {
								nextant.search();
							}, 250);
						},

						suggestTimer : function() {
							if (nextantSuggestDelayTimer != null)
								clearTimeout(nextantSuggestDelayTimer);

							nextantSuggestDelayTimer = setTimeout(function() {
								nextant.suggest();
							}, 50);
						},

						search : function() {
							nextantSearchDelayTimer = null;

							var query = $('#searchbox').val();
							if (query == nextantCurrentSearch)
								return;
							nextantCurrentSearch = query;

							var data = {
								query : query,
								current_dir : nextant.get('dir')
							}

							nextant.searchRequest(data);
						},

						suggest : function() {
							nextantSuggestDelayTimer = null;
							var query = $('#searchbox').val();
							var data = {
								query : query
							}
							nextant.suggestRequest(data);
						},

						suggestRequest : function(data) {
							if (nextantNoSpamSuggest)
								return;
							$.post(
									OC.filePath('nextant', 'ajax',
											'suggest.php'), data,
									nextant.suggestResult);
						},

						suggestResult : function(response) {

							if (response == null || response.status > 0
									|| response.result.length == 0) {

								if (response.status > 0) {
									nextantNoSpamSuggest = true;
									setTimeout(function() {
										nextantNoSpamSuggest = false;
									}, 60000);
								}
								if ($('#nextantSugg_list').length)
									$('#nextantSugg_list').hide(200);
								return;
							}

							if (!$('#nextantSugg_list').length)
								$('#body-user').append(
										'

');

							nextant.suggestShow();

							var offset = $('#searchbox').offset();
							var height = $('#searchbox').height();
							var top = offset.top + height + "px";
							var left = offset.left + "px";

							$('#nextantSugg_list').css({
								'position' : 'absolute',
								'left' : left,
								'top' : top
							});

							$('#nextantSugg_list').empty();
							var result = response.result;
							for (var i = 0; i < result.length; i++) {
								var first = '';
								if (i == 0)
									first = 'nextantSugg_firstitem';

								$('#nextantSugg_list').append(
										'
'
												+ result[i].suggestion
												+ '
');
							}

							$('.nextantSugg_item').click(function() {
								nextant.suggestReplace($(this).text());
								nextant.search();
							});

						},

						suggestShow : function() {

							if (!$('#nextantSugg_list').length)
								return;
							if (nextantCurrentFocus)
								$('#nextantSugg_list').show(200);
							else
								$('#nextantSugg_list').hide(200);
						},

						suggestReplace : function(txt) {
							$('#searchbox').val(txt + ' ');
							$('#searchbox').focus();
						},

						searchRequest : function(data) {
							$.post(
									OC
											.filePath('nextant', 'ajax',
													'search.php'), data,
									nextant.searchResult);
						},

						searchResult : function(response) {
							if (response == null)
								return;

							if (!$('#nextantList').length)
								$('#fileList')
										.append(
												'

');

							$('#nextantList').empty();

							response
									.forEach(function(entry) {
										var row = nextant
												.template_entry()
												.replace(/%ID%/gi, entry.id)
												.replace(/%TYPE%/gi, entry.type)
												.replace(/%TITLE%/gi,
														entry.title)
												.replace(/%LINKMAIN%/gi,
														entry.link_main)
												.replace(/%FILENAME%/gi,
														entry.filename)
												.replace(/%DIRPATH%/gi,
														entry.dirpath)
												.replace(/%SIZE%/gi, entry.size)
												.replace(/%SIZEREAD%/gi,
														entry.size_readable)
												.replace(/%MIMETYPE%/gi,
														entry.mimetype)
												.replace(/%ICON%/gi, entry.icon)
												.replace(/%MTIME%/gi,
														entry.mtime).replace(
														/%HIGHLIGHT1%/gi,
														entry.highlight1)
												.replace(/%HIGHLIGHT2%/gi,
														entry.highlight2);

										row = row
												.replace(
														/%SHARED%/gi,
														(entry.shared != '') ? ' style="background-image:url('
																+ entry.shared
																+ ');"'
																: '');
										row = row
												.replace(
														/%DELETED%/gi,
														(entry.deleted != '') ? ' style="background-image:url('
																+ entry.deleted
																+ ');"'
																: '');

										$('#nextantList').append(row);
									});
						},

						template_entry : function() {

							$tmpl = '
';
							$tmpl += '';
							$tmpl += '';
							$tmpl += '';
							$tmpl += '
';
							$tmpl += '

';
							$tmpl += '
';
							$tmpl += 'Select';

							$tmpl += '';
							$tmpl += '
';
							$tmpl += '%TITLE%';
							$tmpl += '%HIGHLIGHT1%';
							$tmpl += '%HIGHLIGHT2%';
							$tmpl += '
';
							$tmpl += '	';
							$tmpl += '%SIZEREAD%	';
							$tmpl += '
';

							return $tmpl;

						},

						get : function(name, url) {
							if (!url)
								url = window.location.href;
							name = name.replace(/[\[\]]/g, "\\$&");
							var regex = new RegExp("[?&]" + name
									+ "(=([^&#]*)|&|#|$)"), results = regex
									.exec(url);
							if (!results)
								return null;
							if (!results[2])
								return '';
							return decodeURIComponent(results[2].replace(/\+/g,
									' '));
						}

					}

					nextant.init();

				});
<|endoftext|>


<commit_before>function openDataFile(){
    $.dialog({
        title: 'Open/Import',
        content: `
Due to the security reasons, browser doesn't allow to load the images.
        So please load the images manually.

            Browse
                <input id="browse" type="file" class="filebutton" accept=".fpp,.nimn,.xml,.json"   />

`,
        escapeKey: true,
        backgroundDismiss: true,
        onContentReady: function(){
            $("#browse").bind("change", function(input) {
                readDataFile(input);
           });
        }
    });
}

function readDataFile(e){
    var input = e.srcElement;
    if (input.files && input.files[0]) {
        var dataFile = input.files[0];

        var reader = new FileReader();
        reader.onload = function (e) {
            /* if(pointFile.name.endsWith(".pts")){
                loadPts(e.target.result);
            }else*/ if(dataFile.name.endsWith(".json")){
                loadJSONFile(e.target.result);
            }else if(dataFile.name.endsWith(".nimn")){
                loadProjectFile(e.target.result);
            }else if(dataFile.name.endsWith(".fpp")){
                loadFpp(e.target.result);
            }else if(dataFile.name.endsWith(".xml")){
                loadDlibXml(e.target.result);
            }else{
                console.log("Not supported");
            }
        };

        reader.readAsText(input.files[0]);
    }
    input.value = null;
}

var loadJSONFile = function(data){
    labellingData = cocoFormater.fromCOCO(JSON.parse(data));
}

var loadProjectFile = function(data){
    labellingData = nimn.parse(nimnSchema, data);
    //labellingData =  JSON.parse(data);
}
var loadDlibXml = function(data){
    var obj = parser.parse(data,{
        ignoreAttributes : false,
        attributeNamePrefix : "",
    });

    //labellingData = {};
    var image = obj.dataset.images.image;

    if(!Array.isArray(image)){
        image = [image];
    }

    for(var index=0; index < image.length; index++){//for each image
        var pathArr = image[index].file.split(/\\|\//);
        var imgName = pathArr[ pathArr.length -1 ];
        var boxes = image[ index ].box;
        var boxObject = [];
        if(boxes){
            if(!Array.isArray(boxes)){
                boxes = [boxes];
            }
            for(var b_index =0; b_index < boxes.length; b_index++){//for each box
                var currentBox = boxes[ b_index ];

                boxObject .push({
                    id : "rect" + b_index,
                    label: currentBox.label,
                    type: "rect",
                    bbox : {
                        x: currentBox.left,
                        y: currentBox.top,
                        h: currentBox.height,
                        w: currentBox.width,
                        /* ignore: currentBox.ignore */
                        /* pose='4' detection_score='4' */
                    },
                    points : [
                        currentBox.left,
                        currentBox.top,
                        currentBox.width,
                        currentBox.height
                    ],
                    attributes : [],
                    featurePoints: []
                })
                if(currentBox.part){
                    if(!Array.isArray(currentBox.part)){
                        currentBox.part = [currentBox.part];
                    }

                    for(var p_index=0; p_index< currentBox.part.length; p_index++){//for each part
                        var pointlabel = currentBox.part[p_index].name || p_index+1;

                        boxObject[b_index].featurePoints.push({
                            id: "point" + p_index,
                            x: currentBox.part[p_index].x/*  - currentBox.left */,
                            y: currentBox.part[p_index].y/*  - currentBox.top */,
                            label: pointlabel
                        })
                    }//End - for each part
                }
            }//End - for each box
        }

        if(labellingData[imgName]){
            labellingData[imgName].shapes =  boxObject;
        }
    }//End - for each image

}

<commit_msg>Fixes #138<commit_after>function openDataFile(){
    $.dialog({
        title: 'Open/Import',
        content: `
Due to the security reasons, browser doesn't allow to load the images.
        So please load the images manually.

            Browse
                <input id="browse" type="file" class="filebutton" accept=".fpp,.nimn,.xml,.json"   />

`,
        escapeKey: true,
        backgroundDismiss: true,
        onContentReady: function(){
            $("#browse").bind("change", function(input) {
                readDataFile(input);
           });
        }
    });
}

function readDataFile(e){
    var input = e.target || e.srcElement;
    if (input.files && input.files[0]) {
        var dataFile = input.files[0];

        var reader = new FileReader();
        reader.onload = function (e) {
            /* if(pointFile.name.endsWith(".pts")){
                loadPts(e.target.result);
            }else*/ if(dataFile.name.endsWith(".json")){
                loadJSONFile(e.target.result);
            }else if(dataFile.name.endsWith(".nimn")){
                loadProjectFile(e.target.result);
            }else if(dataFile.name.endsWith(".fpp")){
                loadFpp(e.target.result);
            }else if(dataFile.name.endsWith(".xml")){
                loadDlibXml(e.target.result);
            }else{
                console.log("Not supported");
            }
        };

        reader.readAsText(input.files[0]);
    }
    input.value = null;
}

var loadJSONFile = function(data){
    labellingData = cocoFormater.fromCOCO(JSON.parse(data));
}

var loadProjectFile = function(data){
    labellingData = nimn.parse(nimnSchema, data);
    //labellingData =  JSON.parse(data);
}
var loadDlibXml = function(data){
    var obj = parser.parse(data,{
        ignoreAttributes : false,
        attributeNamePrefix : "",
    });

    //labellingData = {};
    var image = obj.dataset.images.image;

    if(!Array.isArray(image)){
        image = [image];
    }

    for(var index=0; index < image.length; index++){//for each image
        var pathArr = image[index].file.split(/\\|\//);
        var imgName = pathArr[ pathArr.length -1 ];
        var boxes = image[ index ].box;
        var boxObject = [];
        if(boxes){
            if(!Array.isArray(boxes)){
                boxes = [boxes];
            }
            for(var b_index =0; b_index < boxes.length; b_index++){//for each box
                var currentBox = boxes[ b_index ];

                boxObject .push({
                    id : "rect" + b_index,
                    label: currentBox.label,
                    type: "rect",
                    bbox : {
                        x: currentBox.left,
                        y: currentBox.top,
                        h: currentBox.height,
                        w: currentBox.width,
                        /* ignore: currentBox.ignore */
                        /* pose='4' detection_score='4' */
                    },
                    points : [
                        currentBox.left,
                        currentBox.top,
                        currentBox.width,
                        currentBox.height
                    ],
                    attributes : [],
                    featurePoints: []
                })
                if(currentBox.part){
                    if(!Array.isArray(currentBox.part)){
                        currentBox.part = [currentBox.part];
                    }

                    for(var p_index=0; p_index< currentBox.part.length; p_index++){//for each part
                        var pointlabel = currentBox.part[p_index].name || p_index+1;

                        boxObject[b_index].featurePoints.push({
                            id: "point" + p_index,
                            x: currentBox.part[p_index].x/*  - currentBox.left */,
                            y: currentBox.part[p_index].y/*  - currentBox.top */,
                            label: pointlabel
                        })
                    }//End - for each part
                }
            }//End - for each box
        }

        if(labellingData[imgName]){
            labellingData[imgName].shapes =  boxObject;
        }
    }//End - for each image

}

<|endoftext|>


<commit_before>/*!
 * parallax.js v2.0.0 (http://pixelcog.github.io/parallax.js/)
 * @copyright 2017 PixelCog, Inc.
 * @license MIT (https://github.com/pixelcog/parallax.js/blob/master/LICENSE)
 */
import $ from 'jquery';
import generatePlugin from './generate-plugin';

class Parallax {

  ///////////////////////
  // Instance Methods //
  /////////////////////

  constructor(element, options) {
    const $window = $(element);

    Parallax.isSet || Parallax.init();
    Parallax.iList.push(this);

    // match returns null if regex is null i.e. falsy, no additional checks needed
    if (navigator.userAgent.match(options.excludeAgents)) {
      // todo: enhance
      if (options.src && !$window.is('img')) {
        $window.css({
          background: 'url("' + options.src + '")' + options.pos + '/cover'
        });
      }
    } else {
      // little parse function to keep duplicate code to a minimum.
      function _parsePos(pos, p1, p2) {
        const p = parseInt(options[pos]);
        if (isNaN(p)) {
          if (options[pos] !== p1 && options[pos] !== p2) {
            options.pos += (options[pos] = 'center') + ' ';
          }
        } else {
          options.pos += (options[pos] = p) + 'px ';
        }
      }

      options.pos = '';
      _parsePos('posX', 'left', 'right');
      _parsePos('posY', 'top', 'bottom');

      /** creating the mirror element */
      const $mirror = $('<div>').addClass('parallax-mirror').css({
        visibility: 'hidden',
        zIndex: options.zIndex,
        position: 'fixed',
        top: 0,
        left: 0,
        overflow: 'hidden'
      }).prependTo($(options.mirrorSelector));

      /** finding the slider with the selector provided*/
      let $slider = $window.find(options.sliderSelector);

      if ($slider.length === 0)
        $slider = $('<img>').attr('src', options.src);
      else {
        /** former parent where the slider will be added again when destroyed */
        options.formerParent = $slider.parent();
        /** former styles which will be set again when destroyed */
        options.formerStyles = $slider.prop('style');
      }

      $slider.addClass('parallax-slider').prependTo($mirror);

      // call re-init after all images are loaded within the slider
      $slider.children('img').add($slider).on('load', () => {
        Parallax.update(true);
      });

      this.$s = $slider;
      this.$m = $mirror;
    }

    this.$w = $window;
    this.o = options;

    if (typeof options.afterSetup === 'function')
      options.afterSetup(this);
  }

  /**
   * recalculates size and position variables, everything except the scroll position.
   */
  refresh() {
    const $window = this.$w;
    const options = this.o;

    // when not initialized yet.
    if (!options)
      return;

    // find out aspect ratio for the first time
    if (!options.aspectRatio) {

      (function ($s, options) {
        // iterate through all children and find out the boundings
        let top = 0, bottom = 0, left = 0, right = 0;
        // when there are no children, the slider itself is an image
        if ($s.children().each(function () {
            const $e = $(this);
            const off = $e.offset();
            const eBottom = off.top + $e.outerHeight();
            const eRight = off.left + $e.outerWidth();

            top = off.top < top ? off.top : top;
            left = off.left < left ? off.left : left;
            bottom = eBottom > bottom ? eBottom : bottom;
            right = eRight > right ? eRight : right;
          }).length === 0) {
          options.aspectRatio = $s[0].naturalWidth / ($s[0].naturalHeight || 1);
        }
        else {

          const offset = $s.offset();
          // not sure if thats correctbut  bottom - top - offset.top yielded in wrong results
          const contentHeight = bottom - Math.max(top, offset.top);
          const contentWidth = right - Math.max(left, offset.left);

          // aspectRatio is 0 when contentWidth is 0 and therefore recalculated until there is some width
          options.aspectRatio = contentWidth / (contentHeight || 1);
        }
      })(this.$s, options);
    }
    const aspect = options.aspectRatio || 1;

    options.boxWidth = $window.outerWidth();
    options.boxHeight = $window.outerHeight() + options.bleed * 2;
    options.boxOffsetTop = $window.offset().top - options.bleed;
    options.boxOffsetLeft = $window.offset().left;
    options.boxOffsetBottom = options.boxOffsetTop + options.boxHeight;

    const winHeight = Parallax.wH;
    const docHeight = Parallax.dH;
    const maxOffset = Math.min(options.boxOffsetTop, docHeight - winHeight);
    const minOffset = Math.max(options.boxOffsetTop + options.boxHeight - winHeight, 0);
    const imageHeightMin = options.boxHeight + (maxOffset - minOffset) * (1 - options.speed) | 0;
    const imageOffsetMin = (options.boxOffsetTop - maxOffset) * (1 - options.speed) | 0;
    let margin;

    // box width is smaller than minimum image width
    if (options.boxWidth < imageHeightMin * aspect) {
      options.imageWidth = imageHeightMin * aspect | 0;
      options.imageHeight = imageHeightMin;
      options.offsetBaseTop = imageOffsetMin;

      margin = options.imageWidth - options.boxWidth;

      if (options.posX === 'left') {
        options.offsetLeft = 0;
      } else if (options.posX === 'right') {
        options.offsetLeft = -margin;
      } else if (!isNaN(options.posX)) {
        options.offsetLeft = Math.max(options.posX, -margin);
      } else {
        options.offsetLeft = -margin / 2 | 0;
      }
    } else {
      options.imageWidth = options.boxWidth;
      options.imageHeight = options.boxWidth / aspect | 0;
      options.offsetLeft = 0;

      margin = options.imageHeight - imageHeightMin;

      if (options.posY === 'top') {
        options.offsetBaseTop = imageOffsetMin;
      } else if (options.posY === 'bottom') {
        options.offsetBaseTop = imageOffsetMin - margin;
      } else if (!isNaN(options.posY)) {
        options.offsetBaseTop = imageOffsetMin + Math.max(options.posY, -margin);
      } else {
        options.offsetBaseTop = imageOffsetMin - margin / 2 | 0;
      }
    }

    if (typeof options.afterRefresh === 'function')
      options.afterRefresh(this);
  }

  /**
   * renders the slider at the correct position relative to the scroll position
   */
  render() {
    const options = this.o;

    const scrollTop = Parallax.sT;
    const scrollLeft = Parallax.sL;
    const overScroll = options.overScrollFix ? Parallax.overScroll : 0;
    const scrollBottom = scrollTop + Parallax.wH;

    if (options.boxOffsetBottom > scrollTop && options.boxOffsetTop <= scrollBottom) {
      options.visibility = 'visible';
      options.mirrorTop = options.boxOffsetTop - scrollTop;
      options.mirrorLeft = options.boxOffsetLeft - scrollLeft;
      options.offsetTop = options.offsetBaseTop - options.mirrorTop * (1 - options.speed);
    } else {
      options.visibility = 'hidden';
    }

    this.$m.css({
      transform: 'translate3d(' + options.mirrorLeft + 'px, ' + (options.mirrorTop - overScroll) + 'px, 0px)',
      visibility: options.visibility,
      height: options.boxHeight,
      width: options.boxWidth,
    });

    this.$s.css({
      transform: 'translate3d(' + options.offsetLeft + 'px, ' + options.offsetTop + 'px, 0px)',
      position: 'absolute',
      height: options.imageHeight,
      width: options.imageWidth,
      maxWidth: 'none',
    });

    if (typeof options.afterRender === 'function')
      options.afterRender(this);
  }

  /**
   * destroys the current instance and puts the slide back where it was before initializing
   */
  destroy() {
    if (this.$m) { // might be empty on mobile
      this.$m.remove();
    }
    if (this.$s) {

      // remove slider from the sliders array
      for (let i = 0; i < Parallax.iList.length; i++) {
        if (Parallax.iList[i] === this) {
          Parallax.iList.splice(i, 1);
        }
      }

      // append slider back to old parent if exists
      if (this.o.formerParent) {
        this.$s.prop('style', this.o.formerStyles);
        this.o.formerParent.append(this.$s);
      }
    }

    if (Parallax.iList.length === 0) {
      $(window).off('scroll.px.parallax resize.px.parallax load.px.parallax');
      Parallax.isSet = false;
    }

    if (typeof options.afterDestroy === 'function')
      options.afterDestroy(this);
  }

  /////////////////////
  // Static Methods //
  ///////////////////

  /**
   * initializes the library and all necessary variables shared among all parallax instances
   */
  static init() {
    if (Parallax.isSet) return;

    /** @type jQuery*/
    const $doc = $(document);
    /** @type jQuery*/
    const $win = $(window);

    function loadDimensions() {
      Parallax.wH = $win.height();
      Parallax.wW = $win.width();
      Parallax.dH = $doc.height();
      Parallax.dW = $doc.width();
    }

    function loadScrollPosition() {
      const winScrollTop = $win.scrollTop();
      const scrollTopMax = Parallax.dH - Parallax.wH;
      const scrollLeftMax = Parallax.dW - Parallax.wW;
      Parallax.sT = Math.max(0, Math.min(scrollTopMax, winScrollTop));
      Parallax.sL = Math.max(0, Math.min(scrollLeftMax, $win.scrollLeft()));
      Parallax.overScroll = Math.max(winScrollTop - scrollTopMax, Math.min(winScrollTop, 0));
    }

    $win.on('resize.px.parallax load.px.parallax', function () {
      loadDimensions();
      Parallax.update(true);
    });

    loadDimensions();

    Parallax.isSet = true;

    let lastPosition = -1;
    (function loop() {
      const yoffset = window.pageYOffset;
      if (lastPosition !== yoffset) {   // Avoid overcalculations
        lastPosition = yoffset;
        loadScrollPosition();
        Parallax.update();
      }
      window.requestAnimationFrame(loop);
    })();
  }

  /**
   * renders all parallax instances
   * @param refresh when true, also call refresh on all instances
   */
  static update(refresh = false) {
    if (refresh)
      $.each(Parallax.iList, function () { this.refresh(); });
    $.each(Parallax.iList, function () { this.render(); });
  }
}

Parallax.DEFAULTS = {
  src: null,
  speed: .2,
  bleed: 0,
  zIndex: -100,
  posX: 'center',
  posY: 'center',
  overScrollFix: false,
  excludeAgents: /(iPod|iPhone|iPad|Android)/,
  aspectRatio: null,
  // jquery selectors
  sliderSelector: '>.parallax-slider',
  mirrorSelector: 'body',
  // callback functions:
  afterRefresh: null,
  afterRender: null,
  afterSetup: null,
  afterDestroy: null,
};

Parallax.AUTOINIT = true;

///////////////////////
// Global variables //
/////////////////////

/**
 * scroll top position
 * @type {number}
 */
Parallax.sT = 0;
/**
 * scroll left position
 * @type {number}
 */
Parallax.sL = 0;
/**
 * window height
 * @type {number}
 */
Parallax.wH = 0;
/**
 * window width
 * @type {number}
 */
Parallax.wW = 0;
/**
 * document height
 * @type {number}
 */
Parallax.dH = 1 << 30;
/**
 * document width
 * @type {number}
 */
Parallax.dW = 1 << 30;
/**
 * all instances
 * @type {Array}
 */
Parallax.iList = [];
/**
 * flag for global setup
 * @type {boolean}
 */
Parallax.isSet = false;

/**
 * call auto initialization. This can be supresst by setting the static Parallax.AUTOINIT parameter to false
 */
$(() => {
  if (Parallax.AUTOINIT) {
    $('[data-parallax]').parallax();
  }
});

generatePlugin('parallax', Parallax);
<commit_msg>Changed Parallax version<commit_after>/*!
 * parallax.js v1.5.0 (http://pixelcog.github.io/parallax.js/)
 * @copyright 2016 PixelCog, Inc.
 * @license MIT (https://github.com/pixelcog/parallax.js/blob/master/LICENSE)
 */

;(function ( $, window, document, undefined ) {

  // Polyfill for requestAnimationFrame
  // via: https://gist.github.com/paulirish/1579671

  (function() {
    var lastTime = 0;
    var vendors = ['ms', 'moz', 'webkit', 'o'];
    for(var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) {
      window.requestAnimationFrame = window[vendors[x]+'RequestAnimationFrame'];
      window.cancelAnimationFrame = window[vendors[x]+'CancelAnimationFrame'] || window[vendors[x]+'CancelRequestAnimationFrame'];
    }

    if (!window.requestAnimationFrame)
      window.requestAnimationFrame = function(callback) {
        var currTime = new Date().getTime();
        var timeToCall = Math.max(0, 16 - (currTime - lastTime));
        var id = window.setTimeout(function() { callback(currTime + timeToCall); },
          timeToCall);
        lastTime = currTime + timeToCall;
        return id;
      };

    if (!window.cancelAnimationFrame)
      window.cancelAnimationFrame = function(id) {
        clearTimeout(id);
      };
  }());

  // Parallax Constructor

  function Parallax(element, options) {
    var self = this;

    if (typeof options == 'object') {
      delete options.refresh;
      delete options.render;
      $.extend(this, options);
    }

    this.$element = $(element);

    if (!this.imageSrc && this.$element.is('img')) {
      this.imageSrc = this.$element.attr('src');
    }

    var positions = (this.position + '').toLowerCase().match(/\S+/g) || [];

    if (positions.length < 1) {
      positions.push('center');
    }
    if (positions.length == 1) {
      positions.push(positions[0]);
    }

    if (positions[0] == 'top' || positions[0] == 'bottom' || positions[1] == 'left' || positions[1] == 'right') {
      positions = [positions[1], positions[0]];
    }

    if (this.positionX !== undefined) positions[0] = this.positionX.toLowerCase();
    if (this.positionY !== undefined) positions[1] = this.positionY.toLowerCase();

    self.positionX = positions[0];
    self.positionY = positions[1];

    if (this.positionX != 'left' && this.positionX != 'right') {
      if (isNaN(parseInt(this.positionX))) {
        this.positionX = 'center';
      } else {
        this.positionX = parseInt(this.positionX);
      }
    }

    if (this.positionY != 'top' && this.positionY != 'bottom') {
      if (isNaN(parseInt(this.positionY))) {
        this.positionY = 'center';
      } else {
        this.positionY = parseInt(this.positionY);
      }
    }

    this.position =
      this.positionX + (isNaN(this.positionX)? '' : 'px') + ' ' +
      this.positionY + (isNaN(this.positionY)? '' : 'px');

    if (navigator.userAgent.match(/(iPod|iPhone|iPad)/)) {
      if (this.imageSrc && this.iosFix && !this.$element.is('img')) {
        this.$element.css({
          backgroundImage: 'url(' + this.imageSrc + ')',
          backgroundSize: 'cover',
          backgroundPosition: this.position
        });
      }
      return this;
    }

    if (navigator.userAgent.match(/(Android)/)) {
      if (this.imageSrc && this.androidFix && !this.$element.is('img')) {
        this.$element.css({
          backgroundImage: 'url(' + this.imageSrc + ')',
          backgroundSize: 'cover',
          backgroundPosition: this.position
        });
      }
      return this;
    }

    this.$mirror = $('<div />').prependTo(this.mirrorContainer);

    var slider = this.$element.find('>.parallax-slider');
    var sliderExisted = false;

    if (slider.length == 0)
      this.$slider = $('<img />').prependTo(this.$mirror);
    else {
      this.$slider = slider.prependTo(this.$mirror)
      sliderExisted = true;
    }

    this.$mirror.addClass('parallax-mirror').css({
      visibility: 'hidden',
      zIndex: this.zIndex,
      position: 'fixed',
      top: 0,
      left: 0,
      overflow: 'hidden'
    });

    this.$slider.addClass('parallax-slider').one('load', function() {
      if (!self.naturalHeight || !self.naturalWidth) {
        self.naturalHeight = this.naturalHeight || this.height || 1;
        self.naturalWidth  = this.naturalWidth  || this.width  || 1;
      }
      self.aspectRatio = self.naturalWidth / self.naturalHeight;

      Parallax.isSetup || Parallax.setup();
      Parallax.sliders.push(self);
      Parallax.isFresh = false;
      Parallax.requestRender();
    });

    if (!sliderExisted)
      this.$slider[0].src = this.imageSrc;

    if (this.naturalHeight && this.naturalWidth || this.$slider[0].complete || slider.length > 0) {
      this.$slider.trigger('load');
    }

  }

  // Parallax Instance Methods

  $.extend(Parallax.prototype, {
    speed:    0.2,
    bleed:    0,
    zIndex:   -100,
    iosFix:   true,
    androidFix: true,
    position: 'center',
    overScrollFix: false,
    mirrorContainer: 'body',

    refresh: function() {
      this.boxWidth        = this.$element.outerWidth();
      this.boxHeight       = this.$element.outerHeight() + this.bleed * 2;
      this.boxOffsetTop    = this.$element.offset().top - this.bleed;
      this.boxOffsetLeft   = this.$element.offset().left;
      this.boxOffsetBottom = this.boxOffsetTop + this.boxHeight;

      var winHeight = Parallax.winHeight;
      var docHeight = Parallax.docHeight;
      var maxOffset = Math.min(this.boxOffsetTop, docHeight - winHeight);
      var minOffset = Math.max(this.boxOffsetTop + this.boxHeight - winHeight, 0);
      var imageHeightMin = this.boxHeight + (maxOffset - minOffset) * (1 - this.speed) | 0;
      var imageOffsetMin = (this.boxOffsetTop - maxOffset) * (1 - this.speed) | 0;
      var margin;

      if (imageHeightMin * this.aspectRatio >= this.boxWidth) {
        this.imageWidth    = imageHeightMin * this.aspectRatio | 0;
        this.imageHeight   = imageHeightMin;
        this.offsetBaseTop = imageOffsetMin;

        margin = this.imageWidth - this.boxWidth;

        if (this.positionX == 'left') {
          this.offsetLeft = 0;
        } else if (this.positionX == 'right') {
          this.offsetLeft = - margin;
        } else if (!isNaN(this.positionX)) {
          this.offsetLeft = Math.max(this.positionX, - margin);
        } else {
          this.offsetLeft = - margin / 2 | 0;
        }
      } else {
        this.imageWidth    = this.boxWidth;
        this.imageHeight   = this.boxWidth / this.aspectRatio | 0;
        this.offsetLeft    = 0;

        margin = this.imageHeight - imageHeightMin;

        if (this.positionY == 'top') {
          this.offsetBaseTop = imageOffsetMin;
        } else if (this.positionY == 'bottom') {
          this.offsetBaseTop = imageOffsetMin - margin;
        } else if (!isNaN(this.positionY)) {
          this.offsetBaseTop = imageOffsetMin + Math.max(this.positionY, - margin);
        } else {
          this.offsetBaseTop = imageOffsetMin - margin / 2 | 0;
        }
      }
    },

    render: function() {
      var scrollTop    = Parallax.scrollTop;
      var scrollLeft   = Parallax.scrollLeft;
      var overScroll   = this.overScrollFix ? Parallax.overScroll : 0;
      var scrollBottom = scrollTop + Parallax.winHeight;

      if (this.boxOffsetBottom > scrollTop && this.boxOffsetTop <= scrollBottom) {
        this.visibility = 'visible';
        this.mirrorTop = this.boxOffsetTop  - scrollTop;
        this.mirrorLeft = this.boxOffsetLeft - scrollLeft;
        this.offsetTop = this.offsetBaseTop - this.mirrorTop * (1 - this.speed);
      } else {
        this.visibility = 'hidden';
      }

      this.$mirror.css({
        transform: 'translate3d('+this.mirrorLeft+'px, '+(this.mirrorTop - overScroll)+'px, 0px)',
        visibility: this.visibility,
        height: this.boxHeight,
        width: this.boxWidth
      });

      this.$slider.css({
        transform: 'translate3d('+this.offsetLeft+'px, '+this.offsetTop+'px, 0px)',
        position: 'absolute',
        height: this.imageHeight,
        width: this.imageWidth,
        maxWidth: 'none'
      });
    }
  });

  // Parallax Static Methods

  $.extend(Parallax, {
    scrollTop:    0,
    scrollLeft:   0,
    winHeight:    0,
    winWidth:     0,
    docHeight:    1 << 30,
    docWidth:     1 << 30,
    sliders:      [],
    isReady:      false,
    isFresh:      false,
    isBusy:       false,

    setup: function() {
      if (this.isReady) return;

      var self = this;

      var $doc = $(document), $win = $(window);

      var loadDimensions = function() {
        Parallax.winHeight = $win.height();
        Parallax.winWidth  = $win.width();
        Parallax.docHeight = $doc.height();
        Parallax.docWidth  = $doc.width();
      };

      var loadScrollPosition = function() {
        var winScrollTop  = $win.scrollTop();
        var scrollTopMax  = Parallax.docHeight - Parallax.winHeight;
        var scrollLeftMax = Parallax.docWidth  - Parallax.winWidth;
        Parallax.scrollTop  = Math.max(0, Math.min(scrollTopMax,  winScrollTop));
        Parallax.scrollLeft = Math.max(0, Math.min(scrollLeftMax, $win.scrollLeft()));
        Parallax.overScroll = Math.max(winScrollTop - scrollTopMax, Math.min(winScrollTop, 0));
      };

      $win.on('resize.px.parallax load.px.parallax', function() {
          loadDimensions();
          self.refresh();
          Parallax.isFresh = false;
          Parallax.requestRender();
        })
        .on('scroll.px.parallax load.px.parallax', function() {
          loadScrollPosition();
          Parallax.requestRender();
        });

      loadDimensions();
      loadScrollPosition();

      this.isReady = true;

      var lastPosition = -1;

      function frameLoop() {
        if (lastPosition == window.pageYOffset) {   // Avoid overcalculations
          window.requestAnimationFrame(frameLoop);
          return false;
        } else lastPosition = window.pageYOffset;

        self.render();
        window.requestAnimationFrame(frameLoop);
      }

      frameLoop();
    },

    configure: function(options) {
      if (typeof options == 'object') {
        delete options.refresh;
        delete options.render;
        $.extend(this.prototype, options);
      }
    },

    refresh: function() {
      $.each(this.sliders, function(){ this.refresh(); });
      this.isFresh = true;
    },

    render: function() {
      this.isFresh || this.refresh();
      $.each(this.sliders, function(){ this.render(); });
    },

    requestRender: function() {
      var self = this;
      self.render();
      self.isBusy = false;
    },
    destroy: function(el){
      var i,
          parallaxElement = $(el).data('px.parallax');
      parallaxElement.$mirror.remove();
      for(i=0; i < this.sliders.length; i+=1){
        if(this.sliders[i] == parallaxElement){
          this.sliders.splice(i, 1);
        }
      }
      $(el).data('px.parallax', false);
      if(this.sliders.length === 0){
        $(window).off('scroll.px.parallax resize.px.parallax load.px.parallax');
        this.isReady = false;
        Parallax.isSetup = false;
      }
    }
  });

  // Parallax Plugin Definition

  function Plugin(option) {
    return this.each(function () {
      var $this = $(this);
      var options = typeof option == 'object' && option;

      if (this == window || this == document || $this.is('body')) {
        Parallax.configure(options);
      }
      else if (!$this.data('px.parallax')) {
        options = $.extend({}, $this.data(), options);
        $this.data('px.parallax', new Parallax(this, options));
      }
      else if (typeof option == 'object')
      {
        $.extend($this.data('px.parallax'), options);
      }
      if (typeof option == 'string') {
        if(option == 'destroy'){
            Parallax.destroy(this);
        }else{
          Parallax[option]();
        }
      }
    });
  }

  var old = $.fn.parallax;

  $.fn.parallax             = Plugin;
  $.fn.parallax.Constructor = Parallax;

  // Parallax No Conflict

  $.fn.parallax.noConflict = function () {
    $.fn.parallax = old;
    return this;
  };

  // Parallax Data-API

  $( function () {
    $('[data-parallax="scroll"]').parallax();
  });

}(jQuery, window, document));
<|endoftext|>