<commit_before>#!/usr/bin/env node

const fs = require('fs-extra')
const path = require('path')
const spawnSync = require('child_process').spawnSync

const program = require('commander')

const build = (sourceDir) => {
  // Check that the source directory exists and chdir to it.
  if (!fs.existsSync(sourceDir)) {
    console.error('The source directory: ' + sourceDir + ' does not exist!')
    process.exit(1)
  }
  process.chdir(sourceDir)

  // Make the 'web-build' directory to hold the dockcross script and the CMake
  // build.
  try {
    fs.mkdirSync('web-build')
  } catch (err) {
    if (err.code !== 'EEXIST') throw err
  }

  // Check that we have docker and can run it.
  const dockerVersion = spawnSync('docker', ['--version'], {
    env: process.env,
    stdio: [ 'ignore', 'ignore', 'ignore' ]
  })
  if (dockerVersion.status !== 0) {
    console.error("Could not run the 'docker' command.")
    console.error('This tool requires Docker.')
    console.error('')
    console.error('Please find installation instructions at:')
    console.error('')
    console.error('  https://docs.docker.com/install/')
    console.error('')
    process.exit(dockerVersion.status)
  }

  let dockerImage = 'insighttoolkit/itk-js'
  if (program.commands[0].image) {
    dockerImage = program.commands[0].image
  }

  // Ensure we have the 'dockcross' Docker build environment driver script
  const dockcrossScript = path.join('web-build', 'itk-js-build-env')
  try {
    fs.statSync(dockcrossScript)
  } catch (err) {
    if (err.code === 'ENOENT') {
      const output = fs.openSync(dockcrossScript, 'w')
      const dockerCall = spawnSync('docker', ['run', '--rm', dockerImage], {
        env: process.env,
        stdio: [ 'ignore', output, null ]
      })
      if (dockerCall.status !== 0) {
        process.exit(dockerCall.status)
      }
      fs.closeSync(output)
      fs.chmodSync(dockcrossScript, '755')
    } else {
      throw err
    }
  }

  const hypenIndex = program.rawArgs.findIndex((arg) => arg === '--')
  let cmakeArgs = []
  if (hypenIndex !== -1) {
    cmakeArgs = program.rawArgs.slice(hypenIndex + 1)
  }

  const dockerBuild = spawnSync(dockcrossScript, ['web-build'].concat(cmakeArgs), {
    env: process.env,
    stdio: 'inherit'
  })
  if (dockerBuild.status !== 0) {
    process.exit(dockerBuild.status)
  }
}

const test = (sourceDir) => {
  // Check that the source directory exists and chdir to it.
  if (!fs.existsSync(sourceDir)) {
    console.error('The source directory: ' + sourceDir + ' does not exist!')
    process.exit(1)
  }
  process.chdir(sourceDir)

  const dockcrossScript = path.join('web-build', 'itk-js-build-env')
  try {
    fs.statSync(dockcrossScript)
  } catch (err) {
    console.error('Could not find ' + sourceDir + '/web-build/itk-js-build-env')
    console.error('')
    console.error('Has')
    console.error('')
    console.error('  itk-js build ' + sourceDir)
    console.error('')
    console.error('been executed?')
    process.exit(1)
  }

  const hypenIndex = program.rawArgs.findIndex((arg) => arg === '--')
  let ctestArgs = ''
  if (hypenIndex !== -1) {
    ctestArgs = program.rawArgs.slice(hypenIndex + 1).join(' ')
  }

  const dockerBuild = spawnSync(dockcrossScript,
    ['bash', '-c',
      'cd web-build && ctest ' + ctestArgs],
    {
      env: process.env,
      stdio: 'inherit'
    })
  if (dockerBuild.status !== 0) {
    process.exit(dockerBuild.status)
  }
}

program
  .command('build <sourceDir>')
  .usage('[options] <sourceDir> [-- <cmake arguments>]')
  .description('build the CMake project found in the given source directory')
  .action(build)
  .option('-i, --image <image>', 'build environment Docker image, defaults to insighttoolkit/itk-js')

program
  .command('test <sourceDir>')
  .usage('[options] <sourceDir> [-- <ctest arguments>]')
  .description('run ctest on the project previously built from the given source directory')
  .action(test)

program
  .parse(process.argv)
<commit_msg>fix(itk-js-cli): Disable test command -- more work is needed to work with inputs<commit_after>#!/usr/bin/env node

const fs = require('fs-extra')
const path = require('path')
const spawnSync = require('child_process').spawnSync

const program = require('commander')

const build = (sourceDir) => {
  // Check that the source directory exists and chdir to it.
  if (!fs.existsSync(sourceDir)) {
    console.error('The source directory: ' + sourceDir + ' does not exist!')
    process.exit(1)
  }
  process.chdir(sourceDir)

  // Make the 'web-build' directory to hold the dockcross script and the CMake
  // build.
  try {
    fs.mkdirSync('web-build')
  } catch (err) {
    if (err.code !== 'EEXIST') throw err
  }

  // Check that we have docker and can run it.
  const dockerVersion = spawnSync('docker', ['--version'], {
    env: process.env,
    stdio: [ 'ignore', 'ignore', 'ignore' ]
  })
  if (dockerVersion.status !== 0) {
    console.error("Could not run the 'docker' command.")
    console.error('This tool requires Docker.')
    console.error('')
    console.error('Please find installation instructions at:')
    console.error('')
    console.error('  https://docs.docker.com/install/')
    console.error('')
    process.exit(dockerVersion.status)
  }

  let dockerImage = 'insighttoolkit/itk-js'
  if (program.commands[0].image) {
    dockerImage = program.commands[0].image
  }

  // Ensure we have the 'dockcross' Docker build environment driver script
  const dockcrossScript = path.join('web-build', 'itk-js-build-env')
  try {
    fs.statSync(dockcrossScript)
  } catch (err) {
    if (err.code === 'ENOENT') {
      const output = fs.openSync(dockcrossScript, 'w')
      const dockerCall = spawnSync('docker', ['run', '--rm', dockerImage], {
        env: process.env,
        stdio: [ 'ignore', output, null ]
      })
      if (dockerCall.status !== 0) {
        process.exit(dockerCall.status)
      }
      fs.closeSync(output)
      fs.chmodSync(dockcrossScript, '755')
    } else {
      throw err
    }
  }

  const hypenIndex = program.rawArgs.findIndex((arg) => arg === '--')
  let cmakeArgs = []
  if (hypenIndex !== -1) {
    cmakeArgs = program.rawArgs.slice(hypenIndex + 1)
  }

  const dockerBuild = spawnSync(dockcrossScript, ['web-build'].concat(cmakeArgs), {
    env: process.env,
    stdio: 'inherit'
  })
  if (dockerBuild.status !== 0) {
    process.exit(dockerBuild.status)
  }
}

const test = (sourceDir) => {
  // Check that the source directory exists and chdir to it.
  if (!fs.existsSync(sourceDir)) {
    console.error('The source directory: ' + sourceDir + ' does not exist!')
    process.exit(1)
  }
  process.chdir(sourceDir)

  const dockcrossScript = path.join('web-build', 'itk-js-build-env')
  try {
    fs.statSync(dockcrossScript)
  } catch (err) {
    console.error('Could not find ' + sourceDir + '/web-build/itk-js-build-env')
    console.error('')
    console.error('Has')
    console.error('')
    console.error('  itk-js build ' + sourceDir)
    console.error('')
    console.error('been executed?')
    process.exit(1)
  }

  const hypenIndex = program.rawArgs.findIndex((arg) => arg === '--')
  let ctestArgs = ''
  if (hypenIndex !== -1) {
    ctestArgs = program.rawArgs.slice(hypenIndex + 1).join(' ')
  }

  const dockerBuild = spawnSync(dockcrossScript,
    ['bash', '-c',
      'cd web-build && ctest ' + ctestArgs],
    {
      env: process.env,
      stdio: 'inherit'
    })
  if (dockerBuild.status !== 0) {
    process.exit(dockerBuild.status)
  }
}

program
  .command('build <sourceDir>')
  .usage('[options] <sourceDir> [-- <cmake arguments>]')
  .description('build the CMake project found in the given source directory')
  .action(build)
  .option('-i, --image <image>', 'build environment Docker image, defaults to insighttoolkit/itk-js')

// todo: needs a wrapper in web_add_test that 1) mount /work into the emscripten filesystem
// and 2) invokes the runtime
// program
//   .command('test <sourceDir>')
//   .usage('[options] <sourceDir> [-- <ctest arguments>]')
//   .description('run ctest on the project previously built from the given source directory')
//   .action(test)

program
  .parse(process.argv)
<|endoftext|>


<commit_before>/**
 * Infinite Ajax Scroll v2.1.3
 * A jQuery plugin for infinite scrolling
 * http://infiniteajaxscroll.com
 *
 * Commercial use requires one-time purchase of a commercial license
 * http://infiniteajaxscroll.com/docs/license.html
 *
 * Non-commercial use is licensed under the MIT License
 *
 * Copyright 2014 Webcreate (Jeroen Fiege)
 */

(function($) {

  'use strict';

  var UNDETERMINED_SCROLLOFFSET = -1;

  var IAS = function($element, options) {
    this.itemsContainerSelector = options.container;
    this.itemSelector = options.item;
    this.nextSelector = options.next;
    this.paginationSelector = options.pagination;
    this.$scrollContainer = $element;
    this.$container = (window === $element.get(0) ? $(document) : $element);
    this.defaultDelay = options.delay;
    this.negativeMargin = options.negativeMargin;
    this.nextUrl = null;
    this.isBound = false;
    this.listeners = {
      next:     new IASCallbacks(),
      load:     new IASCallbacks(),
      loaded:   new IASCallbacks(),
      render:   new IASCallbacks(),
      rendered: new IASCallbacks(),
      scroll:   new IASCallbacks(),
      noneLeft: new IASCallbacks(),
      ready:    new IASCallbacks()
    };
    this.extensions = [];

    /**
     * Scroll event handler
     *
     * Note: calls to this functions should be throttled
     *
     * @private
     */
    this.scrollHandler = function() {
      var currentScrollOffset = this.getCurrentScrollOffset(this.$scrollContainer),
          scrollThreshold = this.getScrollThreshold()
      ;

      // the throttle method can call the scrollHandler even thought we have called unbind()
      if (!this.isBound) {
        return;
      }

      // invalid scrollThreshold. The DOM might not have loaded yet...
      if (UNDETERMINED_SCROLLOFFSET == scrollThreshold) {
        return;
      }

      this.fire('scroll', [currentScrollOffset, scrollThreshold]);

      if (currentScrollOffset >= scrollThreshold) {
        this.next();
      }
    };

    /**
     * Returns the items container currently in the DOM
     *
     * @private
     * @returns {object}
     */
    this.getItemsContainer = function() {
      return $(this.itemsContainerSelector);
    };

    /**
     * Returns the last item currently in the DOM
     *
     * @private
     * @returns {object}
     */
    this.getLastItem = function() {
      return $(this.itemSelector, this.getItemsContainer().get(0)).last();
    };

    /**
     * Returns the first item currently in the DOM
     *
     * @private
     * @returns {object}
     */
    this.getFirstItem = function() {
      return $(this.itemSelector, this.getItemsContainer().get(0)).first();
    };

    /**
     * Returns scroll threshold. This threshold marks the line from where
     * IAS should start loading the next page.
     *
     * @private
     * @param negativeMargin defaults to {this.negativeMargin}
     * @return {number}
     */
    this.getScrollThreshold = function(negativeMargin) {
      var $lastElement;

      negativeMargin = negativeMargin || this.negativeMargin;
      negativeMargin = (negativeMargin >= 0 ? negativeMargin * -1 : negativeMargin);

      $lastElement = this.getLastItem();

      // if the don't have a last element, the DOM might not have been loaded,
      // or the selector is invalid
      if (0 === $lastElement.size()) {
        return UNDETERMINED_SCROLLOFFSET;
      }

      return ($lastElement.offset().top + $lastElement.height() + negativeMargin);
    };

    /**
     * Returns current scroll offset for the given scroll container
     *
     * @private
     * @param $container
     * @returns {number}
     */
    this.getCurrentScrollOffset = function($container) {
      var scrollTop = 0,
          containerHeight = $container.height();

      if (window === $container.get(0))  {
        scrollTop = $container.scrollTop();
      } else {
        scrollTop = $container.offset().top;
      }

      // compensate for iPhone
      if (navigator.platform.indexOf("iPhone") != -1 || navigator.platform.indexOf("iPod") != -1) {
        containerHeight += 80;
      }

      return (scrollTop + containerHeight);
    };

    /**
     * Returns the url for the next page
     *
     * @private
     */
    this.getNextUrl = function(container) {
      container = container || this.$container;

      // always take the last matching item
      return $(this.nextSelector, container).last().attr('href');
    };

    /**
     * Loads a page url
     *
     * @param url
     * @param callback
     * @param delay
     * @returns {object}        jsXhr object
     */
    this.load = function(url, callback, delay) {
      var self = this,
          $itemContainer,
          items = [],
          timeStart = +new Date(),
          timeDiff;

      delay = delay || this.defaultDelay;

      var loadEvent = {
        url: url
      };

      self.fire('load', [loadEvent]);

      return $.get(loadEvent.url, null, $.proxy(function(data) {
        $itemContainer = $(this.itemsContainerSelector, data).eq(0);
        if (0 === $itemContainer.length) {
          $itemContainer = $(data).filter(this.itemsContainerSelector).eq(0);
        }

        if ($itemContainer) {
          $itemContainer.find(this.itemSelector).each(function() {
            items.push(this);
          });
        }

        self.fire('loaded', [data, items]);

        if (callback) {
          timeDiff = +new Date() - timeStart;
          if (timeDiff < delay) {
            setTimeout(function() {
              callback.call(self, data, items);
            }, delay - timeDiff);
          } else {
            callback.call(self, data, items);
          }
        }
      }, self), 'html');
    };

    /**
     * Renders items
     *
     * @param callback
     * @param items
     */
    this.render = function(items, callback) {
      var self = this,
          $lastItem = this.getLastItem(),
          count = 0;

      var promise = this.fire('render', [items]);

      promise.done(function() {
        $(items).hide(); // at first, hide it so we can fade it in later

        $lastItem.after(items);

        $(items).fadeIn(400, function() {
          // complete callback get fired for each item,
          // only act on the last item
          if (++count < items.length) {
            return;
          }

          self.fire('rendered', [items]);

          if (callback) {
            callback();
          }
        });
      });
    };

    /**
     * Hides the pagination
     */
    this.hidePagination = function() {
      if (this.paginationSelector) {
        $(this.paginationSelector, this.$container).hide();
      }
    };

    /**
     * Restores the pagination
     */
    this.restorePagination = function() {
      if (this.paginationSelector) {
        $(this.paginationSelector, this.$container).show();
      }
    };

    /**
     * Throttles a method
     *
     * Adopted from Ben Alman's jQuery throttle / debounce plugin
     *
     * @param callback
     * @param delay
     * @return {object}
     */
    this.throttle = function(callback, delay) {
      var lastExecutionTime = 0,
          wrapper,
          timerId
      ;

      wrapper = function() {
        var that = this,
            args = arguments,
            diff = +new Date() - lastExecutionTime;

        function execute() {
          lastExecutionTime = +new Date();
          callback.apply(that, args);
        }

        if (!timerId) {
          execute();
        } else {
          clearTimeout(timerId);
        }

        if (diff > delay) {
          execute();
        } else {
          timerId = setTimeout(execute, delay);
        }
      };

      if ($.guid) {
        wrapper.guid = callback.guid = callback.guid || $.guid++;
      }

      return wrapper;
    };

    /**
     * Fires an event with the ability to cancel further processing. This
     * can be achieved by returning false in a listener.
     *
     * @param event
     * @param args
     * @returns {*}
     */
    this.fire = function(event, args) {
      return this.listeners[event].fireWith(this, args);
    };

    return this;
  };

  /**
   * Initialize IAS
   *
   * Note: Should be called when the document is ready
   *
   * @public
   */
  IAS.prototype.initialize = function() {
    var currentScrollOffset = this.getCurrentScrollOffset(this.$scrollContainer),
        scrollThreshold = this.getScrollThreshold();

    this.hidePagination();
    this.bind();

    for (var i = 0, l = this.extensions.length; i < l; i++) {
      this.extensions[i].bind(this);
    }

    this.fire('ready');

    this.nextUrl = this.getNextUrl();

    // start loading next page if content is shorter than page fold
    if (currentScrollOffset >= scrollThreshold) {
      this.next();
    }

    return this;
  };

  /**
   * Reinitializes IAS, for example after an ajax page update
   *
   * @public
   */
  IAS.prototype.reinitialize = function () {
    this.unbind();
    this.initialize();
  };

  /**
   * Binds IAS to DOM events
   *
   * @public
   */
  IAS.prototype.bind = function() {
    if (this.isBound) {
      return;
    }

    this.$scrollContainer.on('scroll', $.proxy(this.throttle(this.scrollHandler, 150), this));

    this.isBound = true;
  };

  /**
   * Unbinds IAS to events
   *
   * @public
   */
  IAS.prototype.unbind = function() {
    if (!this.isBound) {
      return;
    }

    this.$scrollContainer.off('scroll', this.scrollHandler);

    this.isBound = false;
  };

  /**
   * Destroys IAS instance
   *
   * @public
   */
  IAS.prototype.destroy = function() {
    this.unbind();
  };

  /**
   * Registers an eventListener
   *
   * Note: chainable
   *
   * @public
   * @returns IAS
   */
  IAS.prototype.on = function(event, callback, priority) {
    if (typeof this.listeners[event] == 'undefined') {
      throw new Error('There is no event called "' + event + '"');
    }

    priority = priority || 0;

    this.listeners[event].add($.proxy(callback, this), priority);

    return this;
  };

  /**
   * Registers an eventListener which only gets
   * fired once.
   *
   * Note: chainable
   *
   * @public
   * @returns IAS
   */
  IAS.prototype.one = function(event, callback) {
    var self = this;

    var remover = function() {
      self.off(event, callback);
      self.off(event, remover);
    };

    this.on(event, callback);
    this.on(event, remover);

    return this;
  };

  /**
   * Removes an eventListener
   *
   * Note: chainable
   *
   * @public
   * @returns IAS
   */
  IAS.prototype.off = function(event, callback) {
    if (typeof this.listeners[event] == 'undefined') {
      throw new Error('There is no event called "' + event + '"');
    }

    this.listeners[event].remove(callback);

    return this;
  };

  /**
   * Load the next page
   *
   * @public
   */
  IAS.prototype.next = function() {
    var url = this.nextUrl,
        self = this;

    this.unbind();

    if (!url) {
      this.fire('noneLeft', [this.getLastItem()]);
      this.listeners['noneLeft'].disable(); // disable it so it only fires once

      self.bind();

      return false;
    }

    var promise = this.fire('next', [url]);

    promise.done(function() {
      self.load(url, function(data, items) {
        self.render(items, function() {
          self.nextUrl = self.getNextUrl(data);

          self.bind();
        });
      });
    });

    promise.fail(function() {
      self.bind();
    });

    return true;
  };

  /**
   * Adds an extension
   *
   * @public
   */
  IAS.prototype.extension = function(extension) {
    if (typeof extension['bind'] == 'undefined') {
      throw new Error('Extension doesn\'t have required method "bind"');
    }

    if (typeof extension['initialize'] != 'undefined') {
      extension.initialize(this);
    }

    this.extensions.push(extension);

    return this;
  };

  /**
   * Shortcut. Sets the window as scroll container.
   *
   * @public
   * @param option
   * @returns {*}
   */
  $.ias = function(option) {
    var $window = $(window);

    return $window.ias.apply($window, arguments);
  };

  /**
   * jQuery plugin initialization
   *
   * @public
   * @param option
   * @returns {*} the last IAS instance will be returned
   */
  $.fn.ias = function(option) {
    var args = Array.prototype.slice.call(arguments);
    var retval = this;

    this.each(function() {
      var $this = $(this),
          data = $this.data('ias'),
          options = $.extend({}, $.fn.ias.defaults, $this.data(), typeof option == 'object' && option)
      ;

      // set a new instance as data
      if (!data) {
        $this.data('ias', (data = new IAS($this, options)));

        $(document).ready($.proxy(data.initialize, data));
      }

      // when the plugin is called with a method
      if (typeof option === 'string') {
        if (typeof data[option] !== 'function') {
          throw new Error('There is no method called "' + option + '"');
        }

        args.shift(); // remove first argument ('option')
        data[option].apply(data, args);

        if (option === 'destroy') {
          $this.data('ias', null);
        }
      }

      retval = $this.data('ias');
    });

    return retval;
  };

  /**
   * Plugin defaults
   *
   * @public
   * @type {object}
   */
  $.fn.ias.defaults = {
    item: '.item',
    container: '.listing',
    next: '.next',
    pagination: false,
    delay: 600,
    negativeMargin: 10
  };
})(jQuery);
<commit_msg>really destroy instance on destroy method (fixes #160)<commit_after>/**
 * Infinite Ajax Scroll v2.1.3
 * A jQuery plugin for infinite scrolling
 * http://infiniteajaxscroll.com
 *
 * Commercial use requires one-time purchase of a commercial license
 * http://infiniteajaxscroll.com/docs/license.html
 *
 * Non-commercial use is licensed under the MIT License
 *
 * Copyright 2014 Webcreate (Jeroen Fiege)
 */

(function($) {

  'use strict';

  var UNDETERMINED_SCROLLOFFSET = -1;

  var IAS = function($element, options) {
    this.itemsContainerSelector = options.container;
    this.itemSelector = options.item;
    this.nextSelector = options.next;
    this.paginationSelector = options.pagination;
    this.$scrollContainer = $element;
    this.$container = (window === $element.get(0) ? $(document) : $element);
    this.defaultDelay = options.delay;
    this.negativeMargin = options.negativeMargin;
    this.nextUrl = null;
    this.isBound = false;
    this.listeners = {
      next:     new IASCallbacks(),
      load:     new IASCallbacks(),
      loaded:   new IASCallbacks(),
      render:   new IASCallbacks(),
      rendered: new IASCallbacks(),
      scroll:   new IASCallbacks(),
      noneLeft: new IASCallbacks(),
      ready:    new IASCallbacks()
    };
    this.extensions = [];

    /**
     * Scroll event handler
     *
     * Note: calls to this functions should be throttled
     *
     * @private
     */
    this.scrollHandler = function() {
      var currentScrollOffset = this.getCurrentScrollOffset(this.$scrollContainer),
          scrollThreshold = this.getScrollThreshold()
      ;

      // the throttle method can call the scrollHandler even thought we have called unbind()
      if (!this.isBound) {
        return;
      }

      // invalid scrollThreshold. The DOM might not have loaded yet...
      if (UNDETERMINED_SCROLLOFFSET == scrollThreshold) {
        return;
      }

      this.fire('scroll', [currentScrollOffset, scrollThreshold]);

      if (currentScrollOffset >= scrollThreshold) {
        this.next();
      }
    };

    /**
     * Returns the items container currently in the DOM
     *
     * @private
     * @returns {object}
     */
    this.getItemsContainer = function() {
      return $(this.itemsContainerSelector);
    };

    /**
     * Returns the last item currently in the DOM
     *
     * @private
     * @returns {object}
     */
    this.getLastItem = function() {
      return $(this.itemSelector, this.getItemsContainer().get(0)).last();
    };

    /**
     * Returns the first item currently in the DOM
     *
     * @private
     * @returns {object}
     */
    this.getFirstItem = function() {
      return $(this.itemSelector, this.getItemsContainer().get(0)).first();
    };

    /**
     * Returns scroll threshold. This threshold marks the line from where
     * IAS should start loading the next page.
     *
     * @private
     * @param negativeMargin defaults to {this.negativeMargin}
     * @return {number}
     */
    this.getScrollThreshold = function(negativeMargin) {
      var $lastElement;

      negativeMargin = negativeMargin || this.negativeMargin;
      negativeMargin = (negativeMargin >= 0 ? negativeMargin * -1 : negativeMargin);

      $lastElement = this.getLastItem();

      // if the don't have a last element, the DOM might not have been loaded,
      // or the selector is invalid
      if (0 === $lastElement.size()) {
        return UNDETERMINED_SCROLLOFFSET;
      }

      return ($lastElement.offset().top + $lastElement.height() + negativeMargin);
    };

    /**
     * Returns current scroll offset for the given scroll container
     *
     * @private
     * @param $container
     * @returns {number}
     */
    this.getCurrentScrollOffset = function($container) {
      var scrollTop = 0,
          containerHeight = $container.height();

      if (window === $container.get(0))  {
        scrollTop = $container.scrollTop();
      } else {
        scrollTop = $container.offset().top;
      }

      // compensate for iPhone
      if (navigator.platform.indexOf("iPhone") != -1 || navigator.platform.indexOf("iPod") != -1) {
        containerHeight += 80;
      }

      return (scrollTop + containerHeight);
    };

    /**
     * Returns the url for the next page
     *
     * @private
     */
    this.getNextUrl = function(container) {
      container = container || this.$container;

      // always take the last matching item
      return $(this.nextSelector, container).last().attr('href');
    };

    /**
     * Loads a page url
     *
     * @param url
     * @param callback
     * @param delay
     * @returns {object}        jsXhr object
     */
    this.load = function(url, callback, delay) {
      var self = this,
          $itemContainer,
          items = [],
          timeStart = +new Date(),
          timeDiff;

      delay = delay || this.defaultDelay;

      var loadEvent = {
        url: url
      };

      self.fire('load', [loadEvent]);

      return $.get(loadEvent.url, null, $.proxy(function(data) {
        $itemContainer = $(this.itemsContainerSelector, data).eq(0);
        if (0 === $itemContainer.length) {
          $itemContainer = $(data).filter(this.itemsContainerSelector).eq(0);
        }

        if ($itemContainer) {
          $itemContainer.find(this.itemSelector).each(function() {
            items.push(this);
          });
        }

        self.fire('loaded', [data, items]);

        if (callback) {
          timeDiff = +new Date() - timeStart;
          if (timeDiff < delay) {
            setTimeout(function() {
              callback.call(self, data, items);
            }, delay - timeDiff);
          } else {
            callback.call(self, data, items);
          }
        }
      }, self), 'html');
    };

    /**
     * Renders items
     *
     * @param callback
     * @param items
     */
    this.render = function(items, callback) {
      var self = this,
          $lastItem = this.getLastItem(),
          count = 0;

      var promise = this.fire('render', [items]);

      promise.done(function() {
        $(items).hide(); // at first, hide it so we can fade it in later

        $lastItem.after(items);

        $(items).fadeIn(400, function() {
          // complete callback get fired for each item,
          // only act on the last item
          if (++count < items.length) {
            return;
          }

          self.fire('rendered', [items]);

          if (callback) {
            callback();
          }
        });
      });
    };

    /**
     * Hides the pagination
     */
    this.hidePagination = function() {
      if (this.paginationSelector) {
        $(this.paginationSelector, this.$container).hide();
      }
    };

    /**
     * Restores the pagination
     */
    this.restorePagination = function() {
      if (this.paginationSelector) {
        $(this.paginationSelector, this.$container).show();
      }
    };

    /**
     * Throttles a method
     *
     * Adopted from Ben Alman's jQuery throttle / debounce plugin
     *
     * @param callback
     * @param delay
     * @return {object}
     */
    this.throttle = function(callback, delay) {
      var lastExecutionTime = 0,
          wrapper,
          timerId
      ;

      wrapper = function() {
        var that = this,
            args = arguments,
            diff = +new Date() - lastExecutionTime;

        function execute() {
          lastExecutionTime = +new Date();
          callback.apply(that, args);
        }

        if (!timerId) {
          execute();
        } else {
          clearTimeout(timerId);
        }

        if (diff > delay) {
          execute();
        } else {
          timerId = setTimeout(execute, delay);
        }
      };

      if ($.guid) {
        wrapper.guid = callback.guid = callback.guid || $.guid++;
      }

      return wrapper;
    };

    /**
     * Fires an event with the ability to cancel further processing. This
     * can be achieved by returning false in a listener.
     *
     * @param event
     * @param args
     * @returns {*}
     */
    this.fire = function(event, args) {
      return this.listeners[event].fireWith(this, args);
    };

    return this;
  };

  /**
   * Initialize IAS
   *
   * Note: Should be called when the document is ready
   *
   * @public
   */
  IAS.prototype.initialize = function() {
    var currentScrollOffset = this.getCurrentScrollOffset(this.$scrollContainer),
        scrollThreshold = this.getScrollThreshold();

    this.hidePagination();
    this.bind();

    for (var i = 0, l = this.extensions.length; i < l; i++) {
      this.extensions[i].bind(this);
    }

    this.fire('ready');

    this.nextUrl = this.getNextUrl();

    // start loading next page if content is shorter than page fold
    if (currentScrollOffset >= scrollThreshold) {
      this.next();
    }

    return this;
  };

  /**
   * Reinitializes IAS, for example after an ajax page update
   *
   * @public
   */
  IAS.prototype.reinitialize = function () {
    this.unbind();
    this.initialize();
  };

  /**
   * Binds IAS to DOM events
   *
   * @public
   */
  IAS.prototype.bind = function() {
    if (this.isBound) {
      return;
    }

    this.$scrollContainer.on('scroll', $.proxy(this.throttle(this.scrollHandler, 150), this));

    this.isBound = true;
  };

  /**
   * Unbinds IAS to events
   *
   * @public
   */
  IAS.prototype.unbind = function() {
    if (!this.isBound) {
      return;
    }

    this.$scrollContainer.off('scroll', this.scrollHandler);

    this.isBound = false;
  };

  /**
   * Destroys IAS instance
   *
   * @public
   */
  IAS.prototype.destroy = function() {
    this.unbind();

    this.$scrollContainer.data('ias', null);
  };

  /**
   * Registers an eventListener
   *
   * Note: chainable
   *
   * @public
   * @returns IAS
   */
  IAS.prototype.on = function(event, callback, priority) {
    if (typeof this.listeners[event] == 'undefined') {
      throw new Error('There is no event called "' + event + '"');
    }

    priority = priority || 0;

    this.listeners[event].add($.proxy(callback, this), priority);

    return this;
  };

  /**
   * Registers an eventListener which only gets
   * fired once.
   *
   * Note: chainable
   *
   * @public
   * @returns IAS
   */
  IAS.prototype.one = function(event, callback) {
    var self = this;

    var remover = function() {
      self.off(event, callback);
      self.off(event, remover);
    };

    this.on(event, callback);
    this.on(event, remover);

    return this;
  };

  /**
   * Removes an eventListener
   *
   * Note: chainable
   *
   * @public
   * @returns IAS
   */
  IAS.prototype.off = function(event, callback) {
    if (typeof this.listeners[event] == 'undefined') {
      throw new Error('There is no event called "' + event + '"');
    }

    this.listeners[event].remove(callback);

    return this;
  };

  /**
   * Load the next page
   *
   * @public
   */
  IAS.prototype.next = function() {
    var url = this.nextUrl,
        self = this;

    this.unbind();

    if (!url) {
      this.fire('noneLeft', [this.getLastItem()]);
      this.listeners['noneLeft'].disable(); // disable it so it only fires once

      self.bind();

      return false;
    }

    var promise = this.fire('next', [url]);

    promise.done(function() {
      self.load(url, function(data, items) {
        self.render(items, function() {
          self.nextUrl = self.getNextUrl(data);

          self.bind();
        });
      });
    });

    promise.fail(function() {
      self.bind();
    });

    return true;
  };

  /**
   * Adds an extension
   *
   * @public
   */
  IAS.prototype.extension = function(extension) {
    if (typeof extension['bind'] == 'undefined') {
      throw new Error('Extension doesn\'t have required method "bind"');
    }

    if (typeof extension['initialize'] != 'undefined') {
      extension.initialize(this);
    }

    this.extensions.push(extension);

    return this;
  };

  /**
   * Shortcut. Sets the window as scroll container.
   *
   * @public
   * @param option
   * @returns {*}
   */
  $.ias = function(option) {
    var $window = $(window);

    return $window.ias.apply($window, arguments);
  };

  /**
   * jQuery plugin initialization
   *
   * @public
   * @param option
   * @returns {*} the last IAS instance will be returned
   */
  $.fn.ias = function(option) {
    var args = Array.prototype.slice.call(arguments);
    var retval = this;

    this.each(function() {
      var $this = $(this),
          instance = $this.data('ias'),
          options = $.extend({}, $.fn.ias.defaults, $this.data(), typeof option == 'object' && option)
          ;

      // set a new instance as data
      if (!instance) {
        $this.data('ias', (instance = new IAS($this, options)));

        $(document).ready($.proxy(instance.initialize, instance));
      }

      // when the plugin is called with a method
      if (typeof option === 'string') {
        if (typeof instance[option] !== 'function') {
          throw new Error('There is no method called "' + option + '"');
        }

        args.shift(); // remove first argument ('option')
        instance[option].apply(instance, args);
      }

      retval = instance;
    });

    return retval;
  };

  /**
   * Plugin defaults
   *
   * @public
   * @type {object}
   */
  $.fn.ias.defaults = {
    item: '.item',
    container: '.listing',
    next: '.next',
    pagination: false,
    delay: 600,
    negativeMargin: 10
  };
})(jQuery);
<|endoftext|>


<commit_before>/* globals require, module */
import jQueryDeferred from 'jquery-deferred';
import jQueryParam from 'jquery-param';

function jQueryFunction(aSubject) {
    const events = aSubject.events || {};

    if (aSubject && aSubject === aSubject.window) {
        return {
            0: aSubject,
            load: handler => aSubject.addEventListener('load', handler, false),
            bind: (event, handler) => aSubject.addEventListener(event, handler, false),
            unbind: (event, handler) => aSubject.removeEventListener(event, handler, false),
        };
    }

    return {
        0: aSubject,
        unbind(aEvent, aHandler) {
            let handlers = events[aEvent] || [];

            if (aHandler) {
                const index = handlers.indexOf(aHandler);
                if (index !== -1) {
                    handlers.splice(index, 1);
                }
            } else {
                handlers = [];
            }

            events[aEvent] = handlers;
            aSubject.events = events;
        },
        bind(aEvent, aHandler) {
            const current = events[aEvent] || [];
            events[aEvent] = current.concat(aHandler);
            aSubject.events = events;
        },
        triggerHandler(aEvent, aArgs) {
            const handlers = events[aEvent] || [];

            handlers.forEach((aFunction) => {
                let args = aArgs || [];

                if (aArgs && aArgs[0] && aArgs[0].type === undefined) {
                    args = args.concat([{
                        type: aEvent,
                    }]);
                }

                aFunction.apply(this, args);
            });
        },
    };
}

function xhr() {
    try {
        return new window.XMLHttpRequest();
    } catch (e) {
        return null;
    }
}

function ajax(aOptions) {
    const request = xhr();

    request.onreadystatechange = () => {
        if (request.readyState !== 4) {
            return;
        }

        if (request.status === 200 && !request._hasError) {
            aOptions.success && aOptions.success(JSON.parse(request.responseText));
        }
        else {
            aOptions.error && aOptions.error(request);
        }
    };

    request.open(aOptions.type, aOptions.url);
    request.setRequestHeader('content-type', aOptions.contentType);
    request.send(aOptions.data.data && `data=${aOptions.data.data}`);

    return {
        abort(aReason) {
            request.abort(aReason);
        },
    };
}

function makeArray(aPotentialArray) {
    return Array.prototype.slice.call(aPotentialArray);
}

function isEmptyObject(aObject) {
    return !aObject || Object.keys(aObject).length === 0;
}

function trim(aString) {
    return typeof aString === 'string' && aString.trim();
}

function inArray(aArray, aItem) {
    return aArray.indexOf(aItem) !== -1;
}

const jQueryMethods = {
    defaultAjaxHeaders: null,
    ajax,
    inArray,
    trim,
    isEmptyObject,
    makeArray,
    param: jQueryParam,
    support: {
        cors: (() => {
            const xhrObj = xhr();
            return Boolean(xhrObj) && 'withCredentials' in xhrObj;
        })(),
    },
};

export const jQueryShim = jQueryDeferred.extend(
    jQueryFunction,
    jQueryMethods,
    jQueryDeferred
);
<commit_msg>Refactored jqueryshim<commit_after>import jQueryDeferred from 'jquery-deferred';
import jQueryParam from 'jquery-param';

/**
 * This function acts as jquery function, so it wrapps given items
 * @param {any} aItem The item that should be wrapped by jquery proxy
 * @returns {Object} The wrapped item
 */
function jQueryFunction(aItem) {
    const events = aItem.events || {};

    // if the passed thing is the window element, we return a window specific object
    if (aItem === window) {
        return {
            0: aItem,
            load: (handler) => aItem.addEventListener('load', handler),
            bind: (event, handler) => aItem.addEventListener(event, handler),
            unbind: (event, handler) => aItem.removeEventListener(event, handler)
        };
    }
    // else we return a default jquery like wrapped item
    return {
        // by default at 0 we have the item value
        0: aItem,
        // allow binding events
        bind(aEvent, aHandler) {
            if (!Array.isArray(events[aEvent])) {
                events[aEvent] = [];
            }

            events[aEvent].push(aHandler);
            aItem.events = events;
        },
        // allow unbinding events
        unbind(aEvent, aHandler) {
            if (Array.isArray(events[aEvent]) && !!aHandler) {
                const index = events[aEvent].indexOf(aHandler);

                if (index !== -1) {
                    events[aEvent].splice(index, 1);
                }
            }

            aItem.events = events;
        },
        triggerHandler(aEvent, aArgs) {
            if (Array.isArray(events[aEvent])) {
                let args = [{type: aEvent}];

                if (Array.isArray(aArgs)) {
                    args = args.concat(aArgs)
                }
                else if (aArgs !== undefined) {
                    args.push(aArgs);
                }

                for (let i = 0, iLen = events[aEvent].length; i < iLen; i++) {
                    events[aEvent][i].apply(this, args);
                }
            }
        },
    };
}

/**
 * Simulates the jquery ajax function used by signalr
 * @param {Object} aOptions The call options for this request
 * @returns {{abort: (function(*=))}}
 */
function ajax(aOptions) {
    // first we generate our request
    const request = new window.XMLHttpRequest();

    // then we setup the ready state change handler, to listen for the time it's ready
    request.onreadystatechange = () => {
        // if the request is not ready yet, we're not doing anything
        if (request.readyState !== 4) {
            return;
        }

        if (request.status === 200) {
            // here we just pass the responseText back to signalR, because signalR has it's own response-parsing builtin
            typeof aOptions.success === 'function' && aOptions.success(request.responseText);
        }
        // else some worms ate up the server and it crys for help. We don't care, but indicate an error
        else {
            typeof aOptions.error === 'function' && aOptions.error(request);
        }
    };

    // if there are any xhrFields passed, we parse them as well and handle them just fine
    if (typeof aOptions.xhrFields === 'object' && aOptions.xhrFields !== null) {
        if (typeof aOptions.xhrFields.withCredentials === 'boolean') {
            request.withCredentials = aOptions.xhrFields.withCredentials;
        }

        if (typeof aOptions.xhrFields.onprogress === 'function') {
            request.addEventListener('progress', aOptions.xhrFields.onprogress);
        }
    }

    // if there is a timeout defined, we setup a timer aborting the request. Because the send is sync and the timeout
    // async, we can place this here
    if (typeof aOptions.timeout === 'number' && aOptions.timeout > 0) {
        window.setTimeout(() => request.abort(), aOptions.timeout);
    }

    // then we open the connection
    request.open(aOptions.type, aOptions.url);

    // and now we're trying to determine the data to send. By default we send null
    let dataToSend = null;

    // but if a contentType is given and we got an data object
    if (typeof aOptions.contentType === 'string' && typeof aOptions.data === 'object' && aOptions.data !== null) {
        // we can set the data to send. To determime the correct form of data, we examine the contentType.
        dataToSend = aOptions.contentType.indexOf('application/x-www-form-urlencoded') === 0 ?
            // jQueryParam returns always a string, and according to jQuery we have to replace %20 with +, see
            // https://github.com/jquery/jquery/issues/2658. The only other option is json, so we choose json as default
            jQueryParam(aOptions.data).replace(/%20/g, '+') : JSON.stringify(aOptions.data);

        // and then we set the content-type header, because we actually have an header value
        request.setRequestHeader('content-type', aOptions.contentType);
    }

    // then we send the data to the server
    request.send(dataToSend);

    // and we return an object with an abort method, because signalR might use it
    return {
        abort(aReason) {
            request.abort(aReason);
        }
    };
}

/**
 * Makes an array based on given potential array
 * @param {any} aPotentialArray The potential array to transform
 * @returns {Array} The resulting array
 */
function makeArray(aPotentialArray) {
    return Array.prototype.slice.call(aPotentialArray);
}

/**
 * Tests whether given object has no keys
 * @param {Object} aObject The object to test
 * @returns {boolean} The result
 */
function isEmptyObject(aObject) {
    return typeof aObject !== 'object' || aObject === null || Object.keys(aObject).length === 0;
}

/**
 * Trims given string
 * @param {String} aString The string to trim
 * @returns {string} The trimmed string
 */
function trim(aString) {
    return typeof aString === 'string' && aString.trim();
}

/**
 * Tests if given item is in given array
 * @param {Array} aArray The array potentially containing the item
 * @param {any} aItem The potential item
 * @returns {boolean} The result
 */
function inArray(aArray, aItem) {
    return Array.isArray(aArray) && aArray.indexOf(aItem) !== -1;
}

const jQueryMethods = {
    defaultAjaxHeaders: null,
    ajax,
    inArray,
    trim,
    isEmptyObject,
    makeArray,
    param: jQueryParam,
    support: {
        cors: (() => {
            const xhrObj = new window.XMLHttpRequest();

            return 'withCredentials' in xhrObj;
        })(),
    },
};

export const jQueryShim = jQueryDeferred.extend(
    jQueryFunction,
    jQueryMethods,
    jQueryDeferred
);
<|endoftext|>


<commit_before>var app = angular.module('GhFp', ['ngRoute']);

app.config(function ($routeProvider) {
   $routeProvider
      .when('/', {
    	  controller:  'main',
          templateUrl: 'src/html/views/main.html'
      })
      .when('/test', {
    	  controller:  function($scope, $location, $route) {
			    		    $scope.urlName = 'test';
			    			$scope.location_path = $location.path();
			    			$scope.location_url = $location.url();

			    		    $scope.getName = function() {
			    		   	 	return $scope.name;
			    			};
			    		    $scope.getNameWith = function(addThis) {
			    		   	 	return $scope.name + addThis;
			    			};
    	  				},
          templateUrl: 'src/html/views/test.html'
      })
      .otherwise({redirectTo: '/'});
});

app.controller('main', function($scope) {
     $scope.accounts = [
                        	{ name: "Linkedin", url: "http://pl.linkedin.com/pub/rados%C5%82aw-go%C5%82%C4%99biewski/70/832/35/"},
                        	{ name: "G+", url: "https://google.com/+RadoslawGolebiewski"},
                        	{ name: "local test", url: "#/test"}
                    	];
});

app.controller('TextAreaWithLimitCtrl', function($scope) {
	var MAX_LEN = 128;
	var WARN_THRESHOLD = 96;
	// TODO 3 steps colored info, by font or text background;
	// TODO clear, add cleaning
	// TODO then use clean just for tests
	// TODO cut more then valid
	$scope.remaining = function () {
		return MAX_LEN - $scope.message.length;
	};

	$scope.hasValidLength = function () {
		return $scope.remaining() > 0;
	};

	$scope.shouldWarn = function () {
		return $scope.remaining() < WARN_THRESHOLD;
	};
});
<commit_msg>Warning threshold, changed.<commit_after>var app = angular.module('GhFp', ['ngRoute']);

app.config(function ($routeProvider) {
   $routeProvider
      .when('/', {
    	  controller:  'main',
          templateUrl: 'src/html/views/main.html'
      })
      .when('/test', {
    	  controller:  function($scope, $location, $route) {
			    		    $scope.urlName = 'test';
			    			$scope.location_path = $location.path();
			    			$scope.location_url = $location.url();

			    		    $scope.getName = function() {
			    		   	 	return $scope.name;
			    			};
			    		    $scope.getNameWith = function(addThis) {
			    		   	 	return $scope.name + addThis;
			    			};
    	  				},
          templateUrl: 'src/html/views/test.html'
      })
      .otherwise({redirectTo: '/'});
});

app.controller('main', function($scope) {
     $scope.accounts = [
                        	{ name: "Linkedin", url: "http://pl.linkedin.com/pub/rados%C5%82aw-go%C5%82%C4%99biewski/70/832/35/"},
                        	{ name: "G+", url: "https://google.com/+RadoslawGolebiewski"},
                        	{ name: "local test", url: "#/test"}
                    	];
});

app.controller('TextAreaWithLimitCtrl', function($scope) {
	var MAX_LEN = 128;
	var WARN_THRESHOLD = 120;
	// TODO 3 steps colored info, by font or text background;
	// TODO clear, add cleaning
	// TODO then use clean just for tests
	// TODO cut more then valid
	$scope.remaining = function () {
		return MAX_LEN - $scope.message.length;
	};

	$scope.hasValidLength = function () {
		return $scope.remaining() > 0;
	};

	$scope.shouldWarn = function () {
		return $scope.remaining() < WARN_THRESHOLD;
	};
});
<|endoftext|>


<commit_before>import linvodb  from 'linvodb3';
import leveljs  from 'level-js';
import path     from 'path';
import teeny    from 'teeny-conf';
import Promise  from 'bluebird';

const remote = electron.remote;
const app    = remote.app;

/*
|--------------------------------------------------------------------------
| Some variables
|--------------------------------------------------------------------------
*/

const browserWindows = {};
browserWindows.main = remote.getCurrentWindow();

const pathUserData = app.getPath('userData');
const pathSrc      = __dirname;

/*
|--------------------------------------------------------------------------
| Config
|--------------------------------------------------------------------------
*/

const conf = new teeny(path.join(pathUserData, 'config.json'));
conf.loadOrCreateSync();

/*
|--------------------------------------------------------------------------
| supported Formats
|--------------------------------------------------------------------------
*/

const supportedExtensions = [
    '.mp3',
    '.mp4',
    '.aac',
    '.m4a',
    '.3gp',
    '.wav',
    '.ogg'
];

/*
|--------------------------------------------------------------------------
| Database
|--------------------------------------------------------------------------
*/

linvodb.defaults.store = { db: leveljs }; // Comment out to use LevelDB instead of level-js
// Set dbPath - this should be done explicitly and will be the dir where each model's store is saved
linvodb.dbPath = pathUserData;

const Track = new linvodb('track', {
    album: String,
    albumartist: [String],
    artist: [String],
    cover: {
        default: null
    },
    disk: {
        no: Number,
        of: Number
    },
    duration: Number,
    genre: [String],
    loweredMetas : {
        artist      : [String],
        album       : String,
        albumartist : [String],
        title       : String,
        genre       : [String]
    },
    path: String,
    playCount: Number,
    title: String,
    track: {
        no: Number,
        of: Number
    },
    year: String
});

Track.ensureIndex({ fieldName: 'path', unique: true });

const Playlist = new linvodb('playlist', {
    name: String,
    tracks: {
        type: [String],
        default: []
    }
});

const models = {
    Track,
    Playlist
};

Promise.promisifyAll(models.Track.find().__proto__);
Promise.promisifyAll(models.Track);
Promise.promisifyAll(models.Playlist);

/*
|--------------------------------------------------------------------------
| Export
|--------------------------------------------------------------------------
*/

export default {
    supportedExtensions, // supported audio formats
    pathSrc,             // path of the app
    browserWindows,      // Object containing all the windows
    models,              // database models
    version       : app.getVersion(), // Museeks version
    config        : conf,             // teeny-conf
    initialConfig : conf.getAll(),    // the config at the start of the application
};
<commit_msg>Add Opus extensions<commit_after>import linvodb  from 'linvodb3';
import leveljs  from 'level-js';
import path     from 'path';
import teeny    from 'teeny-conf';
import Promise  from 'bluebird';

const remote = electron.remote;
const app    = remote.app;

/*
|--------------------------------------------------------------------------
| Some variables
|--------------------------------------------------------------------------
*/

const browserWindows = {};
browserWindows.main = remote.getCurrentWindow();

const pathUserData = app.getPath('userData');
const pathSrc      = __dirname;

/*
|--------------------------------------------------------------------------
| Config
|--------------------------------------------------------------------------
*/

const conf = new teeny(path.join(pathUserData, 'config.json'));
conf.loadOrCreateSync();

/*
|--------------------------------------------------------------------------
| supported Formats
|--------------------------------------------------------------------------
*/

const supportedExtensions = [
    '.mp3',
    '.mp4',
    '.aac',
    '.m4a',
    '.3gp',
    '.wav',

    // Opus
    '.ogg',
    '.ogv',
    '.oga',
    '.ogx',
    '.ogm',
    '.spx',
    '.opus'
];

/*
|--------------------------------------------------------------------------
| Database
|--------------------------------------------------------------------------
*/

linvodb.defaults.store = { db: leveljs }; // Comment out to use LevelDB instead of level-js
// Set dbPath - this should be done explicitly and will be the dir where each model's store is saved
linvodb.dbPath = pathUserData;

const Track = new linvodb('track', {
    album: String,
    albumartist: [String],
    artist: [String],
    cover: {
        default: null
    },
    disk: {
        no: Number,
        of: Number
    },
    duration: Number,
    genre: [String],
    loweredMetas : {
        artist      : [String],
        album       : String,
        albumartist : [String],
        title       : String,
        genre       : [String]
    },
    path: String,
    playCount: Number,
    title: String,
    track: {
        no: Number,
        of: Number
    },
    year: String
});

Track.ensureIndex({ fieldName: 'path', unique: true });

const Playlist = new linvodb('playlist', {
    name: String,
    tracks: {
        type: [String],
        default: []
    }
});

const models = {
    Track,
    Playlist
};

Promise.promisifyAll(models.Track.find().__proto__);
Promise.promisifyAll(models.Track);
Promise.promisifyAll(models.Playlist);

/*
|--------------------------------------------------------------------------
| Export
|--------------------------------------------------------------------------
*/

export default {
    supportedExtensions, // supported audio formats
    pathSrc,             // path of the app
    browserWindows,      // Object containing all the windows
    models,              // database models
    version       : app.getVersion(), // Museeks version
    config        : conf,             // teeny-conf
    initialConfig : conf.getAll(),    // the config at the start of the application
};
<|endoftext|>


<commit_before>  $.extend(prototype, {

    // Show the viewer (only available in modal mode)
    show: function () {
      var options = this.options;
      var $viewer;

      if (options.inline || this.transitioning) {
        return;
      }

      if (!this.isBuilt) {
        this.build();
      }

      if ($.isFunction(options.show)) {
        this.$element.one(EVENT_SHOW, options.show);
      }

      if (this.trigger(EVENT_SHOW).isDefaultPrevented()) {
        return;
      }

      this.$body.addClass(CLASS_OPEN);
      $viewer = this.$viewer.removeClass(CLASS_HIDE);

      this.$element.one(EVENT_SHOWN, $.proxy(function () {
        this.view((this.target ? this.$images.index(this.target) : 0) || this.index);
        this.target = false;
      }, this));

      if (options.transition) {
        this.transitioning = true;

        /* jshint expr:true */
        $viewer.addClass(CLASS_TRANSITION).get(0).offsetWidth;
        $viewer.one(EVENT_TRANSITIONEND, $.proxy(this.shown, this)).addClass(CLASS_IN);
      } else {
        $viewer.addClass(CLASS_IN);
        this.shown();
      }
    },

    // Hide the viewer (only available in modal mode)
    hide: function () {
      var options = this.options;
      var $viewer = this.$viewer;

      if (options.inline || this.transitioning || !this.isShown) {
        return;
      }

      if ($.isFunction(options.hide)) {
        this.$element.one(EVENT_HIDE, options.hide);
      }

      if (this.trigger(EVENT_HIDE).isDefaultPrevented()) {
        return;
      }

      if (this.isViewed && options.transition) {
        this.transitioning = true;
        this.$image.one(EVENT_TRANSITIONEND, $.proxy(function () {
          $viewer.one(EVENT_TRANSITIONEND, $.proxy(this.hidden, this)).removeClass(CLASS_IN);
        }, this));
        this.zoomTo(0, false, true);
      } else {
        $viewer.removeClass(CLASS_IN);
        this.hidden();
      }
    },

    /**
     * View one of the images with image's index
     *
     * @param {Number} index
     */
    view: function (index) {
      var $title = this.$title;
      var $image;
      var $item;
      var $img;
      var url;
      var alt;

      index = Number(index) || 0;

      if (!this.isShown || this.isPlayed || index < 0 || index >= this.length ||
        this.isViewed && index === this.index) {
        return;
      }

      if (this.trigger(EVENT_VIEW).isDefaultPrevented()) {
        return;
      }

      $item = this.$items.eq(index);
      $img = $item.find(SELECTOR_IMG);
      url = $img.data('originalUrl');
      alt = $img.attr('alt');

      this.$image = $image = $('');
      this.$items.eq(this.index).removeClass(CLASS_ACTIVE);
      $item.addClass(CLASS_ACTIVE);

      this.isViewed = false;
      this.index = index;
      this.image = null;
      this.$canvas.html($image.addClass(CLASS_INVISIBLE));

      if ($image[0].complete) {
        this.load();
      } else {
        $image.one(EVENT_LOAD, $.proxy(this.load, this));

        // Make the image visible if it fails to load within 1s
        setTimeout(function () {
          $image.removeClass(CLASS_INVISIBLE);
        }, 1000);
      }

      $title.empty();

      // Center current item
      this.renderList();

      // Show title when viewed
      this.$element.one(EVENT_VIEWED, $.proxy(function () {
        var image = this.image;
        var width = image.naturalWidth;
        var height = image.naturalHeight;

        $title.html(alt + ' (' + width + ' × ' + height + ')');
      }, this));
    },

    // View the previous image
    prev: function () {
      this.view(max(this.index - 1, 0));
    },

    // View the next image
    next: function () {
      this.view(min(this.index + 1, this.length - 1));
    },

    /**
     * Move the image
     *
     * @param {Number} offsetX
     * @param {Number} offsetY (optional)
     */
    move: function (offsetX, offsetY) {
      var image = this.image;

      // If "offsetY" is not present, its default value is "offsetX"
      if (isUndefined(offsetY)) {
        offsetY = offsetX;
      }

      offsetX = num(offsetX);
      offsetY = num(offsetY);

      if (this.isShown && !this.isPlayed && this.options.movable) {
        image.left += isNumber(offsetX) ? offsetX : 0;
        image.top += isNumber(offsetY) ? offsetY : 0;
        this.renderImage();
      }
    },

    /**
     * Zoom the image
     *
     * @param {Number} ratio
     * @param {Boolean} hasTooltip (optional)
     */
    zoom: function (ratio, hasTooltip) {
      var options = this.options;
      var minZoomRatio = max(0.01, options.minZoomRatio);
      var maxZoomRatio = min(100, options.maxZoomRatio);
      var image = this.image;
      var width;
      var height;

      ratio = num(ratio);

      if (isNumber(ratio) && this.isShown && !this.isPlayed && options.zoomable) {
        if (ratio < 0) {
          ratio =  1 / (1 - ratio);
        } else {
          ratio = 1 + ratio;
        }

        width = image.width * ratio;
        height = image.height * ratio;
        ratio = width / image.naturalWidth;
        ratio = min(max(ratio, minZoomRatio), maxZoomRatio);

        if (ratio > 0.95 && ratio < 1.05) {
          ratio = 1;
          width = image.naturalWidth;
          height = image.naturalHeight;
        }

        image.left -= (width - image.width) / 2;
        image.top -= (height - image.height) / 2;
        image.width = width;
        image.height = height;
        image.ratio = ratio;
        this.renderImage();

        if (hasTooltip) {
          this.tooltip();
        }
      }
    },

    /**
     * Zoom the image to a special ratio
     *
     * @param {Number} ratio
     * @param {Boolean} hasTooltip (optional)
     * @param {Boolean} _zoomable (private)
     */
    zoomTo: function (ratio, hasTooltip, _zoomable) {
      var image = this.image;
      var width;
      var height;

      ratio = max(ratio, 0);

      if (isNumber(ratio) && this.isShown && !this.isPlayed && (_zoomable || this.options.zoomable)) {
        width = image.naturalWidth * ratio;
        height = image.naturalHeight * ratio;
        image.left -= (width - image.width) / 2;
        image.top -= (height - image.height) / 2;
        image.width = width;
        image.height = height;
        image.ratio = ratio;
        this.renderImage();

        if (hasTooltip) {
          this.tooltip();
        }
      }
    },

    /**
     * Rotate the image
     * https://developer.mozilla.org/en-US/docs/Web/CSS/transform-function#rotate()
     *
     * @param {Number} degrees
     */
    rotate: function (degrees) {
      var image = this.image;

      degrees = num(degrees);

      if (isNumber(degrees) && this.isShown && !this.isPlayed && this.options.rotatable) {
        image.rotate = ((image.rotate || 0) + degrees);
        this.renderImage();
      }
    },

    /**
     * Rotate the image to a special angle
     *
     * @param {Number} degrees
     */
    rotateTo: function (degrees) {
      var image = this.image;

      degrees = num(degrees);

      if (isNumber(degrees) && this.isShown && !this.isPlayed && this.options.rotatable) {
        image.rotate = degrees;
        this.renderImage();
      }
    },

    /**
     * Scale the image
     * https://developer.mozilla.org/en-US/docs/Web/CSS/transform-function#scale()
     *
     * @param {Number} scaleX
     * @param {Number} scaleY (optional)
     */
    scale: function (scaleX, scaleY) {
      var image = this.image;

      // If "scaleY" is not present, its default value is "scaleX"
      if (isUndefined(scaleY)) {
        scaleY = scaleX;
      }

      scaleX = num(scaleX);
      scaleY = num(scaleY);

      if (this.isShown && !this.isPlayed && this.options.scalable) {
        image.scaleX = isNumber(scaleX) ? scaleX : 1;
        image.scaleY = isNumber(scaleY) ? scaleY : 1;
        this.renderImage();
      }
    },

    /**
     * Scale the abscissa of the image
     *
     * @param {Number} scaleX
     */
    scaleX: function (scaleX) {
      this.scale(scaleX, this.image.scaleY);
    },

    /**
     * Scale the ordinate of the image
     *
     * @param {Number} scaleY
     */
    scaleY: function (scaleY) {
      this.scale(this.image.scaleX, scaleY);
    },

    // Play the images
    play: function () {
      var options = this.options;
      var $player = this.$player;
      var load = $.proxy(this.loadImage, this);
      var list = [];
      var total = 0;
      var index = 0;
      var playing;

      if (!this.isShown || this.isPlayed) {
        return;
      }

      if (options.fullscreen) {
        this.fullscreen();
      }

      this.isPlayed = true;
      $player.addClass(CLASS_SHOW);

      this.$items.each(function (i) {
        var $this = $(this);
        var $img = $this.find(SELECTOR_IMG);
        var $image = $('');

        total++;

        $image.addClass(CLASS_FADE).toggleClass(CLASS_TRANSITION, options.transition);

        if ($this.hasClass(CLASS_ACTIVE)) {
          $image.addClass(CLASS_IN);
          index = i;
        }

        list.push($image);
        $image.one(EVENT_LOAD, {
          filled: false
        }, load);
        $player.append($image);
      });

      if (isNumber(options.interval) && options.interval > 0) {
        playing = $.proxy(function () {
          this.playing = setTimeout(function () {
            list[index].removeClass(CLASS_IN);
            index++;
            index = index < total ? index : 0;
            list[index].addClass(CLASS_IN);

            playing();
          }, options.interval);
        }, this);

        if (total > 1) {
          playing();
        }
      }
    },

    // Stop play
    stop: function () {
      if (!this.isPlayed) {
        return;
      }

      this.isPlayed = false;
      clearTimeout(this.playing);
      this.$player.removeClass(CLASS_SHOW).empty();
    },

    // Enter modal mode (only available in inline mode)
    full: function () {
      var options = this.options;
      var $image = this.$image;
      var $list = this.$list;

      if (!this.isShown || this.isPlayed || this.isFulled || !options.inline) {
        return;
      }

      this.isFulled = true;
      this.$body.addClass(CLASS_OPEN);
      this.$button.addClass(CLASS_FULLSCREEN_EXIT);

      if (options.transition) {
        $image.removeClass(CLASS_TRANSITION);
        $list.removeClass(CLASS_TRANSITION);
      }

      this.$viewer.addClass(CLASS_FIXED).removeAttr('style').css('z-index', options.zIndex);
      this.initContainer();
      this.viewer = $.extend({}, this.container);
      this.renderList();
      this.initImage($.proxy(function () {
        this.renderImage(function () {
          if (options.transition) {
            setTimeout(function () {
              $image.addClass(CLASS_TRANSITION);
              $list.addClass(CLASS_TRANSITION);
            }, 0);
          }
        });
      }, this));
    },

    // Exit modal mode (only available in inline mode)
    exit: function () {
      var options = this.options;
      var $image = this.$image;
      var $list = this.$list;

      if (!this.isFulled) {
        return;
      }

      this.isFulled = false;
      this.$body.removeClass(CLASS_OPEN);
      this.$button.removeClass(CLASS_FULLSCREEN_EXIT);

      if (options.transition) {
        $image.removeClass(CLASS_TRANSITION);
        $list.removeClass(CLASS_TRANSITION);
      }

      this.$viewer.removeClass(CLASS_FIXED).css('z-index', options.zIndexInline);
      this.viewer = $.extend({}, this.parent);
      this.renderViewer();
      this.renderList();
      this.initImage($.proxy(function () {
        this.renderImage(function () {
          if (options.transition) {
            setTimeout(function () {
              $image.addClass(CLASS_TRANSITION);
              $list.addClass(CLASS_TRANSITION);
            }, 0);
          }
        });
      }, this));
    },

    // Show the current ratio of the image with percentage
    tooltip: function () {
      var options = this.options;
      var $tooltip = this.$tooltip;
      var image = this.image;
      var classes = [
            CLASS_SHOW,
            CLASS_FADE,
            CLASS_TRANSITION
          ].join(' ');

      if (!this.isShown || this.isPlayed || !options.tooltip) {
        return;
      }

      $tooltip.text(round(image.ratio * 100) + '%');

      if (!this.fading) {
        if (options.transition) {

          /* jshint expr:true */
          $tooltip.addClass(classes).get(0).offsetWidth;
          $tooltip.addClass(CLASS_IN);
        } else {
          $tooltip.addClass(CLASS_SHOW);
        }
      } else {
        clearTimeout(this.fading);
      }

      this.fading = setTimeout($.proxy(function () {
        if (options.transition) {
          $tooltip.one(EVENT_TRANSITIONEND, function () {
            $tooltip.removeClass(classes);
          }).removeClass(CLASS_IN);
        } else {
          $tooltip.removeClass(CLASS_SHOW);
        }

        this.fading = false;
      }, this), 1000);
    },

    // Toggle the image size between its natural size and initial size.
    toggle: function () {
      if (this.image.ratio === 1) {
        this.zoomTo(this.initialImage.ratio);
      } else {
        this.zoomTo(1);
      }
    },

    // Reset the image to its initial state.
    reset: function () {
      if (this.isShown && !this.isPlayed) {
        this.image = $.extend({}, this.initialImage);
        this.renderImage();
      }
    },

    // Destroy the viewer
    destroy: function () {
      var $this = this.$element;

      if (this.options.inline) {
        this.unbind();
      } else {
        if (this.isShown) {
          this.unbind();
        }

        this.$images.removeClass(CLASS_TOGGLE);
        $this.off(EVENT_CLICK, this.start);
      }

      this.unbuild();
      $this.removeData(NAMESPACE);
    }
  });
<commit_msg>Added `moveTo` method<commit_after>  $.extend(prototype, {

    // Show the viewer (only available in modal mode)
    show: function () {
      var options = this.options;
      var $viewer;

      if (options.inline || this.transitioning) {
        return;
      }

      if (!this.isBuilt) {
        this.build();
      }

      if ($.isFunction(options.show)) {
        this.$element.one(EVENT_SHOW, options.show);
      }

      if (this.trigger(EVENT_SHOW).isDefaultPrevented()) {
        return;
      }

      this.$body.addClass(CLASS_OPEN);
      $viewer = this.$viewer.removeClass(CLASS_HIDE);

      this.$element.one(EVENT_SHOWN, $.proxy(function () {
        this.view((this.target ? this.$images.index(this.target) : 0) || this.index);
        this.target = false;
      }, this));

      if (options.transition) {
        this.transitioning = true;

        /* jshint expr:true */
        $viewer.addClass(CLASS_TRANSITION).get(0).offsetWidth;
        $viewer.one(EVENT_TRANSITIONEND, $.proxy(this.shown, this)).addClass(CLASS_IN);
      } else {
        $viewer.addClass(CLASS_IN);
        this.shown();
      }
    },

    // Hide the viewer (only available in modal mode)
    hide: function () {
      var options = this.options;
      var $viewer = this.$viewer;

      if (options.inline || this.transitioning || !this.isShown) {
        return;
      }

      if ($.isFunction(options.hide)) {
        this.$element.one(EVENT_HIDE, options.hide);
      }

      if (this.trigger(EVENT_HIDE).isDefaultPrevented()) {
        return;
      }

      if (this.isViewed && options.transition) {
        this.transitioning = true;
        this.$image.one(EVENT_TRANSITIONEND, $.proxy(function () {
          $viewer.one(EVENT_TRANSITIONEND, $.proxy(this.hidden, this)).removeClass(CLASS_IN);
        }, this));
        this.zoomTo(0, false, true);
      } else {
        $viewer.removeClass(CLASS_IN);
        this.hidden();
      }
    },

    /**
     * View one of the images with image's index
     *
     * @param {Number} index
     */
    view: function (index) {
      var $title = this.$title;
      var $image;
      var $item;
      var $img;
      var url;
      var alt;

      index = Number(index) || 0;

      if (!this.isShown || this.isPlayed || index < 0 || index >= this.length ||
        this.isViewed && index === this.index) {
        return;
      }

      if (this.trigger(EVENT_VIEW).isDefaultPrevented()) {
        return;
      }

      $item = this.$items.eq(index);
      $img = $item.find(SELECTOR_IMG);
      url = $img.data('originalUrl');
      alt = $img.attr('alt');

      this.$image = $image = $('');
      this.$items.eq(this.index).removeClass(CLASS_ACTIVE);
      $item.addClass(CLASS_ACTIVE);

      this.isViewed = false;
      this.index = index;
      this.image = null;
      this.$canvas.html($image.addClass(CLASS_INVISIBLE));

      if ($image[0].complete) {
        this.load();
      } else {
        $image.one(EVENT_LOAD, $.proxy(this.load, this));

        // Make the image visible if it fails to load within 1s
        setTimeout(function () {
          $image.removeClass(CLASS_INVISIBLE);
        }, 1000);
      }

      $title.empty();

      // Center current item
      this.renderList();

      // Show title when viewed
      this.$element.one(EVENT_VIEWED, $.proxy(function () {
        var image = this.image;
        var width = image.naturalWidth;
        var height = image.naturalHeight;

        $title.html(alt + ' (' + width + ' × ' + height + ')');
      }, this));
    },

    // View the previous image
    prev: function () {
      this.view(max(this.index - 1, 0));
    },

    // View the next image
    next: function () {
      this.view(min(this.index + 1, this.length - 1));
    },

    /**
     * Move the image with relative offsets
     *
     * @param {Number} offsetX
     * @param {Number} offsetY (optional)
     */
    move: function (offsetX, offsetY) {
      var image = this.image;

      this.moveTo(
        isUndefined(offsetX) ? offsetX : image.left + num(offsetX),
        isUndefined(offsetY) ? offsetY : image.top + num(offsetY)
      );
    },

    /**
     * Move the image to an absolute point
     *
     * @param {Number} x
     * @param {Number} y (optional)
     */
    moveTo: function (x, y) {
      var image = this.image;
      var changed = false;

      // If "y" is not present, its default value is "x"
      if (isUndefined(y)) {
        y = x;
      }

      x = num(x);
      y = num(y);

      if (this.isShown && !this.isPlayed && this.options.movable) {
        if (isNumber(x)) {
          image.left = x;
          changed = true;
        }

        if (isNumber(y)) {
          image.top = y;
          changed = true;
        }

        if (changed) {
          this.renderImage();
        }
      }
    },

    /**
     * Zoom the image with a relative ratio
     *
     * @param {Number} ratio
     * @param {Boolean} hasTooltip (optional)
     */
    zoom: function (ratio, hasTooltip) {
      var image = this.image;

      ratio = num(ratio);

      if (ratio < 0) {
        ratio =  1 / (1 - ratio);
      } else {
        ratio = 1 + ratio;
      }

      this.zoomTo(image.width * ratio / image.naturalWidth, hasTooltip);
    },

    /**
     * Zoom the image to an absolute ratio
     *
     * @param {Number} ratio
     * @param {Boolean} hasTooltip (optional)
     * @param {Boolean} _zoomable (private)
     */
    zoomTo: function (ratio, hasTooltip, _zoomable) {
      var options = this.options;
      var minZoomRatio = 0.01;
      var maxZoomRatio = 100;
      var image = this.image;
      var width = image.width;
      var height = image.height;
      var newWidth;
      var newHeight;

      ratio = max(0, ratio);

      if (isNumber(ratio) && this.isShown && !this.isPlayed && (_zoomable || options.zoomable)) {
        if (!_zoomable) {
          minZoomRatio = max(minZoomRatio, options.minZoomRatio);
          maxZoomRatio = min(maxZoomRatio, options.maxZoomRatio);
          ratio = min(max(ratio, minZoomRatio), maxZoomRatio);
        }

        if (ratio > 0.95 && ratio < 1.05) {
          ratio = 1;
        }

        newWidth = image.naturalWidth * ratio;
        newHeight = image.naturalHeight * ratio;
        image.left -= (newWidth - width) / 2;
        image.top -= (newHeight - height) / 2;
        image.width = newWidth;
        image.height = newHeight;
        image.ratio = ratio;
        this.renderImage();

        if (hasTooltip) {
          this.tooltip();
        }
      }
    },

    /**
     * Rotate the image with a relative degree
     *
     * @param {Number} degree
     */
    rotate: function (degree) {
      this.rotateTo((this.image.rotate || 0) + num(degree));
    },

    /**
     * Rotate the image to an absolute degree
     * https://developer.mozilla.org/en-US/docs/Web/CSS/transform-function#rotate()
     *
     * @param {Number} degree
     */
    rotateTo: function (degree) {
      var image = this.image;

      degree = num(degree);

      if (isNumber(degree) && this.isShown && !this.isPlayed && this.options.rotatable) {
        image.rotate = degree;
        this.renderImage();
      }
    },

    /**
     * Scale the image
     * https://developer.mozilla.org/en-US/docs/Web/CSS/transform-function#scale()
     *
     * @param {Number} scaleX
     * @param {Number} scaleY (optional)
     */
    scale: function (scaleX, scaleY) {
      var image = this.image;
      var changed = false;

      // If "scaleY" is not present, its default value is "scaleX"
      if (isUndefined(scaleY)) {
        scaleY = scaleX;
      }

      scaleX = num(scaleX);
      scaleY = num(scaleY);

      if (this.isShown && !this.isPlayed && this.options.scalable) {
        if (isNumber(scaleX)) {
          image.scaleX = scaleX;
          changed = true;
        }

        if (isNumber(scaleY)) {
          image.scaleY = scaleY;
          changed = true;
        }

        if (changed) {
          this.renderImage();
        }
      }
    },

    /**
     * Scale the abscissa of the image
     *
     * @param {Number} scaleX
     */
    scaleX: function (scaleX) {
      this.scale(scaleX, this.image.scaleY);
    },

    /**
     * Scale the ordinate of the image
     *
     * @param {Number} scaleY
     */
    scaleY: function (scaleY) {
      this.scale(this.image.scaleX, scaleY);
    },

    // Play the images
    play: function () {
      var options = this.options;
      var $player = this.$player;
      var load = $.proxy(this.loadImage, this);
      var list = [];
      var total = 0;
      var index = 0;
      var playing;

      if (!this.isShown || this.isPlayed) {
        return;
      }

      if (options.fullscreen) {
        this.fullscreen();
      }

      this.isPlayed = true;
      $player.addClass(CLASS_SHOW);

      this.$items.each(function (i) {
        var $this = $(this);
        var $img = $this.find(SELECTOR_IMG);
        var $image = $('');

        total++;

        $image.addClass(CLASS_FADE).toggleClass(CLASS_TRANSITION, options.transition);

        if ($this.hasClass(CLASS_ACTIVE)) {
          $image.addClass(CLASS_IN);
          index = i;
        }

        list.push($image);
        $image.one(EVENT_LOAD, {
          filled: false
        }, load);
        $player.append($image);
      });

      if (isNumber(options.interval) && options.interval > 0) {
        playing = $.proxy(function () {
          this.playing = setTimeout(function () {
            list[index].removeClass(CLASS_IN);
            index++;
            index = index < total ? index : 0;
            list[index].addClass(CLASS_IN);

            playing();
          }, options.interval);
        }, this);

        if (total > 1) {
          playing();
        }
      }
    },

    // Stop play
    stop: function () {
      if (!this.isPlayed) {
        return;
      }

      this.isPlayed = false;
      clearTimeout(this.playing);
      this.$player.removeClass(CLASS_SHOW).empty();
    },

    // Enter modal mode (only available in inline mode)
    full: function () {
      var options = this.options;
      var $image = this.$image;
      var $list = this.$list;

      if (!this.isShown || this.isPlayed || this.isFulled || !options.inline) {
        return;
      }

      this.isFulled = true;
      this.$body.addClass(CLASS_OPEN);
      this.$button.addClass(CLASS_FULLSCREEN_EXIT);

      if (options.transition) {
        $image.removeClass(CLASS_TRANSITION);
        $list.removeClass(CLASS_TRANSITION);
      }

      this.$viewer.addClass(CLASS_FIXED).removeAttr('style').css('z-index', options.zIndex);
      this.initContainer();
      this.viewer = $.extend({}, this.container);
      this.renderList();
      this.initImage($.proxy(function () {
        this.renderImage(function () {
          if (options.transition) {
            setTimeout(function () {
              $image.addClass(CLASS_TRANSITION);
              $list.addClass(CLASS_TRANSITION);
            }, 0);
          }
        });
      }, this));
    },

    // Exit modal mode (only available in inline mode)
    exit: function () {
      var options = this.options;
      var $image = this.$image;
      var $list = this.$list;

      if (!this.isFulled) {
        return;
      }

      this.isFulled = false;
      this.$body.removeClass(CLASS_OPEN);
      this.$button.removeClass(CLASS_FULLSCREEN_EXIT);

      if (options.transition) {
        $image.removeClass(CLASS_TRANSITION);
        $list.removeClass(CLASS_TRANSITION);
      }

      this.$viewer.removeClass(CLASS_FIXED).css('z-index', options.zIndexInline);
      this.viewer = $.extend({}, this.parent);
      this.renderViewer();
      this.renderList();
      this.initImage($.proxy(function () {
        this.renderImage(function () {
          if (options.transition) {
            setTimeout(function () {
              $image.addClass(CLASS_TRANSITION);
              $list.addClass(CLASS_TRANSITION);
            }, 0);
          }
        });
      }, this));
    },

    // Show the current ratio of the image with percentage
    tooltip: function () {
      var options = this.options;
      var $tooltip = this.$tooltip;
      var image = this.image;
      var classes = [
            CLASS_SHOW,
            CLASS_FADE,
            CLASS_TRANSITION
          ].join(' ');

      if (!this.isShown || this.isPlayed || !options.tooltip) {
        return;
      }

      $tooltip.text(round(image.ratio * 100) + '%');

      if (!this.fading) {
        if (options.transition) {

          /* jshint expr:true */
          $tooltip.addClass(classes).get(0).offsetWidth;
          $tooltip.addClass(CLASS_IN);
        } else {
          $tooltip.addClass(CLASS_SHOW);
        }
      } else {
        clearTimeout(this.fading);
      }

      this.fading = setTimeout($.proxy(function () {
        if (options.transition) {
          $tooltip.one(EVENT_TRANSITIONEND, function () {
            $tooltip.removeClass(classes);
          }).removeClass(CLASS_IN);
        } else {
          $tooltip.removeClass(CLASS_SHOW);
        }

        this.fading = false;
      }, this), 1000);
    },

    // Toggle the image size between its natural size and initial size.
    toggle: function () {
      if (this.image.ratio === 1) {
        this.zoomTo(this.initialImage.ratio);
      } else {
        this.zoomTo(1);
      }
    },

    // Reset the image to its initial state.
    reset: function () {
      if (this.isShown && !this.isPlayed) {
        this.image = $.extend({}, this.initialImage);
        this.renderImage();
      }
    },

    // Destroy the viewer
    destroy: function () {
      var $this = this.$element;

      if (this.options.inline) {
        this.unbind();
      } else {
        if (this.isShown) {
          this.unbind();
        }

        this.$images.removeClass(CLASS_TOGGLE);
        $this.off(EVENT_CLICK, this.start);
      }

      this.unbuild();
      $this.removeData(NAMESPACE);
    }
  });
<|endoftext|>


<commit_before>/**
 * --------------------------------------------------------------------------
 * Licensed under MIT https://github.com/sitevision/envision/blob/master/LICENSE
 * --------------------------------------------------------------------------
 */

import Popper from 'popper.js';

const Popover = (($) => {

   const NAME = 'envPopover';
   const IDENTIFIER = 'env.popover';
   const EVENT_NAMESPACE = `.${IDENTIFIER}`;
   const NO_CONFLICT = $.fn[NAME];

   const attachmentMapping = {
      top: 'top',
      bottom: 'bottom',
      left: 'left',
      right: 'right'
   };

   const DEFAULTS = {
      clickOutside: false,
      constraints: [],
      container: 'body',
      content: '',
      escapeContent: true,
      placement: 'top',
      template: `

`,
      title: '',
      trigger: 'click'
   };

   class Popover {

      constructor(element, config) {
         this.el = element;
         this.$el = $(this.el);
         this.config = $.extend({}, DEFAULTS, this.$el.data(), config);

         this.bindEvents();
      }

      bindEvents() {
         const triggers = this.config.trigger.split(' ');

         triggers.forEach((trigger) => {
            if (trigger === 'click') {
               this.$el.on(this.config.trigger + EVENT_NAMESPACE, () => {
                  if (this.isShowing) {
                     this.hide();
                  } else {
                     this.show();
                  }
               });
            } else {
               const hoverTrigger = trigger === 'hover';
               const inEvent = hoverTrigger ? 'mouseenter' : 'focusin';
               const outEvent = hoverTrigger ? 'mouseleave' : 'focusout';

               this.$el
                  .on(inEvent + EVENT_NAMESPACE, () => {
                     if (!this.isShowing) {
                        this.show();
                     }
                  })
                  .on(outEvent + EVENT_NAMESPACE, () => {
                     if (this.isShowing) {
                        this.hide();
                     }
                  });
            }
         });
      }

      getPopoverElement() {
         return this.$popoverElement = this.$popoverElement || $(this.config.template);
      }

      setText($popoverElement, selector, text) {
         $popoverElement.find(selector)[this.config.escapeContent ? 'text' : 'html'](text);
      }

      setTitle($popoverElement) {
         this.setText($popoverElement, '.env-popover__header__title', this.config.title);
      }

      setContent($popoverElement) {
         this.setText($popoverElement, '.env-popover__content', this.config.content);
      }

      setArrowPosition($popoverElement) {
         $popoverElement.find('.env-popover__arrow')
            .addClass(`env-popover__arrow--${this.config.placement}`);
      }

      hide() {
         const $popoverElement = this.getPopoverElement();

         $popoverElement.detach();

         if (this.config.clickOutside) {
            $('body').off(this.config.trigger + EVENT_NAMESPACE);
         }

         this.isShowing = false;
      }

      _cleanTipClass() {
         this.$popoverElement
            .find('.env-popover__arrow')
            .removeClass((index, oldClassNames) => {
               const modifier = oldClassNames.replace(/env-popover__arrow/g, '').trim();

               return `env-popover__arrow${modifier}`;
            });
      }

      _handlePopperPlacementChange(data) {
         this._cleanTipClass();
         this._addAttachmentClass(attachmentMapping[data.placement]);
      }

      _addAttachmentClass(className) {
         this.$popoverElement
            .find('.env-popover__arrow')
            .addClass(`env-popover__arrow--${className}`);
      }

      show() {
         const $popoverElement = this.getPopoverElement();

         this.setTitle($popoverElement);
         this.setContent($popoverElement);
         this.setArrowPosition($popoverElement);

         $('body').append($popoverElement);

         this._popper = new Popper(this.el, $popoverElement[0], {
            placement         : attachmentMapping[this.config.placement],
            modifiers         : {
               flip: {
                  behavior: 'flip'
               },
               arrow: {
                  element: '.env-popover__arrow'
               }
            },
            onCreate: (data) => {
               if (data.originalPlacement !== data.placement) {
                  this._handlePopperPlacementChange(data);
               }
            },
            onUpdate : (data) => {
               this._handlePopperPlacementChange(data);
            }
         });

         this._popper.update();

         if (this.config.clickOutside) {
            $('body').on(this.config.trigger + EVENT_NAMESPACE, this.clickOutsideHandler.bind(this));
         }

         this.isShowing = true;
      }

      destroy() {
         this.hide();

         if (this._popper !== null) {
            this._popper.destroy();
         }

         this.$popoverElement = undefined;
         this._popper = undefined;
         this.isShowing = false;
         this.$el.removeData(IDENTIFIER);
      }

      clickOutsideHandler(e) {
         const $target = $(e.target);

         if (this.isShowing && !$target.is(this.$el) && !$target.closest(this.$popoverElement).length) {
            this.hide();
         }
      }

      static _jQuery(config) {
         return this.each(function() {
            const $this = $(this);
            let data = $this.data(IDENTIFIER);

            if (!data && /destroy|hide/.test(config)) {
               return;
            }

            if (!data) {
               data = new Popover(this, typeof config === 'object' ? config : null);
               $this.data(IDENTIFIER, data);
            }

            if (typeof config === 'string') {
               const method = data[config];

               if (!method) {
                  throw new Error(`Invalid method name "${config}"`);
               }

               method.call(data);
            }
         });
      }
   }

   $.fn[NAME] = Popover._jQuery;
   $.fn[NAME].Constructor = Popover;
   $.fn[NAME].noConflict = () => {
      $.fn[NAME] = NO_CONFLICT;
      return Popover._jQuery;
   };

   return Popover;

})(jQuery);

export default Popover;
<commit_msg>Code style..<commit_after>/**
 * --------------------------------------------------------------------------
 * Licensed under MIT https://github.com/sitevision/envision/blob/master/LICENSE
 * --------------------------------------------------------------------------
 */

import Popper from 'popper.js';

const Popover = (($) => {

   const NAME = 'envPopover';
   const IDENTIFIER = 'env.popover';
   const EVENT_NAMESPACE = `.${IDENTIFIER}`;
   const NO_CONFLICT = $.fn[NAME];

   const attachmentMapping = {
      top: 'top',
      bottom: 'bottom',
      left: 'left',
      right: 'right'
   };

   const DEFAULTS = {
      clickOutside: false,
      constraints: [],
      container: 'body',
      content: '',
      escapeContent: true,
      placement: 'top',
      template: `

`,
      title: '',
      trigger: 'click'
   };

   class Popover {

      constructor(element, config) {
         this.el = element;
         this.$el = $(this.el);
         this.config = $.extend({}, DEFAULTS, this.$el.data(), config);

         this.bindEvents();
      }

      bindEvents() {
         const triggers = this.config.trigger.split(' ');

         triggers.forEach((trigger) => {
            if (trigger === 'click') {
               this.$el.on(this.config.trigger + EVENT_NAMESPACE, () => {
                  if (this.isShowing) {
                     this.hide();
                  } else {
                     this.show();
                  }
               });
            } else {
               const hoverTrigger = trigger === 'hover';
               const inEvent = hoverTrigger ? 'mouseenter' : 'focusin';
               const outEvent = hoverTrigger ? 'mouseleave' : 'focusout';

               this.$el
                  .on(inEvent + EVENT_NAMESPACE, () => {
                     if (!this.isShowing) {
                        this.show();
                     }
                  })
                  .on(outEvent + EVENT_NAMESPACE, () => {
                     if (this.isShowing) {
                        this.hide();
                     }
                  });
            }
         });
      }

      getPopoverElement() {
         return this.$popoverElement = this.$popoverElement || $(this.config.template);
      }

      setText($popoverElement, selector, text) {
         $popoverElement.find(selector)[this.config.escapeContent ? 'text' : 'html'](text);
      }

      setTitle($popoverElement) {
         this.setText($popoverElement, '.env-popover__header__title', this.config.title);
      }

      setContent($popoverElement) {
         this.setText($popoverElement, '.env-popover__content', this.config.content);
      }

      setArrowPosition($popoverElement) {
         $popoverElement.find('.env-popover__arrow')
            .addClass(`env-popover__arrow--${this.config.placement}`);
      }

      hide() {
         const $popoverElement = this.getPopoverElement();

         $popoverElement.detach();

         if (this.config.clickOutside) {
            $('body').off(this.config.trigger + EVENT_NAMESPACE);
         }

         this.isShowing = false;
      }

      _cleanTipClass() {
         this.$popoverElement
            .find('.env-popover__arrow')
            .removeClass((index, oldClassNames) => {
               const modifier = oldClassNames.replace(/env-popover__arrow/g, '').trim();

               return `env-popover__arrow${modifier}`;
            });
      }

      _handlePopperPlacementChange(data) {
         this._cleanTipClass();
         this._addAttachmentClass(attachmentMapping[data.placement]);
      }

      _addAttachmentClass(className) {
         this.$popoverElement
            .find('.env-popover__arrow')
            .addClass(`env-popover__arrow--${className}`);
      }

      show() {
         const $popoverElement = this.getPopoverElement();

         this.setTitle($popoverElement);
         this.setContent($popoverElement);
         this.setArrowPosition($popoverElement);

         $('body').append($popoverElement);

         this._popper = new Popper(this.el, $popoverElement[0], {
            placement         : attachmentMapping[this.config.placement],
            modifiers         : {
               flip: {
                  behavior: 'flip'
               },
               arrow: {
                  element: '.env-popover__arrow'
               }
            },
            onCreate: (data) => {
               if (data.originalPlacement !== data.placement) {
                  this._handlePopperPlacementChange(data);
               }
            },
            onUpdate: (data) => {
               this._handlePopperPlacementChange(data);
            }
         });

         this._popper.update();

         if (this.config.clickOutside) {
            $('body').on(this.config.trigger + EVENT_NAMESPACE, this.clickOutsideHandler.bind(this));
         }

         this.isShowing = true;
      }

      destroy() {
         this.hide();

         if (this._popper !== null) {
            this._popper.destroy();
         }

         this.$popoverElement = undefined;
         this._popper = undefined;
         this.isShowing = false;
         this.$el.removeData(IDENTIFIER);
      }

      clickOutsideHandler(e) {
         const $target = $(e.target);

         if (this.isShowing && !$target.is(this.$el) && !$target.closest(this.$popoverElement).length) {
            this.hide();
         }
      }

      static _jQuery(config) {
         return this.each(function() {
            const $this = $(this);
            let data = $this.data(IDENTIFIER);

            if (!data && /destroy|hide/.test(config)) {
               return;
            }

            if (!data) {
               data = new Popover(this, typeof config === 'object' ? config : null);
               $this.data(IDENTIFIER, data);
            }

            if (typeof config === 'string') {
               const method = data[config];

               if (!method) {
                  throw new Error(`Invalid method name "${config}"`);
               }

               method.call(data);
            }
         });
      }
   }

   $.fn[NAME] = Popover._jQuery;
   $.fn[NAME].Constructor = Popover;
   $.fn[NAME].noConflict = () => {
      $.fn[NAME] = NO_CONFLICT;
      return Popover._jQuery;
   };

   return Popover;

})(jQuery);

export default Popover;
<|endoftext|>


<commit_before>function userRss () {
  if (window.location.hash == "#home") {
    var link,button;
    link = document.getElementById('rss_link');
    button = document.getElementById('sendRssLink');

    console.log(button);

    button.addEventListener("click",function(){
        console.log("wesh");
        getXML(link.value).then(function(response){
          var tabItem;
          var tableauHTML = document.getElementById("tableau");
          tabItem = response.getElementsByTagName("item");
          for (var i = 0; i < tabItem.length; i++) {
            var tbody,tr,td,td_2,td_3,audio;

            //console.log(tabItem[i].childNodes[1].innerHTML);
            //console.log(tabItem[i].childNodes[11].getAttribute('url'));

            audio = document.createElement('AUDIO');
            audio.src = tabItem[i].getElementsByTagName('enclosure')[0].getAttribute('url');
            console.log(tabItem[i].getElementsByTagName('enclosure')[0].getAttribute('type'));
            audio.controls="controls";
            audio.preload = "false";
            tr = document.createElement('tr');
            td = document.createElement('td');
            td_2 = document.createElement('td');
            td_3 = document.createElement('td');
            tbody = document.createElement('tbody');
            td_2.innerHTML = i;
            td.innerHTML = tabItem[i].getElementsByTagName('title')[0].innerHTML;
            td_3.appendChild(audio);
            tr.appendChild(td_2);
            tr.appendChild(td);
            tr.appendChild(td_3);
            console.log(audio);

            tbody.appendChild(tr);
            tableauHTML.appendChild(tbody);
          }
          console.log(tabItem);
        },function(Error){

        });
    });
  }
}
<commit_msg>gestion des contenus<commit_after>function userRss () {
  if (window.location.hash == "#home") {
    var link,button;
    link = document.getElementById('rss_link');
    button = document.getElementById('sendRssLink');

    console.log(button);

    button.addEventListener("click",function(){
        console.log("wesh");
        getXML(link.value).then(function(response){
          var tabItem;
          var tableauHTML = document.getElementById("tableau");
          tabItem = response.getElementsByTagName("item");
          for (var i = 0; i < tabItem.length; i++) {
            var tbody,tr,td,td_2,td_3,media,type;

            //console.log(tabItem[i].childNodes[1].innerHTML);
            //console.log(tabItem[i].childNodes[11].getAttribute('url'));
            console.log(tabItem[i].getElementsByTagName('enclosure')[0].getAttribute('type'));
            type = tabItem[i].getElementsByTagName('enclosure')[0].getAttribute('type');
            if (type == "audio/mpeg") {
              console.log("met de l'audio gros");
              media = document.createElement('AUDIO');
              media.controls="controls";
              media.preload = "false";
            } else if (type == "image/jpg" || type == "image/jpeg"){
              media = document.createElement('img');
              media.width = 100;
              media.height = 100;

            }
            media.src = tabItem[i].getElementsByTagName('enclosure')[0].getAttribute('url');
            tr = document.createElement('tr');
            td = document.createElement('td');
            td_2 = document.createElement('td');
            td_3 = document.createElement('td');
            tbody = document.createElement('tbody');
            td_2.innerHTML = i;
            td.innerHTML = tabItem[i].getElementsByTagName('title')[0].innerHTML;
            td_3.appendChild(media);
            tr.appendChild(td_2);
            tr.appendChild(td);
            tr.appendChild(td_3);
            console.log(media);

            tbody.appendChild(tr);
            tableauHTML.appendChild(tbody);
          }
          console.log(tabItem);
        },function(Error){

        });
    });
  }
}
<|endoftext|>