<commit_before>/* global angular, Evaporate */
;(function (angular, Evaporate) {
  'use strict';
  angular.module('angular-evaporate', [])

    // Evaporate

      .value('Evaporate', Evaporate)

    // AngularEvaporateException

      .factory('AngularEvaporateException', [
        function () {

          /**
           * Defines custom exception.
           * @class
           *
           * @param {String} code
           * @param {String} message
           * @returns {AngularEvaporateException}
           *
           * @property {String} code
           * @property {String} message
           */
          var AngularEvaporateException = function (code, message) {
            if (!(this instanceof AngularEvaporateException))
              return new AngularEvaporateException(code, message);

            this.code = code;
            this.message = message;
          };

          /**
           * Override default toString() fn for the nice console output.
           * @returns {String}
           */
          AngularEvaporateException.prototype.toString = function () {
            return ('AngularEvaporateException: [' + this.code + '] ' + this.message);
          };

          return AngularEvaporateException;
        }
      ])

    // AngularEvaporateUpload

      .factory('AngularEvaporateUpload', ['$timeout', 'AngularEvaporateException',
        function ($timeout, AngularEvaporateException) {

          /**
           * Defines an upload object for the AngularEvaporate uploads queue.
           * @class
           *
           * @param {AngularEvaporate} ae - instance of AngularEvaporate which creates an upload
           * @param {Object} config - config of the requested upload
           * @returns {AngularEvaporateUpload}
           *
           * @property {String} name
           * @property {String} contentType
           *
           * @property {Number} $id - returned by the Evaporate.add()
           * @property {String} $url - full url of the file when it will be uploaded
           *
           * @property {Function} $start
           * @property {Function} $pause
           * @property {Function} $resume
           * @property {Function} $cancel
           *
           * @property {Number} $started
           * @property {Number} $stopped - if either $complete, $cancelled of $error happened
           * @property {Number} $paused
           * @property {Number} $resumed
           * @property {Number} $pausing
           * @property {Number} $cancelled
           * @property {Number} $complete
           * @property {Number} $info
           * @property {Number} $warn
           * @property {Number} $error
           * @property {Number} $progress
           *
           * @property {String} $infoMsg
           * @property {String} $warnMsg
           * @property {String} $errorMsg
           *
           * @property {Number} $percent - current uploading progress
           * @property {Number} $seconds - estimated elapsed time
           */
          var AngularEvaporateUpload = function (ae, config) {
            if (!(this instanceof AngularEvaporateUpload))
              throw new AngularEvaporateException('INVALID_USAGE', 'constructor called without the new keyword');

            // merge with the default config
            angular.extend(this, ae.$config, config);

            // add computable parameters
            if (!this.name) this.name = ae.$namer(this);
            this.contentType = this.file.type || 'binary/octet-stream';
            this.$url = ae.$url + this.name;

            // proxy callbacks
            [
              'started',
              'paused',
              'resumed',
              'pausing',
              'cancelled',
              'complete',
              'info',
              'warn',
              'error',
              'progress'
            ].forEach(function (name) {

              // save initial if exists
              var fn = (typeof this[name] === 'function') ? this[name] : undefined;

              // proxy
              var that = this;
              this[name] = function () {
                var arg = arguments[0];
                var now = Date.now();

                // save timestamp
                that['$' + name] = now;

                // progress: save percent and estimated time in seconds
                if (name === 'progress') {
                  that.$percent = Math.round(arg * 10000) / 100;
                  if (that.$percent) {
                    that.$seconds = Math.round(
                      (100 - that.$percent) / that.$percent *
                      (now - that.$started) / 1000
                    );
                  }
                }

                // complete, cancelled, error: set stopped flag
                // info, warn, error: save message
                else if (name === 'complete' || name === 'cancelled') that.$stopped = now;
                else if (name === 'info' || name === 'warn') that['$' + name + 'Msg'] = arg;
                else if (name === 'error') {
                  that.$errorMsg = arg;
                  that.$stopped = now;
                }

                // paused, resumed, pausing: manage dependencies
                else if (name === 'paused') {
                  delete that.$pausing;
                  delete that.$resumed;
                } else if (name === 'resumed') {
                  delete that.$pausing;
                  delete that.$paused;
                } else if (name === 'pausing') {
                  delete that.$paused;
                  delete that.$resumed;
                }

                // run initial fn
                if (fn) fn.apply(that, arguments);

                // update ui
                if (ae.$apply) $timeout(ae.$apply);
              };
            }, this);
          };

          return AngularEvaporateUpload;
        }
      ])

    // AngularEvaporate

      .factory('AngularEvaporate', ['$timeout', 'Evaporate', 'AngularEvaporateUpload', 'AngularEvaporateException',
        function ($timeout, Evaporate, AngularEvaporateUpload, AngularEvaporateException) {

          // constructor

            /**
             * Extends Evaporate.
             * @class
             *
             * @params - same as for new Evaporate()
             * @returns {AngularEvaporate}
             *
             * @property {Array} $uploads - internal list of uploads
             * @property {Object} $config - default config to merge with for an Evaporate.add() fn
             * @property {String} $url - url of the bucket's root directory
             * @property {Function} $namer - custom upload naming function
             *
             * @property {Function} $apply - (optional) binded $scope.$apply() to update UI
             */
            var AngularEvaporate = function (config) {
              if (!(this instanceof AngularEvaporate))
                throw new AngularEvaporateException('INVALID_USAGE', 'constructor called without the new keyword');

              // init Evaporate
              Evaporate.apply(this, arguments);

              // init self
              this.$uploads = [];
              this.$config = {};
              this.$url = (config.aws_url || 'http://s3.amazonaws.com/') + config.bucket + '/';
            };

            // inherit Evaporate
            AngularEvaporate.prototype = Object.create(Evaporate.prototype);
            AngularEvaporate.prototype.constructor = AngularEvaporate;

            /**
             * Default upload naming function.
             * @params {AngularEvaporateUpload}
             * @returns {String} - name of the upload
             */
            AngularEvaporate.prototype.$namer = function (upload) {
              return upload.file.name;
            };

          // api

            /**
             * Create an upload from file and append it to the queue.
             * @params - same as for Evaporate.add()
             * @returns {AngularEvaporateUpload}
             */
            AngularEvaporate.prototype.$enqueue = function () {

              // create an upload
              var upload = new AngularEvaporateUpload(this, arguments[0]);

              //
              upload.$start = this.$start.bind.apply(this.$start, [this, upload].concat(Array.prototype.slice.call(arguments, 1)));
              upload.$dequeue = this.$dequeue.bind(this, upload);

              // append to the list
              this.$uploads.push(upload);

              // update scope
              if (this.$apply) $timeout(this.$apply);

              //
              return upload;
            };

            /**
             * Remove an upload from the queue.
             * @param {AngularEvaporateUpload}
             * @returns {Number} index
             */
            AngularEvaporate.prototype.$dequeue = function (upload) {

              // find it in the list
              var index = this.$uploads.findIndex(function (upload) { return (upload === this); }, upload);
              if (index !== -1) {

                // and remove
                this.$uploads.splice(index, 1);

                // update scope
                if (this.$apply) $timeout(this.$apply);
              }

              //
              return index;
            };

            /**
             * Enqueue and immediately start uploading.
             * @params - same as for Evaporate.add()
             * @returns - same as Evaporate.add()
             */
            AngularEvaporate.prototype.$add = function () {
              var upload = this.$enqueue.apply(this, arguments);
              return upload.$start();
            };

            /**
             * Start uploading an already queued upload.
             * @param {AngularEvaporateUpload}
             * @returns - same as Evaporate.add()
             */
            AngularEvaporate.prototype.$start = function (upload) {

              // run
              upload.$id = this.add.apply(this, arguments);

              // modify api
              delete upload.$start;
              upload.$pause = this.pause.bind(this, upload.$id);
              upload.$resume = this.resume.bind(this, upload.$id);
              upload.$cancel = this.cancel.bind(this, upload.$id);

              // update scope
              if (this.$apply) $timeout(this.$apply);

              //
              return upload.$id;
            };

          return AngularEvaporate;
        }
      ])

    // angular-evaporate

      .directive('angularEvaporate', [
        function () {
          return {
            restrict: 'A',
            scope: { ae: '=angularEvaporate' },
            link: function ($scope, $element) {
              $scope.$on('$destroy', function () { $element.unbind('change'); });
              $element.bind('change', function (event) {
                if ($scope.ae && $scope.ae.supported) {

                  // choose behaviour
                  var fn = $scope.ae.$slothmode ? '$enqueue' : '$add';

                  // process files
                  angular.forEach(event.target.files, function (file) { $scope.ae[fn]({ file: file }); });

                  // clear selection
                  if ($scope.ae.$rinserepeat) $element.val(null);
                }
              });
            }
          };
        }
      ]);

})(angular, Evaporate);
<commit_msg>lib: add api for all uploads at once<commit_after>/* global angular, Evaporate */
;(function (angular, Evaporate) {
  'use strict';
  angular.module('angular-evaporate', [])

    // Evaporate

      .value('Evaporate', Evaporate)

    // AngularEvaporateException

      .factory('AngularEvaporateException', [
        function () {

          /**
           * Defines custom exception.
           * @class
           *
           * @param {String} code
           * @param {String} message
           * @returns {AngularEvaporateException}
           *
           * @property {String} code
           * @property {String} message
           */
          var AngularEvaporateException = function (code, message) {
            if (!(this instanceof AngularEvaporateException))
              return new AngularEvaporateException(code, message);

            this.code = code;
            this.message = message;
          };

          /**
           * Override default toString() fn for the nice console output.
           * @returns {String}
           */
          AngularEvaporateException.prototype.toString = function () {
            return ('AngularEvaporateException: [' + this.code + '] ' + this.message);
          };

          return AngularEvaporateException;
        }
      ])

    // AngularEvaporateUpload

      .factory('AngularEvaporateUpload', ['$timeout', 'AngularEvaporateException',
        function ($timeout, AngularEvaporateException) {

          /**
           * Defines an upload object for the AngularEvaporate uploads queue.
           * @class
           *
           * @param {AngularEvaporate} ae - instance of AngularEvaporate which creates an upload
           * @param {Object} config - config of the requested upload
           * @returns {AngularEvaporateUpload}
           *
           * @property {String} name
           * @property {String} contentType
           *
           * @property {Number} $id - returned by the Evaporate.add()
           * @property {String} $url - full url of the file when it will be uploaded
           *
           * @property {Function} $start
           * @property {Function} $pause
           * @property {Function} $resume
           * @property {Function} $cancel
           *
           * @property {Number} $started
           * @property {Number} $stopped - if either $complete, $cancelled of $error happened
           * @property {Number} $paused
           * @property {Number} $resumed
           * @property {Number} $pausing
           * @property {Number} $cancelled
           * @property {Number} $complete
           * @property {Number} $info
           * @property {Number} $warn
           * @property {Number} $error
           * @property {Number} $progress
           *
           * @property {String} $infoMsg
           * @property {String} $warnMsg
           * @property {String} $errorMsg
           *
           * @property {Number} $percent - current uploading progress
           * @property {Number} $seconds - estimated elapsed time
           */
          var AngularEvaporateUpload = function (ae, config) {
            if (!(this instanceof AngularEvaporateUpload))
              throw new AngularEvaporateException('INVALID_USAGE', 'constructor called without the new keyword');

            // merge with the default config
            angular.extend(this, ae.$config, config);

            // add computable parameters
            if (!this.name) this.name = ae.$namer(this);
            this.contentType = this.file.type || 'binary/octet-stream';
            this.$url = ae.$url + this.name;

            // proxy callbacks
            [
              'started',
              'paused',
              'resumed',
              'pausing',
              'cancelled',
              'complete',
              'info',
              'warn',
              'error',
              'progress'
            ].forEach(function (name) {

              // save initial if exists
              var fn = (typeof this[name] === 'function') ? this[name] : undefined;

              // proxy
              var that = this;
              this[name] = function () {
                var arg = arguments[0];
                var now = Date.now();

                // save timestamp
                that['$' + name] = now;

                // progress: save percent and estimated time in seconds
                if (name === 'progress') {
                  that.$percent = Math.round(arg * 10000) / 100;
                  if (that.$percent) {
                    that.$seconds = Math.round(
                      (100 - that.$percent) / that.$percent *
                      (now - that.$started) / 1000
                    );
                  }
                }

                // complete, cancelled, error: set stopped flag
                // info, warn, error: save message
                else if (name === 'complete' || name === 'cancelled') that.$stopped = now;
                else if (name === 'info' || name === 'warn') that['$' + name + 'Msg'] = arg;
                else if (name === 'error') {
                  that.$errorMsg = arg;
                  that.$stopped = now;
                }

                // paused, resumed, pausing: manage dependencies
                else if (name === 'paused') {
                  delete that.$pausing;
                  delete that.$resumed;
                } else if (name === 'resumed') {
                  delete that.$pausing;
                  delete that.$paused;
                } else if (name === 'pausing') {
                  delete that.$paused;
                  delete that.$resumed;
                }

                // run initial fn
                if (fn) fn.apply(that, arguments);

                // update ui
                if (ae.$apply) $timeout(ae.$apply);
              };
            }, this);
          };

          return AngularEvaporateUpload;
        }
      ])

    // AngularEvaporate

      .factory('AngularEvaporate', ['$timeout', 'Evaporate', 'AngularEvaporateUpload', 'AngularEvaporateException',
        function ($timeout, Evaporate, AngularEvaporateUpload, AngularEvaporateException) {

          // constructor

            /**
             * Extends Evaporate.
             * @class
             *
             * @params - same as for new Evaporate()
             * @returns {AngularEvaporate}
             *
             * @property {Array} $uploads - internal list of uploads
             * @property {Object} $config - default config to merge with for an Evaporate.add() fn
             * @property {String} $url - url of the bucket's root directory
             * @property {Function} $namer - custom upload naming function
             *
             * @property {Function} $apply - (optional) binded $scope.$apply() to update UI
             */
            var AngularEvaporate = function (config) {
              if (!(this instanceof AngularEvaporate))
                throw new AngularEvaporateException('INVALID_USAGE', 'constructor called without the new keyword');

              // init Evaporate
              Evaporate.apply(this, arguments);

              // init self
              this.$uploads = [];
              this.$config = {};
              this.$url = (config.aws_url || 'http://s3.amazonaws.com/') + config.bucket + '/';
            };

            // inherit Evaporate
            AngularEvaporate.prototype = Object.create(Evaporate.prototype);
            AngularEvaporate.prototype.constructor = AngularEvaporate;

            /**
             * Default upload naming function.
             * @params {AngularEvaporateUpload}
             * @returns {String} - name of the upload
             */
            AngularEvaporate.prototype.$namer = function (upload) {
              return upload.file.name;
            };

          // api

            // individual upload

              /**
               * Create an upload from file and append it to the queue.
               * @params - same as for Evaporate.add()
               * @returns {AngularEvaporateUpload}
               */
              AngularEvaporate.prototype.$enqueue = function () {

                // create an upload
                var upload = new AngularEvaporateUpload(this, arguments[0]);

                //
                upload.$start = this.$start.bind.apply(this.$start, [this, upload].concat(Array.prototype.slice.call(arguments, 1)));
                upload.$dequeue = this.$dequeue.bind(this, upload);

                // append to the list
                this.$uploads.push(upload);

                // update scope
                if (this.$apply) $timeout(this.$apply);

                //
                return upload;
              };

              /**
               * Remove an upload from the queue.
               * @param {AngularEvaporateUpload}
               * @returns {Number} index
               */
              AngularEvaporate.prototype.$dequeue = function (upload) {

                // find it in the list
                var index = this.$uploads.findIndex(function (upload) { return (upload === this); }, upload);
                if (index !== -1) {

                  // and remove
                  this.$uploads.splice(index, 1);

                  // update scope
                  if (this.$apply) $timeout(this.$apply);
                }

                //
                return index;
              };

              /**
               * Start uploading an already queued upload.
               * @param {AngularEvaporateUpload}
               * @returns - same as Evaporate.add()
               */
              AngularEvaporate.prototype.$start = function (upload) {

                // run
                upload.$id = this.add.apply(this, arguments);

                // modify api
                delete upload.$start;
                upload.$pause = this.pause.bind(this, upload.$id);
                upload.$resume = this.resume.bind(this, upload.$id);
                upload.$cancel = this.cancel.bind(this, upload.$id);

                // update scope
                if (this.$apply) $timeout(this.$apply);

                //
                return upload.$id;
              };

              /**
               * Enqueue and immediately start uploading.
               * @params - same as for Evaporate.add()
               * @returns - same as Evaporate.add()
               */
              AngularEvaporate.prototype.$add = function () {
                var upload = this.$enqueue.apply(this, arguments);
                return upload.$start();
              };

            // all uploads

              /**
               * Start all uploads.
               */
              AngularEvaporate.prototype.$startAll = function () {
                this.$uploads.forEach(function (upload) {
                  if (upload.$start) upload.$start();
                });
              };

              /**
               * Pause all uploads.
               * @params - same as for Evaporate.pause(), but without an id
               */
              AngularEvaporate.prototype.$pauseAll = function () {
                return this.pause.apply(this, [undefined].concat(Array.prototype.slice.call(arguments)));
              };

              /**
               * Resume all uploads.
               * @params - same as for Evaporate.resume(), but without an id
               */
              AngularEvaporate.prototype.$resumeAll = function () {
                return this.resume.apply(this, [undefined].concat(Array.prototype.slice.call(arguments)));
              };

              /**
               * Cancel all uploads.
               */
              AngularEvaporate.prototype.$cancelAll = function () {
                this.$uploads.forEach(function (upload) {
                  if (upload.$cancel) upload.$cancel();
                });
              };

              /**
               * Dequeue all uploads.
               */
              AngularEvaporate.prototype.$dequeueAll = function () {

                // flush the queue
                this.$uploads = [];

                // update scope
                if (this.$apply) $timeout(this.$apply);
              };

          return AngularEvaporate;
        }
      ])

    // angular-evaporate

      .directive('angularEvaporate', [
        function () {
          return {
            restrict: 'A',
            scope: { ae: '=angularEvaporate' },
            link: function ($scope, $element) {
              $scope.$on('$destroy', function () { $element.unbind('change'); });
              $element.bind('change', function (event) {
                if ($scope.ae && $scope.ae.supported) {

                  // choose behaviour
                  var fn = $scope.ae.$slothmode ? '$enqueue' : '$add';

                  // process files
                  angular.forEach(event.target.files, function (file) { $scope.ae[fn]({ file: file }); });

                  // clear selection
                  if ($scope.ae.$rinserepeat) $element.val(null);
                }
              });
            }
          };
        }
      ]);

})(angular, Evaporate);
<|endoftext|>


<commit_before><commit_msg>Remove unused variable<commit_after><|endoftext|>


<commit_before>L.Icon.Pulse = L.DivIcon.extend({

    options: {
        className: '',
        iconSize: [12,12],
        color: 'red'
    },

    initialize: function (options) {
        L.setOptions(this,options);

        // creating unique class name
        var uniqueClassName = 'lpi-'+ new Date().getTime()+'-'+Math.round(Math.random()*100000);

        this.options.className = this.options.className+' leaflet-pulsing-icon '+uniqueClassName;

        // prepare styles
        var css = '.'+unique+'{background-color:'+this.options.color+';}'
            css += '.'+unique+':after{box-shadow: 0 0 6px 2px '+this.options.color+';}';

        // CREATE STYLE ELEMENT
        var styleEl=document.createElement('style');
        if (styleEl.styleSheet)
            styleEl.styleSheet.cssText=css;
        else
            styleEl.appendChild(document.createTextNode(css));

        // appending style element to document
        document.getElementsByTagName('head')[0].appendChild(styleEl);

        // initialize icon
        L.DivIcon.prototype.initialize.call(this,options);
    }
});

L.icon.pulse = function (options) {
    return new L.Icon.Pulse(options);
};<commit_msg>class bug<commit_after>L.Icon.Pulse = L.DivIcon.extend({

    options: {
        className: '',
        iconSize: [12,12],
        color: 'red'
    },

    initialize: function (options) {
        L.setOptions(this,options);

        // creating unique class name
        var uniqueClassName = 'lpi-'+ new Date().getTime()+'-'+Math.round(Math.random()*100000);

        this.options.className = this.options.className+' leaflet-pulsing-icon '+uniqueClassName;

        // prepare styles
        var css = '.'+uniqueClassName+'{background-color:'+this.options.color+';}';
            css += '.'+uniqueClassName+':after{box-shadow: 0 0 6px 2px '+this.options.color+';}';

        // CREATE STYLE ELEMENT
        var styleEl=document.createElement('style');
        if (styleEl.styleSheet)
            styleEl.styleSheet.cssText=css;
        else
            styleEl.appendChild(document.createTextNode(css));

        // appending style element to document
        document.getElementsByTagName('head')[0].appendChild(styleEl);

        // initialize icon
        L.DivIcon.prototype.initialize.call(this,options);
    }
});

L.icon.pulse = function (options) {
    return new L.Icon.Pulse(options);
};<|endoftext|>


<commit_before>"use strict";

describe('MainController', function() {
  beforeEach(module('arethusa'));

  it('sets scope values', inject(function($controller) {
    var scope = {};
    var mystate = { "mystate": "state"};
    var ctrl = $controller('MainController', {$scope:scope, state:mystate, configurator: {
      configurationFor : function(name) {
        return { plugins: { "plugin1" : {}, "plugin2" : {} }, template: "template"};
      }
    }});

    expect(scope.state).toBe(mystate);
    expect(scope.plugins).toEqual(['plugin1', 'plugin2']);
    expect(scope.template).toBe("template");
  }));
});
<commit_msg>Fix broken spec<commit_after>"use strict";

describe('MainController', function() {
  beforeEach(module('arethusa'));

  it('sets scope values', inject(function($controller) {
    var scope = {};
    var mystate = { "mystate": "state"};
    var ctrl = $controller('MainController', {$scope:scope, state:mystate, configurator: {
      configurationFor : function(name) {
        return { plugins: {}, template: "template"};
      }
    }});

    expect(scope.state).toBe(mystate);
    expect(scope.plugins).toEqual({});
    expect(scope.template).toBe("template");
  }));
});
<|endoftext|>


<commit_before>'use strict';

var Generator = require('source-map').SourceMapGenerator;

/**
 * Apply changes to the content.
 * @param {string} content The content to process
 * @param {{additions:Array, removals:Array}} pending A hash of additions and removals
 * @param {string} quoteChar String literal character to use
 * @returns {{content:string, generator:SourceMapGenerator}} Amended content and source-map generator
 */
function applyAlterations(sourcePath, content, pending, quoteChar) {

  // additions will be the '"ngInject";' directive
  var directive = quoteChar + 'ngInject' + quoteChar + ';';

  // create a list containing each line and any line terminator relating to it
  //  changes will mutate this list
  var lines = content
    .split(/(\r?\n)/)
    .reduce(mergeByTwos, []);

  // create a source-map generator to track changes in the source
  //  changes will mutate this instance
  var generator = new Generator();

  // if we make later changes first then the remaining locations will not be changed
  pending.additions.map(encode(addition))
    .concat(pending.removals.map(encode(removal)))
    .sort(startLocDescending)
    .forEach(executeItem);

  return {
    content  : lines.join(''),
    generator: generator
  };

  /**
   * Add "ngInject" directive syntax at the given location.
   * @param {{line:number, column:number}} start A start location from where we might insert
   * @param {{line:number, column:number}} end An end location to where we might insert
   */
  function addition(start, end) {

    // zero-based start and end indices
    var startZero = zeroBased(start),
        endZero   = zeroBased(end);

    // look at the first line and determine whether we should annotate in the first line
    var fnDeclaration       = lines[startZero.line].slice(0, startZero.column),
        blockOuterFirstLine = lines[startZero.line].slice(startZero.column),
        blockOuterPrefix    = blockOuterFirstLine.match(/^\s*\{/)[0],
        blockInnerFirstLine = blockOuterFirstLine.slice(blockOuterPrefix.length),
        isInline            = !(/^\s*$/.test(blockInnerFirstLine));

    // comment in the same line
    if (isInline) {
      lines[startZero.line] = fnDeclaration + blockOuterPrefix + directive + blockInnerFirstLine;
      generator.addMapping({
        source   : sourcePath,
        original : start,
        generated: oneBased({
          line  : startZero.line,
          column: startZero.column + directive.length
        })
      });
    }
    // comment on a new line
    else {
      var iLineFirst = startZero.line + 1,
          iLineLast  = endZero.line,
          text       = lines[iLineFirst],
          indent     = lines.slice(iLineFirst, iLineLast).reduce(reduceIndent, text),
          eol        = text.match(/\r?\n/)[0],
          insertion  = indent + directive + eol;

      // text change
      lines.splice(iLineFirst, 0, insertion);

      // source-map change
      generator.addMapping({
        source   : sourcePath,
        original : start,
        generated: oneBased({
          line  : iLineFirst,
          column: startZero.column + insertion.length
        })
      });
    }
  }

  /**
   * Remove @ngInject comment at the given location.
   * @param {{line:number, column:number}} start A start location from where we might remove
   * @param {{line:number, column:number}} end An end location to where we might remove
   */
  function removal(start, end) {

    // zero-based start and end indices
    var startZero = zeroBased(start),
        endZero   = zeroBased(end);

    // text change
    lines.slice(endZero.line, endZero.line + 1)
      .forEach(eachLine);

    // source-map change
    generator.addMapping({
      source   : sourcePath,
      original : end,
      generated: start
    });

    function eachLine(text, i, array) {
      var index = endZero.line + i;

      // first and last line in one
      if (array.length === 1) {
        lines[index] = text.slice(0, startZero.column) + text.slice(endZero.column + 1);
      }
      // first line
      else if (i === 0) {
        lines[index] = text.slice(0, startZero.column);
      }
      // last line
      else if (i === array.length - 1) {
        lines[index] = text.slice(endZero.column + 1);
      }
    }
  }
}

module.exports = applyAlterations;

/**
 * Merge every pair of elements into the output as a single element.
 */
function mergeByTwos(reduced, value, i, array) {
  return (i % 2 === 0) ? reduced : reduced.concat(array[i - 1] + value);
}

/**
 * Execute the item operation.
 * @param {{operation:function, loc:object}} item The item to execute
 */
function executeItem(item) {
  item.operation(item.loc.start, item.loc.end);
}

/**
 * Array.sort() compare function that sorts on start location descending
 */
function startLocDescending(a, b) {
  var dLine   = b.loc.start.line - a.loc.start.line,
      dColumn = b.loc.start.column - a.loc.start.column;
  return dLine || dColumn;
}

/**
 * Create a method that encodes an item with the given operation and converts its current value to a location.
 * @param {function} operation An operation field
 * @returns {function():{operation:function, loc:object}} A method that performs the encoding
 */
function encode(operation) {
  return function eachItem(loc) {
    return {
      operation: operation,
      loc      : loc
    };
  };
}

/**
 * Determine leading whitespace over by reducing a number of lines.
 */
function reduceIndent(reduced, line) {
  var analysis = /^\s*/.exec(line),
      indent   = analysis && analysis[0];
  return indent && (indent.length < reduced.length) && indent || reduced;
}

/**
 * Convert all values in the location from 1-based to 0-based.
 * @param {{line:number, column:number}} loc A location with 1-based numeric fields
 * @returns {{line:number, column:number}} A location with 0-based numeric fields
 */
function zeroBased(loc) {
  return {
    line  : loc.line - 1,
    column: loc.column - 1
  };
}

/**
 * Convert all values in the location from 0-based to 1-based.
 * @param {{line:number, column:number}} A location with 0-based numeric fields
 * @returns {{line:number, column:number}} loc A location with 1-based numeric fields
 */
function oneBased(loc) {
  return {
    line  : loc.line + 1,
    column: loc.column + 1
  };
}<commit_msg>working but source-maps are untested<commit_after>'use strict';

var Generator = require('source-map').SourceMapGenerator;

/**
 * Apply changes to the content.
 * @param {string} content The content to process
 * @param {{additions:Array, removals:Array}} pending A hash of additions and removals
 * @param {string} quoteChar String literal character to use
 * @returns {{content:string, generator:SourceMapGenerator}} Amended content and source-map generator
 */
function applyAlterations(sourcePath, content, pending, quoteChar) {

  // additions will be the '"ngInject";' directive
  var directive = quoteChar + 'ngInject' + quoteChar + ';';

  // create a list containing each line and any line terminator relating to it
  //  changes will mutate this list
  var lines = content
    .split(/(\r?\n)/)
    .reduce(mergeByTwos, []);

  // create a source-map generator to track changes in the source
  //  changes will mutate this instance
  var generator = new Generator();

  // if we make later changes first then the remaining locations will not be changed
  pending.additions.map(encode(addition))
    .concat(pending.removals.map(encode(removal)))
    .sort(startLocDescending)
    .forEach(executeItem);

  return {
    content  : lines.join(''),
    generator: generator
  };

  /**
   * Add "ngInject" directive syntax at the given location.
   * @param {{line:number, column:number}} start A start location from where we might insert
   * @param {{line:number, column:number}} end An end location to where we might insert
   */
  function addition(start, end) {

    // zero-based start and end indices
    var startZero = zeroBased(start),
        endZero   = zeroBased(end);

    // look at the first line and determine whether we should annotate in the first line
    var fnDeclaration       = lines[startZero.line].slice(0, startZero.column),
        blockOuterFirstLine = lines[startZero.line].slice(startZero.column),
        blockOuterPrefix    = blockOuterFirstLine.match(/^\s*\{/)[0],
        blockInnerFirstLine = blockOuterFirstLine.slice(blockOuterPrefix.length),
        isInline            = !(/^\s*$/.test(blockInnerFirstLine));

    // comment in the same line
    if (isInline) {
      lines[startZero.line] = fnDeclaration + blockOuterPrefix + directive + blockInnerFirstLine;
      generator.addMapping({
        source   : sourcePath,
        original : start,
        generated: {
          line  : start.line,
          column: start.column + directive.length
        }
      });
    }
    // comment on a new line
    else {
      var indexFirst = startZero.line + 1,
          indexLast  = endZero.line - 1,
          text       = lines[indexFirst],
          indent     = lines.slice(indexFirst, indexLast + 1).reduce(reduceIndent, text),
          eol        = text.match(/\r?\n/)[0],
          insertion  = indent + directive + eol;

      // text change
      lines.splice(indexFirst, 0, insertion);

      // source-map change
      generator.addMapping({
        source   : sourcePath,
        original : oneBased({
          line  : indexFirst,
          column: 0
        }),
        generated: oneBased({
          line  : indexFirst + 1,
          column: 0
        })
      });
    }
  }

  /**
   * Remove @ngInject comment at the given location.
   * @param {{line:number, column:number}} start A start location from where we might remove
   * @param {{line:number, column:number}} end An end location to where we might remove
   */
  function removal(start, end) {

    // zero-based start and end indices
    var startZero = zeroBased(start),
        endZero   = zeroBased(end);

    // text change
    lines.slice(endZero.line, endZero.line + 1)
      .forEach(eachLine);

    // source-map change
    generator.addMapping({
      source   : sourcePath,
      original : end,
      generated: start
    });

    function eachLine(text, i, array) {
      var index = endZero.line + i;

      // first and last line in one
      if (array.length === 1) {
        lines[index] = text.slice(0, startZero.column) + text.slice(endZero.column + 1);
      }
      // first line
      else if (i === 0) {
        lines[index] = text.slice(0, startZero.column);
      }
      // last line
      else if (i === array.length - 1) {
        lines[index] = text.slice(endZero.column + 1);
      }
    }
  }
}

module.exports = applyAlterations;

/**
 * Merge every pair of elements into the output as a single element.
 */
function mergeByTwos(reduced, value, i, array) {
  return (i % 2 === 0) ? reduced : reduced.concat(array[i - 1] + value);
}

/**
 * Execute the item operation.
 * @param {{operation:function, loc:object}} item The item to execute
 */
function executeItem(item) {
  item.operation(item.loc.start, item.loc.end);
}

/**
 * Array.sort() compare function that sorts on start location descending
 */
function startLocDescending(a, b) {
  var dLine   = b.loc.start.line - a.loc.start.line,
      dColumn = b.loc.start.column - a.loc.start.column;
  return dLine || dColumn;
}

/**
 * Create a method that encodes an item with the given operation and converts its current value to a location.
 * @param {function} operation An operation field
 * @returns {function():{operation:function, loc:object}} A method that performs the encoding
 */
function encode(operation) {
  return function eachItem(loc) {
    return {
      operation: operation,
      loc      : loc
    };
  };
}

/**
 * Determine leading whitespace over by reducing a number of lines.
 */
function reduceIndent(reduced, line) {
  var analysis = /^\s*/.exec(line),
      indent   = analysis && analysis[0];
  return indent && (indent.length < reduced.length) && indent || reduced;
}

/**
 * Convert all values in the location from 1-based to 0-based.
 * @param {{line:number, column:number}} loc A location with 1-based numeric fields
 * @returns {{line:number, column:number}} A location with 0-based numeric fields
 */
function zeroBased(loc) {
  return {
    line  : loc.line - 1,
    column: loc.column - 1
  };
}

/**
 * Convert all values in the location from 0-based to 1-based.
 * @param {{line:number, column:number}} A location with 0-based numeric fields
 * @returns {{line:number, column:number}} loc A location with 1-based numeric fields
 */
function oneBased(loc) {
  return {
    line  : loc.line + 1,
    column: loc.column + 1
  };
}<|endoftext|>


<commit_before>var EventEmitter = require('wolfy87-eventemitter');
var NodeNetworkRpc = require('node-network-rpc');
var DnsClient = require('./DnsClient');
var DnsDb = require('./DnsDb');
var DnsDbRouter = require('./DnsDbRouter');
var Utils = require('./Utils');

function DnsRpc(config) {
	var self = this;

	var defaultConfig = {
		dnsDb: {
			apiPath: '/api/dns', // Default path on router
			public: false // Default is hidden
		},
		dnsClient: {
			apiPath: '/api/dns' // Default path on router
		},
		network: {}
	};

	self._config = Utils.objectMerge(defaultConfig, config);
	self._config.dnsDb = Utils.objectMerge(defaultConfig.dnsDb, self._config.dnsDb);
	self._config.dnsClient = Utils.objectMerge(defaultConfig.dnsClient, self._config.dnsClient);

	// Set up RPC
	self.connected = false;
	self.rpc = NodeNetworkRpc(self._config.network);
	self.network = self.rpc.network;
	self.router = self.rpc.router;

	self.network.on('address', function(address) {
		self.address = address;

		if(typeof address == 'undefined') { // Disconnected
			if(typeof self._prevAddress != 'undefined')
				self.dnsClient.removeFromAllGroups(self._prevAddress);
			self.emit('disconnect');
		}
		else { // Connected
			self._config.groups.forEach(function(groupName) {
				self.dnsClient.addToGroup(address, groupName)
			});
			self.emit('connect');
		}

		self._prevAddress = address;
	});

	// Set up DNS
	self.dnsDb = DnsDb(self._config.dnsDb);
	self.dnsClient = DnsClient(self.dnsDb, self.rpc, self._config.dnsClient);

	// Add dnsDb to Router (make dnsDb public on the network)
	if(typeof self._config.dnsDb.public == 'boolean' && self._config.dnsDb.public) {
		self.router.use(self._config.dnsDb.apiPath, DnsDbRouter(self.dnsDb));
	}
}

DnsRpc.prototype.__proto__ = EventEmitter.prototype;

DnsRpc.prototype.request = function() {
	var self = this;

	// TODO make destGroup optional
	var parsedArgs = Utils.parseArgs(
		arguments,
		[
			{name: 'destGroup', level: 1, type: 'string', default: self._config.defaultGroup},
			{name: 'path', level: 0, validate: function(arg, allArgs) { return typeof arg == 'string' && arg[0] == '/'; }},
			{name: 'query', level: 1, type: 'object', default: {}},
			{name: 'options', level: 2,  type: 'object', default: {}},
			{name: 'callback', level: 1,  type: 'function'}
		]
	);

	var defaultOptions = {
		groupLookup: true,
		groupSubscribe: false
	};

	parsedArgs.options = Utils.objectMerge(defaultOptions, parsedArgs.options);

	if(typeof parsedArgs.destGroup != 'string')
		callbackSafe('destGroup_undefined', null);
	else {
		self._dnsDb.groups.findOne(parsedArgs.destGroup, function(error, groupDoc) {
			if(error)
				callbackSafe(error, null);
			else if(groupDoc != null) { // Group is saved locally
				if(Array.isArray(groupDoc.addresses) && groupDoc.addresses.length > 0) // group has members
					self._rpc.request(groupDoc.addresses, parsedArgs.path, parsedArgs.query, parsedArgs.options, parsedArgs.callback);
			}
			else if(parsedArgs.options.groupLookup) { // Try retreiving group remotely
				if(parsedArgs.options.groupSubscribe) {
					// TODO check if subscription in progress
					self.groupSubscribe(parsedArgs.destGroup, function(error, subscriptionId) {
						self._dnsDb.groups.findOne(parsedArgs.destGroup, function(error, groupDoc) {
							if(error)
								callbackSafe(error, null);
							else if(groupDoc == null || (Array.isArray(groupDoc.addresses) && groupDoc.addresses.length == 0))
								callbackSafe('empty_group', null)
							else if(Array.isArray(groupDoc.addresses) && groupDoc.addresses.length > 0) { // group has members
								self._rpc.request(groupDoc.addresses, parsedArgs.path, parsedArgs.query, parsedArgs.options, parsedArgs.callback);
							}
						});
					});
				}
				else {
					self.groupAddresses(parsedArgs.destGroup, function(error, addresses) {
						if(error)
							callbackSafe(error, null);
						else
							self._rpc.request(addresses, parsedArgs.path, parsedArgs.query, parsedArgs.options, parsedArgs.callback);
					});
				}
			}
			else
				callbackSafe('group_undefined', null);
		});

	}

	function callbackSafe(error, result) {
		if(typeof parsedArgs.callback == 'function')
			parsedArgs.callback(error, result);
	}
};

DnsRpc.prototype.addConnection = function() {
	this.rpc.addConnection.apply(this.rpc, Array.prototype.slice.call(arguments));
};

module.exports = function(config) {
	return new DnsRpc(config)
};<commit_msg>Emits address event on connection<commit_after>var EventEmitter = require('wolfy87-eventemitter');
var NodeNetworkRpc = require('node-network-rpc');
var DnsClient = require('./DnsClient');
var DnsDb = require('./DnsDb');
var DnsDbRouter = require('./DnsDbRouter');
var Utils = require('./Utils');

function DnsRpc(config) {
	var self = this;

	var defaultConfig = {
		dnsDb: {
			apiPath: '/api/dns', // Default path on router
			public: false // Default is hidden
		},
		dnsClient: {
			apiPath: '/api/dns' // Default path on router
		},
		network: {}
	};

	self._config = Utils.objectMerge(defaultConfig, config);
	self._config.dnsDb = Utils.objectMerge(defaultConfig.dnsDb, self._config.dnsDb);
	self._config.dnsClient = Utils.objectMerge(defaultConfig.dnsClient, self._config.dnsClient);

	// Set up RPC
	self.connected = false;
	self.rpc = NodeNetworkRpc(self._config.network);
	self.network = self.rpc.network;
	self.router = self.rpc.router;

	self.network.on('address', function(address) {
		self.address = address;

		self.emit('address', address);

		if(typeof address == 'undefined') { // Disconnected
			if(typeof self._prevAddress != 'undefined')
				self.dnsClient.removeFromAllGroups(self._prevAddress);
			self.emit('disconnect');
		}
		else { // Connected
			self._config.groups.forEach(function(groupName) {
				self.dnsClient.addToGroup(address, groupName)
			});
			self.emit('connect');
		}

		self._prevAddress = address;
	});

	// Set up DNS
	self.dnsDb = DnsDb(self._config.dnsDb);
	self.dnsClient = DnsClient(self.dnsDb, self.rpc, self._config.dnsClient);

	// Add dnsDb to Router (make dnsDb public on the network)
	if(typeof self._config.dnsDb.public == 'boolean' && self._config.dnsDb.public) {
		self.router.use(self._config.dnsDb.apiPath, DnsDbRouter(self.dnsDb));
	}
}

DnsRpc.prototype.__proto__ = EventEmitter.prototype;

DnsRpc.prototype.request = function() {
	var self = this;

	// TODO make destGroup optional
	var parsedArgs = Utils.parseArgs(
		arguments,
		[
			{name: 'destGroup', level: 1, type: 'string', default: self._config.defaultGroup},
			{name: 'path', level: 0, validate: function(arg, allArgs) { return typeof arg == 'string' && arg[0] == '/'; }},
			{name: 'query', level: 1, type: 'object', default: {}},
			{name: 'options', level: 2,  type: 'object', default: {}},
			{name: 'callback', level: 1,  type: 'function'}
		]
	);

	var defaultOptions = {
		groupLookup: true,
		groupSubscribe: false
	};

	parsedArgs.options = Utils.objectMerge(defaultOptions, parsedArgs.options);

	if(typeof parsedArgs.destGroup != 'string')
		callbackSafe('destGroup_undefined', null);
	else {
		self._dnsDb.groups.findOne(parsedArgs.destGroup, function(error, groupDoc) {
			if(error)
				callbackSafe(error, null);
			else if(groupDoc != null) { // Group is saved locally
				if(Array.isArray(groupDoc.addresses) && groupDoc.addresses.length > 0) // group has members
					self._rpc.request(groupDoc.addresses, parsedArgs.path, parsedArgs.query, parsedArgs.options, parsedArgs.callback);
			}
			else if(parsedArgs.options.groupLookup) { // Try retreiving group remotely
				if(parsedArgs.options.groupSubscribe) {
					// TODO check if subscription in progress
					self.groupSubscribe(parsedArgs.destGroup, function(error, subscriptionId) {
						self._dnsDb.groups.findOne(parsedArgs.destGroup, function(error, groupDoc) {
							if(error)
								callbackSafe(error, null);
							else if(groupDoc == null || (Array.isArray(groupDoc.addresses) && groupDoc.addresses.length == 0))
								callbackSafe('empty_group', null)
							else if(Array.isArray(groupDoc.addresses) && groupDoc.addresses.length > 0) { // group has members
								self._rpc.request(groupDoc.addresses, parsedArgs.path, parsedArgs.query, parsedArgs.options, parsedArgs.callback);
							}
						});
					});
				}
				else {
					self.groupAddresses(parsedArgs.destGroup, function(error, addresses) {
						if(error)
							callbackSafe(error, null);
						else
							self._rpc.request(addresses, parsedArgs.path, parsedArgs.query, parsedArgs.options, parsedArgs.callback);
					});
				}
			}
			else
				callbackSafe('group_undefined', null);
		});

	}

	function callbackSafe(error, result) {
		if(typeof parsedArgs.callback == 'function')
			parsedArgs.callback(error, result);
	}
};

DnsRpc.prototype.addConnection = function() {
	this.rpc.addConnection.apply(this.rpc, Array.prototype.slice.call(arguments));
};

module.exports = function(config) {
	return new DnsRpc(config)
};<|endoftext|>


<commit_before>juice.controller(function($$){

    $$.create = function () {

        $$.view.children().each(function(){
            $(this).wrap('

');
            $(this).parent().hide();
        });
        setPage(0, 'bounce');

    };

    $$.destroy = function() {

        $$.view.children().each(function(){
           $(this).unwrap();
        });
        currentPage = -1;

    };

    $$.api = function(command, options) {
      switch (command) {
          case 'setPage':
              setPage(options, 'bounce');
              break;
      }
    };

    // Private Members

    var currentPage = -1;

    function setPage(p, anim) {
        var pages = $$.view.children();
        if (p > currentPage) {
            pages.eq(p).animateCss(anim+'InRight').show();
            pages.eq(currentPage).animateCss(anim+'OutLeft', function(){
                pages.eq(currentPage).hide();
                currentPage = p;
            }).show();
        } else if (p < currentPage) {
            pages.eq(p).animateCss(anim+'InLeft').show();
            pages.eq(currentPage).animateCss(anim+'OutRight', function(){
                pages.eq(currentPage).hide();
                currentPage = p;
            }).show();
        }
    }

});<commit_msg>Delete pager.js<commit_after><|endoftext|>


<commit_before>import Ember from 'ember';
import module from '../helpers/module-for-db';
import { test } from '../helpers/qunit';

const {
  run,
  RSVP: { all }
} = Ember;

module('query-loader', {
  async beforeEach() {
    this.owner = Ember.Object.create({ id: null });
    this.opts = {
      owner: [ 'id' ],
      query(owner) {
        let id = owner.get('id');
        if(!id) {
          return;
        }
        return { id };
      }
    };
    this.parent = {
      withPropertyChanges() {}
    };
    this.first = () => this.db._createInternalQueryLoader(this.parent, this.owner, this.opts, 'first').model(true);
    this.settle = loader => loader._internal.settle();
  }
});

test('it exists', function(assert) {
  this.opts.autoload = false;

  let loader = this.first();
  assert.ok(loader);
  run(() => loader.destroy());
});

test('loader has query', function(assert) {
  this.opts.autoload = false;

  this.owner.set('id', 'duck');
  let loader = this.first();
  assert.deepEqual(loader._internal._query(true), {
    query: {
      id: 'duck'
    }
  });
  run(() => loader.destroy());
});

test('load succeeds', async function(assert) {
  await this.recreate();

  this.opts.autoload = false;

  await this.docs.save({ _id: 'duck' });
  this.owner.set('id', 'duck');

  let loader = this.first();
  assert.ok(!loader.get('isLoading'));

  let promise = loader.load();
  assert.ok(loader.get('isLoading'));

  let result = await promise;
  assert.ok(result === loader);
  assert.ok(this.db.existing('duck'));

  run(() => loader.destroy());
});

test('load fails', async function(assert) {
  await this.recreate();

  this.opts.autoload = false;

  this.owner.set('id', 'duck');
  let loader = this.first();

  try {
    await loader.load();
  } catch(err) {
    assert.equal(err.error, 'not_found');
  }

  assert.equal(loader._internal.state.error.error, 'not_found');

  run(() => loader.destroy());
});

test('two loads. second returns 1st promise', async function(assert) {
  await this.recreate();

  this.opts.autoload = false;

  await this.docs.save({ _id: 'duck' });
  this.owner.set('id', 'duck');

  let loader = this.first();

  let one = loader._internal.reload();
  let two = loader._internal.reload();

  await all([ one, two ]);

  assert.ok(this.db.existing('duck'));
  assert.ok(one === two);

  run(() => loader.destroy());
});

test('destroy while loading', async function(assert) {
  await this.recreate();

  this.opts.autoload = false;
  await this.docs.save({ _id: 'duck' });
  this.owner.set('id', 'duck');

  let loader = this.first();
  let promise = loader._internal.reload();

  run(() => loader.destroy());

  await promise;

  assert.ok(loader._internal.state.isLoaded);
});

test('autoload for isLoading, isLoaded', async function(assert) {
  await this.recreate();

  await this.docs.save({ _id: 'duck' });
  this.owner.set('id', 'duck');
  let loader = this.first();

  assert.equal(loader._internal.state.isLoading, false);
  assert.equal(loader.get('isLoading'), true);
  assert.equal(loader._internal.state.isLoading, true);
  assert.equal(loader._internal.operations.get('length'), 1);

  assert.equal(loader.get('isLoading'), true);
  assert.equal(loader.get('isLoaded'), false);
  assert.equal(loader._internal.operations.get('length'), 1);

  await this.settle(loader);
});

test('autoload and force load', async function(assert) {
  await this.recreate();

  await this.docs.save({ _id: 'duck' });
  this.owner.set('id', 'duck');
  let loader = this.first();

  assert.equal(loader._internal.state.isLoading, false);
  assert.equal(loader.get('isLoading'), true);
  assert.equal(loader._internal.state.isLoading, true);
  assert.equal(loader._internal.operations.get('length'), 1);

  loader.reload();
  assert.equal(loader._internal.operations.get('length'), 1);

  await this.settle(loader);
});

test('load and force load', async function(assert) {
  await this.recreate();

  await this.docs.save({ _id: 'duck' });
  this.owner.set('id', 'duck');
  let loader = this.first();

  loader.load();
  assert.equal(loader._internal.operations.get('length'), 1);

  loader.reload();
  assert.equal(loader._internal.operations.get('length'), 1);

  loader.reload();
  assert.equal(loader._internal.operations.get('length'), 1);

  await this.settle(loader);
});

test('load on owner property change', async function(assert) {
  await this.recreate();

  await this.docs.save({ _id: 'duck' });
  let loader = this.first();

  assert.equal(loader._internal.operations.get('length'), 0);

  this.owner.set('id', 'du');
  assert.equal(loader._internal.operations.get('length'), 0);
  assert.equal(loader.get('isLoading'), true);
  assert.equal(loader._internal.operations.get('length'), 1);

  this.owner.set('id', 'duc');
  assert.equal(loader._internal.operations.get('length'), 1);
  assert.equal(loader.get('isLoading'), true);
  assert.equal(loader._internal.operations.get('length'), 2);

  this.owner.set('id', 'duck');
  assert.equal(loader._internal.operations.get('length'), 2);
  assert.equal(loader.get('isLoading'), true);
  assert.equal(loader._internal.operations.get('length'), 3);

  await this.settle(loader);

  assert.ok(this.db.existing('duck'));
});

test('loadable is false', async function(assert) {
  let loader = this.first();
  assert.equal(loader.get('isLoadable'), false);
  await this.settle(loader);
});

test('loadable updates on owner prop change', async function(assert) {
  let loader = this.first();

  assert.equal(loader.get('isLoadable'), false);
  this.owner.set('id', 'foo');
  assert.equal(loader.get('isLoadable'), true);

  await this.settle(loader);
});

test('loadable is set to false on owner prop change', async function(assert) {
  this.owner.set('id', 'duck');
  let loader = this.first();

  assert.equal(loader.get('isLoadable'), true);
  this.owner.set('id', null);
  assert.equal(loader.get('isLoadable'), false);

  await this.settle(loader);
});

test('not loadable load rejects', async function(assert) {
  let loader = this.first();
  try {
    await loader.load();
  } catch(err) {
    assert.deepEqual(err.toJSON(), {
      "error": "loader",
      "reason": "not_loadable"
    });
  }

  await this.settle(loader);
});

test('reload reject if not loadable', async function(assert) {
  let loader = this.first();
  try {
    await loader.reload();
  } catch(err) {
    assert.deepEqual(err.toJSON(), {
      "error": "loader",
      "reason": "not_loadable"
    });
  }

  await this.settle(loader);
});

test('isLoadable does not start loading', async function(assert) {
  this.owner.set('id', 'duck');
  let loader = this.first();

  assert.equal(loader.get('isLoadable'), true);
  assert.equal(loader._internal.operations.get('length'), 0);

  await this.settle(loader);
});

test('state.isLoadable is false', async function(assert) {
  let loader = this.first();

  assert.deepEqual(loader.get('state'), {
    "error": null,
    "isError": false,
    "isLoadable": false,
    "isLoaded": false,
    "isLoading": false
  });

  assert.equal(loader.get('isLoadable'), false);
  assert.equal(loader.get('isLoaded'), false);

  await this.settle(loader);
});

test('state.isLoadable is true', async function(assert) {
  this.owner.set('id', 'hello');
  let loader = this.first();

  assert.deepEqual(loader.get('state'), {
    "error": null,
    "isError": false,
    "isLoadable": true,
    "isLoaded": false,
    "isLoading": false
  });

  await this.settle(loader);
});

test('state.isLoadable becomes true, load starts on state query', async function(assert) {
  await this.recreate();
  await all([
    this.docs.save({ _id: 'hello' }),
    this.docs.save({ _id: 'another' })
  ]);

  let loader = this.first();

  assert.deepEqual(loader.get('state'), {
    "error": null,
    "isError": false,
    "isLoadable": false,
    "isLoaded": false,
    "isLoading": false
  });

  assert.equal(loader.get('isLoading'), false);
  assert.equal(loader.get('isLoadable'), false);

  this.owner.set('id', 'hello');

  assert.deepEqual(loader.get('state'), {
    "error": null,
    "isError": false,
    "isLoadable": true,
    "isLoaded": false,
    "isLoading": false
  });

  assert.equal(loader._internal.operations.get('length'), 0);

  assert.equal(loader.get('isLoadable'), true);
  assert.equal(loader.get('isLoading'), true);

  assert.equal(loader._internal.operations.get('length'), 1);

  await this.settle(loader);

  assert.ok(this.db.existing('hello'));

  this.owner.set('id', null);

  assert.equal(loader.get('isLoadable'), false);
  assert.equal(loader.get('isLoading'), false);

  this.owner.set('id', 'second');

  assert.equal(loader.get('isLoadable'), true);
  assert.equal(loader.get('isLoading'), true);

  await this.settle(loader);

  assert.ok(this.db.existing('hello'));
});
<commit_msg>few more test asserts<commit_after>import Ember from 'ember';
import module from '../helpers/module-for-db';
import { test } from '../helpers/qunit';

const {
  run,
  RSVP: { all }
} = Ember;

module('query-loader', {
  async beforeEach() {
    this.owner = Ember.Object.create({ id: null });
    this.opts = {
      owner: [ 'id' ],
      query(owner) {
        let id = owner.get('id');
        if(!id) {
          return;
        }
        return { id };
      }
    };
    this.parent = {
      withPropertyChanges() {}
    };
    this.first = () => this.db._createInternalQueryLoader(this.parent, this.owner, this.opts, 'first').model(true);
    this.settle = loader => loader._internal.settle();
  }
});

test('it exists', function(assert) {
  this.opts.autoload = false;

  let loader = this.first();
  assert.ok(loader);
  run(() => loader.destroy());
});

test('loader has query', function(assert) {
  this.opts.autoload = false;

  this.owner.set('id', 'duck');
  let loader = this.first();
  assert.deepEqual(loader._internal._query(true), {
    query: {
      id: 'duck'
    }
  });
  run(() => loader.destroy());
});

test('load succeeds', async function(assert) {
  await this.recreate();

  this.opts.autoload = false;

  await this.docs.save({ _id: 'duck' });
  this.owner.set('id', 'duck');

  let loader = this.first();
  assert.ok(!loader.get('isLoading'));

  let promise = loader.load();
  assert.ok(loader.get('isLoading'));

  let result = await promise;
  assert.ok(result === loader);
  assert.ok(this.db.existing('duck'));

  run(() => loader.destroy());
});

test('load fails', async function(assert) {
  await this.recreate();

  this.opts.autoload = false;

  this.owner.set('id', 'duck');
  let loader = this.first();

  try {
    await loader.load();
  } catch(err) {
    assert.equal(err.error, 'not_found');
  }

  assert.equal(loader._internal.state.error.error, 'not_found');

  run(() => loader.destroy());
});

test('two loads. second returns 1st promise', async function(assert) {
  await this.recreate();

  this.opts.autoload = false;

  await this.docs.save({ _id: 'duck' });
  this.owner.set('id', 'duck');

  let loader = this.first();

  let one = loader._internal.reload();
  let two = loader._internal.reload();

  await all([ one, two ]);

  assert.ok(this.db.existing('duck'));
  assert.ok(one === two);

  run(() => loader.destroy());
});

test('destroy while loading', async function(assert) {
  await this.recreate();

  this.opts.autoload = false;
  await this.docs.save({ _id: 'duck' });
  this.owner.set('id', 'duck');

  let loader = this.first();
  let promise = loader._internal.reload();

  run(() => loader.destroy());

  await promise;

  assert.ok(loader._internal.state.isLoaded);
});

test('autoload for isLoading, isLoaded', async function(assert) {
  await this.recreate();

  await this.docs.save({ _id: 'duck' });
  this.owner.set('id', 'duck');
  let loader = this.first();

  assert.equal(loader._internal.state.isLoading, false);
  assert.equal(loader.get('isLoading'), true);
  assert.equal(loader._internal.state.isLoading, true);
  assert.equal(loader._internal.operations.get('length'), 1);

  assert.equal(loader.get('isLoading'), true);
  assert.equal(loader.get('isLoaded'), false);
  assert.equal(loader._internal.operations.get('length'), 1);

  await this.settle(loader);
});

test('autoload and force load', async function(assert) {
  await this.recreate();

  await this.docs.save({ _id: 'duck' });
  this.owner.set('id', 'duck');
  let loader = this.first();

  assert.equal(loader._internal.state.isLoading, false);
  assert.equal(loader.get('isLoading'), true);
  assert.equal(loader._internal.state.isLoading, true);
  assert.equal(loader._internal.operations.get('length'), 1);

  loader.reload();
  assert.equal(loader._internal.operations.get('length'), 1);

  await this.settle(loader);
});

test('load and force load', async function(assert) {
  await this.recreate();

  await this.docs.save({ _id: 'duck' });
  this.owner.set('id', 'duck');
  let loader = this.first();

  loader.load();
  assert.equal(loader._internal.operations.get('length'), 1);

  loader.reload();
  assert.equal(loader._internal.operations.get('length'), 1);

  loader.reload();
  assert.equal(loader._internal.operations.get('length'), 1);

  await this.settle(loader);
});

test('load on owner property change', async function(assert) {
  await this.recreate();

  await this.docs.save({ _id: 'duck' });
  let loader = this.first();

  assert.equal(loader._internal.operations.get('length'), 0);

  this.owner.set('id', 'du');
  assert.equal(loader._internal.operations.get('length'), 0);
  assert.equal(loader.get('isLoading'), true);
  assert.equal(loader._internal.operations.get('length'), 1);

  this.owner.set('id', 'duc');
  assert.equal(loader._internal.operations.get('length'), 1);
  assert.equal(loader.get('isLoading'), true);
  assert.equal(loader._internal.operations.get('length'), 2);

  this.owner.set('id', 'duck');
  assert.equal(loader._internal.operations.get('length'), 2);
  assert.equal(loader.get('isLoading'), true);
  assert.equal(loader._internal.operations.get('length'), 3);

  await this.settle(loader);

  assert.ok(this.db.existing('duck'));
});

test('loadable is false', async function(assert) {
  let loader = this.first();
  assert.equal(loader.get('isLoadable'), false);
  await this.settle(loader);
});

test('loadable updates on owner prop change', async function(assert) {
  let loader = this.first();

  assert.equal(loader.get('isLoadable'), false);
  this.owner.set('id', 'foo');
  assert.equal(loader.get('isLoadable'), true);

  await this.settle(loader);
});

test('loadable is set to false on owner prop change', async function(assert) {
  this.owner.set('id', 'duck');
  let loader = this.first();

  assert.equal(loader.get('isLoadable'), true);
  this.owner.set('id', null);
  assert.equal(loader.get('isLoadable'), false);

  await this.settle(loader);
});

test('not loadable load rejects', async function(assert) {
  let loader = this.first();
  try {
    await loader.load();
  } catch(err) {
    assert.deepEqual(err.toJSON(), {
      "error": "loader",
      "reason": "not_loadable"
    });
  }

  await this.settle(loader);
});

test('reload reject if not loadable', async function(assert) {
  let loader = this.first();
  try {
    await loader.reload();
  } catch(err) {
    assert.deepEqual(err.toJSON(), {
      "error": "loader",
      "reason": "not_loadable"
    });
  }

  await this.settle(loader);
});

test('isLoadable does not start loading', async function(assert) {
  this.owner.set('id', 'duck');
  let loader = this.first();

  assert.equal(loader.get('isLoadable'), true);
  assert.equal(loader._internal.operations.get('length'), 0);

  await this.settle(loader);
});

test('state.isLoadable is false', async function(assert) {
  let loader = this.first();

  assert.deepEqual(loader.get('state'), {
    "error": null,
    "isError": false,
    "isLoadable": false,
    "isLoaded": false,
    "isLoading": false
  });

  assert.equal(loader.get('isLoadable'), false);
  assert.equal(loader.get('isLoaded'), false);

  await this.settle(loader);
});

test('state.isLoadable is true', async function(assert) {
  this.owner.set('id', 'hello');
  let loader = this.first();

  assert.deepEqual(loader.get('state'), {
    "error": null,
    "isError": false,
    "isLoadable": true,
    "isLoaded": false,
    "isLoading": false
  });

  await this.settle(loader);
});

test('state.isLoadable becomes true, load starts on state query', async function(assert) {
  await this.recreate();
  await all([
    this.docs.save({ _id: 'hello' }),
    this.docs.save({ _id: 'another' })
  ]);

  let loader = this.first();

  assert.deepEqual(loader.get('state'), {
    "error": null,
    "isError": false,
    "isLoadable": false,
    "isLoaded": false,
    "isLoading": false
  });

  assert.equal(loader.get('isLoading'), false);
  assert.equal(loader.get('isLoadable'), false);

  this.owner.set('id', 'hello');

  assert.deepEqual(loader.get('state'), {
    "error": null,
    "isError": false,
    "isLoadable": true,
    "isLoaded": false,
    "isLoading": false
  });

  assert.equal(loader._internal.operations.get('length'), 0);

  assert.equal(loader.get('isLoadable'), true);
  assert.equal(loader.get('isLoading'), true);

  assert.equal(loader._internal.operations.get('length'), 1);

  await this.settle(loader);

  assert.ok(this.db.existing('hello'));

  this.owner.set('id', null);

  assert.equal(loader.get('isLoadable'), false);
  assert.equal(loader.get('isLoading'), false);

  this.owner.set('id', 'second');

  assert.equal(loader.get('isLoadable'), true, 'isLoadable');
  assert.equal(loader.get('isLoading'), true, 'isLoading');

  assert.equal(loader._internal.operations.get('length'), 1, 'load operation scheduled');

  await this.settle(loader);

  assert.ok(this.db.existing('hello'));
});
<|endoftext|>


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

const Me = imports.misc.extensionUtils.getCurrentExtension();
const Gettext = imports.gettext.domain(Me.metadata.uuid);
const Gio = imports.gi.Gio;
const Lang = imports.lang;
const Main = imports.ui.main;
const Meta = imports.gi.Meta;
const PopupMenu = imports.ui.popupMenu;
const Shell = imports.gi.Shell;
const St = imports.gi.St;
const Util = imports.misc.util;
const _ = Gettext.gettext;

const DeskChangerSettings = Me.imports.settings.DeskChangerSettings;
const debug = Me.imports.utils.debug;
const error = Me.imports.utils.error;
const Ui = Me.imports.ui;

const DeskChangerControls = new Lang.Class({
    Name: 'DeskChangerControls',
    Extends: PopupMenu.PopupBaseMenuItem,

    _init: function (dbus, settings) {
        this._dbus = dbus;
        this._settings = settings;
        this._bindings = [];
        this.parent({can_focus: false, reactive: false});

        this._addKeyBinding('next-wallpaper', Lang.bind(this, this.next));
        this._addKeyBinding('prev-wallpaper', Lang.bind(this, this.prev));

        this._next = new Ui.DeskChangerButton('media-skip-forward', Lang.bind(this, this.next));
        this._prev = new Ui.DeskChangerButton('media-skip-backward', Lang.bind(this, this.prev));
        this._random = new Ui.DeskChangerStateButton([
            {
                icon: 'media-playlist-shuffle',
                name: 'random'
            },
            {
                icon: 'media-playlist-repeat',
                name: 'ordered'
            }
        ], Lang.bind(this, this._toggle_random));
        this._random.set_state((this._settings.random) ? 'random' : 'ordered');
        this._rotation = new Ui.DeskChangerStateButton([
            {
                icon: 'media-playback-stop',
                name: 'interval'
            },
            {
                icon: 'media-playback-start',
                name: 'disabled'
            },
            {
                icon: 'appointment-new',
                name: 'hourly'
            }
        ], Lang.bind(this, this._toggle_rotation));
        this._rotation.set_state(this._settings.rotation);

        if (this.addActor) {
            this._box = new St.BoxLayout({style: 'spacing: 20px;'});
            this.addActor(this._box, {align: St.Align.MIDDLE, span: -1});
            this._box.add_actor(this._prev, {expand: true});
            this._box.add_actor(this._random, {expand: true});
            this._box.add_actor(this._rotation, {expand: true});
            this._box.add_actor(this._next, {expand: true});
        } else {
            this.actor.add_actor(this._prev, {expand: true, x_fill: false});
            this.actor.add_actor(this._random, {expand: true, x_fill: false});
            this.actor.add_actor(this._rotation, {expand: true, x_fill: false});
            this.actor.add_actor(this._next, {expand: true, x_fill: false});
        }
    },

    destroy: function () {
        let size = this._bindings.length;

        for (let i = 0; i < size; i++) {
            this._removeKeyBinding(this._bindings[i]);
        }

        this._next.destroy();
        this._prev.destroy();
        this._random.destroy();
        this._rotation.destroy();
        this.parent();
    },

    next: function () {
        debug('next');
        this._dbus.NextRemote(function (result, _error) {
            if (_error) {
                Main.notifyError('Desk Changer', _error);
            }
        });
    },

    prev: function() {
        debug('prev');
        this._dbus.PrevRemote(function (result, _error) {
            if (_error) {
                Main.notifyError('Desk Changer', _error);
            }
        });
    },

    _addKeyBinding: function (key, handler) {
        let success = false;
        if (Shell.ActionMode) { // 3.16 and above
            success = Main.wm.addKeybinding(
                key,
                this._settings.schema,
                Meta.KeyBindingFlags.NONE,
                Shell.ActionMode.NORMAL,
                handler
            );
        } else { // 3.8 and above
            success = Main.wm.addKeybinding(
                key,
                this._settings.schema,
                Meta.KeyBindingFlags.NONE,
                Shell.KeyBindingMode.NORMAL,
                handler
            );
        }

        this._bindings.push(key);
        if (success) {
            debug('added keybinding ' + key);
        } else {
            debug('failed to add keybinding ' + key);
            debug(success);
        }
    },

    _removeKeyBinding: function (key) {
        if (this._bindings.indexOf(key)) {
            this._bindings.splice(this._bindings.indexOf(key), 1);
        }

        debug('removing keybinding ' + key);
        Main.wm.removeKeybinding(key);
    },

    _toggle_random: function (state) {
        debug('setting order to ' + state);
        this._settings.random = (state === 'random');
    },

    _toggle_rotation: function (state) {
        debug('setting rotation to ' + state);
        this._settings.rotation = state;
    }
});

const DeskChangerDaemonControls = new Lang.Class({
    Name: 'DeskChangerDaemonControls',
    Extends: PopupMenu.PopupSwitchMenuItem,

    _init: function (daemon) {
        // Switch label
        this.parent(_('DeskChanger Daemon'));
        this.daemon = daemon;
        this.setToggleState(this.daemon.is_running);
        this._handler = this.connect('toggled', Lang.bind(this, function () {
            this.daemon.toggle();
        }));
        this._daemon_handler = this.daemon.connect('toggled', Lang.bind(this, function (obj, state) {
            this.setToggleState(state);
        }));
    },

    destroy: function () {
        // not sure why, but removing this handler causes the extension to crash on unload... meh
        //debug('removing daemon switch handler '+this._handler);
        //this.disconnect(this._handler);
        debug('removing daemon toggled handler ' + this._daemon_handler);
        this.daemon.disconnect(this._daemon_handler);
        this.parent();
    }
});

const DeskChangerOpenCurrent = new Lang.Class({
    Name: 'DeskChangerOpenCurrent',
    Extends: PopupMenu.PopupMenuItem,

    _init: function () {
        this._background = new Gio.Settings({'schema': 'org.gnome.desktop.background'});
        // Menu item label
        this.parent(_('Open Current Wallpaper'));
        this._activate_id = this.connect('activate', Lang.bind(this, this._activate));
    },

    destroy: function () {
        debug('removing current activate handler ' + this._activate_id);
        this.disconnect(this._activate_id);
        this.parent();
    },

    _activate: function () {
        debug('opening current wallpaper ' + this._background.get_string('picture-uri'));
        Util.spawn(['xdg-open', this._background.get_string('picture-uri')]);
    }
});

const DeskChangerPreviewMenuItem = new Lang.Class({
    Name: 'DeskChangerPreviewMenuItem',
    Extends: PopupMenu.PopupBaseMenuItem,

    _init: function (daemon) {
        this.parent({reactive: true});
        this._box = new St.BoxLayout({vertical: true});
        try {
            this.addActor(this._box, {align: St.Align.MIDDLE, span: -1});
        } catch (e) {
            error(e, 'addActor call failed, falling back to actor.add_actor');
            this.actor.add_actor(this._box, {align: St.Align.MIDDLE, span: -1});
        }
        this._label = new St.Label({text: _('Open Next Wallpaper')});
        this._box.add(this._label);
        this._preview = new Ui.DeskChangerPreview(220, daemon);
        this._box.add(this._preview);
        this._activate_id = this.connect('activate', Lang.bind(this, this._clicked));
    },

    destroy: function () {
        debug('removing preview activate handler ' + this._activate_id);
        this.disconnect(this._activate_id);

        this._preview.destroy();
        this._label.destroy();
        this._box.destroy();
        this.parent();
    },

    _clicked: function () {
        if (this._preview.file) {
            debug('opening file ' + this._preview.file);
            Util.spawn(['xdg-open', this._preview.file]);
        } else {
            debug('ERROR: no preview currently set');
        }
    }
});

const DeskChangerProfileBase = new Lang.Class({
    Abstract: true,
    Name: 'DeskChangerProfileBase',
    Extends: PopupMenu.PopupSubMenuMenuItem,

    _init: function (label, key, settings, sensitive = true) {
        this._key = key;
        this._keyNormalized = key.replace('_', '-');
        this._label = label;
        this._settings = settings;
        this.parent("");
        this.setLabel();
        this._populate_profiles();
        this._settings.connect('changed::'+this._keyNormalized, Lang.bind(this, this.setLabel));
        this._settings.connect('changed::profiles', Lang.bind(this, this._populate_profiles))
        this.setSensitive(sensitive);
    },

    setLabel: function () {
        this.label.text = this._label + ': ' + this._settings[this._key];
    },

    _populate_profiles: function () {
        this.menu.removeAll();
        for (let index in this._settings.profiles) {
            debug('adding menu: ' + index);
            let item = new DeskChangerProfileItem(index, index, this._settings, this._key);
            this.menu.addMenuItem(item);
        }
    }
});

const DeskChangerProfileDesktop = new Lang.Class({
    Name: 'DeskChangerProfileDesktop',
    Extends: DeskChangerProfileBase,

    _init: function(settings, sensitive=true) {
        this.parent(_('Desktop Profile'), 'current_profile', settings, sensitive);
    },
});

const DeskChangerProfileLockscreen = new Lang.Class({
    Name: 'DeskChangerProfileLockscreen',
    Extends: DeskChangerProfileBase,

    _init: function(settings, sensitive=true) {
        this.parent(_('Lock Screen Profile'), 'lockscreen_profile', settings, sensitive);
    },

    setLabel: function () {
        let value = this._settings[this._key];

        if (value === '' || value === this._settings.current_profile) {
            value = _('(inherited)');
        }

        this.label.text = _('Lock Screen Profile') + value;
    },

    _populate_profiles: function () {
        this.parent();
        let inherit = new DeskChangerProfileItem(_('(inherit from desktop)'), '', this._settings, this._key);
        this.menu.addMenuItem(inherit);
    }
});

const DeskChangerProfileItem = new Lang.Class({
    Name: 'DeskChangerProfileItem',
    Extends: PopupMenu.PopupMenuItem,

    _init: function (label, value, settings, key) {
        this.parent(label);
        this._value = value;
        this._settings = settings;
        this._key = key;
        this._settingKey = key.replace('_', '-');

        if (this._settings[this._key] === this._value) {
            this.setOrnament(PopupMenu.Ornament.DOT);
        }

        this._handler_key_changed = this._settings.connect('changed::'+this._settingKey, Lang.bind(this, function () {
            if (this._settings[this._key] === this._value) {
                this.setOrnament(PopupMenu.Ornament.DOT);
            } else {
                this.setOrnament(PopupMenu.Ornament.NONE);
            }
        }));
        this._handler_id = this.connect('activate', Lang.bind(this, function () {
            this._settings[this._key] = this._value;
        }));
    },

    destroy: function () {
        this._settings.disconnect(this._handler_key_changed);
        this.disconnect(this._handler_id);
        this.parent();
    }
});

const DeskChangerSwitch = new Lang.Class({
    Name: 'DeskChangerSwitch',
    Extends: PopupMenu.PopupSwitchMenuItem,

    _init: function (label, setting, settings) {
        this._setting = setting;
        this._settings = settings;
        this.parent(label);
        this.setToggleState(this._settings[setting]);
        this._handler_changed = this._settings.connect('changed::' + this._setting.replace('_', '-'), Lang.bind(this, this._changed));
        this._handler_toggled = this.connect('toggled', Lang.bind(this, this._toggled));
    },

    destroy: function () {
        if (this._handler_changed) {
            debug('removing changed::' + this._setting + ' handler ' + this._handler_changed);
            this._settings.disconnect(this._handler_changed);
        }

        debug('removing swtich toggled handler ' + this._handler_toggled);
        this.disconnect(this._handler_toggled);
    },

    _changed: function (settings, key) {
        this.setToggleState(this._settings[this._setting]);
    },

    _toggled: function () {
        debug('setting ' + this._setting + ' to ' + this.state);
        this._settings[this._setting] = this.state;
    }
});
<commit_msg>Convert object to string before notifyError<commit_after>/**
 * Copyright (c) 2014-2017 Eric Gach <eric.gach@gmail.com>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

const Me = imports.misc.extensionUtils.getCurrentExtension();
const Gettext = imports.gettext.domain(Me.metadata.uuid);
const Gio = imports.gi.Gio;
const Lang = imports.lang;
const Main = imports.ui.main;
const Meta = imports.gi.Meta;
const PopupMenu = imports.ui.popupMenu;
const Shell = imports.gi.Shell;
const St = imports.gi.St;
const Util = imports.misc.util;
const _ = Gettext.gettext;

const DeskChangerSettings = Me.imports.settings.DeskChangerSettings;
const debug = Me.imports.utils.debug;
const error = Me.imports.utils.error;
const Ui = Me.imports.ui;

const DeskChangerControls = new Lang.Class({
    Name: 'DeskChangerControls',
    Extends: PopupMenu.PopupBaseMenuItem,

    _init: function (dbus, settings) {
        this._dbus = dbus;
        this._settings = settings;
        this._bindings = [];
        this.parent({can_focus: false, reactive: false});

        this._addKeyBinding('next-wallpaper', Lang.bind(this, this.next));
        this._addKeyBinding('prev-wallpaper', Lang.bind(this, this.prev));

        this._next = new Ui.DeskChangerButton('media-skip-forward', Lang.bind(this, this.next));
        this._prev = new Ui.DeskChangerButton('media-skip-backward', Lang.bind(this, this.prev));
        this._random = new Ui.DeskChangerStateButton([
            {
                icon: 'media-playlist-shuffle',
                name: 'random'
            },
            {
                icon: 'media-playlist-repeat',
                name: 'ordered'
            }
        ], Lang.bind(this, this._toggle_random));
        this._random.set_state((this._settings.random) ? 'random' : 'ordered');
        this._rotation = new Ui.DeskChangerStateButton([
            {
                icon: 'media-playback-stop',
                name: 'interval'
            },
            {
                icon: 'media-playback-start',
                name: 'disabled'
            },
            {
                icon: 'appointment-new',
                name: 'hourly'
            }
        ], Lang.bind(this, this._toggle_rotation));
        this._rotation.set_state(this._settings.rotation);

        if (this.addActor) {
            this._box = new St.BoxLayout({style: 'spacing: 20px;'});
            this.addActor(this._box, {align: St.Align.MIDDLE, span: -1});
            this._box.add_actor(this._prev, {expand: true});
            this._box.add_actor(this._random, {expand: true});
            this._box.add_actor(this._rotation, {expand: true});
            this._box.add_actor(this._next, {expand: true});
        } else {
            this.actor.add_actor(this._prev, {expand: true, x_fill: false});
            this.actor.add_actor(this._random, {expand: true, x_fill: false});
            this.actor.add_actor(this._rotation, {expand: true, x_fill: false});
            this.actor.add_actor(this._next, {expand: true, x_fill: false});
        }
    },

    destroy: function () {
        let size = this._bindings.length;

        for (let i = 0; i < size; i++) {
            this._removeKeyBinding(this._bindings[i]);
        }

        this._next.destroy();
        this._prev.destroy();
        this._random.destroy();
        this._rotation.destroy();
        this.parent();
    },

    next: function () {
        debug('next');
        this._dbus.NextRemote(function (result, _error) {
            if (_error) {
                Main.notifyError('Desk Changer', String(_error));
            }
        });
    },

    prev: function() {
        debug('prev');
        this._dbus.PrevRemote(function (result, _error) {
            if (_error) {
                Main.notifyError('Desk Changer', String(_error));
            }
        });
    },

    _addKeyBinding: function (key, handler) {
        let success = false;
        if (Shell.ActionMode) { // 3.16 and above
            success = Main.wm.addKeybinding(
                key,
                this._settings.schema,
                Meta.KeyBindingFlags.NONE,
                Shell.ActionMode.NORMAL,
                handler
            );
        } else { // 3.8 and above
            success = Main.wm.addKeybinding(
                key,
                this._settings.schema,
                Meta.KeyBindingFlags.NONE,
                Shell.KeyBindingMode.NORMAL,
                handler
            );
        }

        this._bindings.push(key);
        if (success) {
            debug('added keybinding ' + key);
        } else {
            debug('failed to add keybinding ' + key);
            debug(success);
        }
    },

    _removeKeyBinding: function (key) {
        if (this._bindings.indexOf(key)) {
            this._bindings.splice(this._bindings.indexOf(key), 1);
        }

        debug('removing keybinding ' + key);
        Main.wm.removeKeybinding(key);
    },

    _toggle_random: function (state) {
        debug('setting order to ' + state);
        this._settings.random = (state === 'random');
    },

    _toggle_rotation: function (state) {
        debug('setting rotation to ' + state);
        this._settings.rotation = state;
    }
});

const DeskChangerDaemonControls = new Lang.Class({
    Name: 'DeskChangerDaemonControls',
    Extends: PopupMenu.PopupSwitchMenuItem,

    _init: function (daemon) {
        // Switch label
        this.parent(_('DeskChanger Daemon'));
        this.daemon = daemon;
        this.setToggleState(this.daemon.is_running);
        this._handler = this.connect('toggled', Lang.bind(this, function () {
            this.daemon.toggle();
        }));
        this._daemon_handler = this.daemon.connect('toggled', Lang.bind(this, function (obj, state) {
            this.setToggleState(state);
        }));
    },

    destroy: function () {
        // not sure why, but removing this handler causes the extension to crash on unload... meh
        //debug('removing daemon switch handler '+this._handler);
        //this.disconnect(this._handler);
        debug('removing daemon toggled handler ' + this._daemon_handler);
        this.daemon.disconnect(this._daemon_handler);
        this.parent();
    }
});

const DeskChangerOpenCurrent = new Lang.Class({
    Name: 'DeskChangerOpenCurrent',
    Extends: PopupMenu.PopupMenuItem,

    _init: function () {
        this._background = new Gio.Settings({'schema': 'org.gnome.desktop.background'});
        // Menu item label
        this.parent(_('Open Current Wallpaper'));
        this._activate_id = this.connect('activate', Lang.bind(this, this._activate));
    },

    destroy: function () {
        debug('removing current activate handler ' + this._activate_id);
        this.disconnect(this._activate_id);
        this.parent();
    },

    _activate: function () {
        debug('opening current wallpaper ' + this._background.get_string('picture-uri'));
        Util.spawn(['xdg-open', this._background.get_string('picture-uri')]);
    }
});

const DeskChangerPreviewMenuItem = new Lang.Class({
    Name: 'DeskChangerPreviewMenuItem',
    Extends: PopupMenu.PopupBaseMenuItem,

    _init: function (daemon) {
        this.parent({reactive: true});
        this._box = new St.BoxLayout({vertical: true});
        try {
            this.addActor(this._box, {align: St.Align.MIDDLE, span: -1});
        } catch (e) {
            error(e, 'addActor call failed, falling back to actor.add_actor');
            this.actor.add_actor(this._box, {align: St.Align.MIDDLE, span: -1});
        }
        this._label = new St.Label({text: _('Open Next Wallpaper')});
        this._box.add(this._label);
        this._preview = new Ui.DeskChangerPreview(220, daemon);
        this._box.add(this._preview);
        this._activate_id = this.connect('activate', Lang.bind(this, this._clicked));
    },

    destroy: function () {
        debug('removing preview activate handler ' + this._activate_id);
        this.disconnect(this._activate_id);

        this._preview.destroy();
        this._label.destroy();
        this._box.destroy();
        this.parent();
    },

    _clicked: function () {
        if (this._preview.file) {
            debug('opening file ' + this._preview.file);
            Util.spawn(['xdg-open', this._preview.file]);
        } else {
            debug('ERROR: no preview currently set');
        }
    }
});

const DeskChangerProfileBase = new Lang.Class({
    Abstract: true,
    Name: 'DeskChangerProfileBase',
    Extends: PopupMenu.PopupSubMenuMenuItem,

    _init: function (label, key, settings, sensitive = true) {
        this._key = key;
        this._keyNormalized = key.replace('_', '-');
        this._label = label;
        this._settings = settings;
        this.parent("");
        this.setLabel();
        this._populate_profiles();
        this._settings.connect('changed::'+this._keyNormalized, Lang.bind(this, this.setLabel));
        this._settings.connect('changed::profiles', Lang.bind(this, this._populate_profiles))
        this.setSensitive(sensitive);
    },

    setLabel: function () {
        this.label.text = this._label + ': ' + this._settings[this._key];
    },

    _populate_profiles: function () {
        this.menu.removeAll();
        for (let index in this._settings.profiles) {
            debug('adding menu: ' + index);
            let item = new DeskChangerProfileItem(index, index, this._settings, this._key);
            this.menu.addMenuItem(item);
        }
    }
});

const DeskChangerProfileDesktop = new Lang.Class({
    Name: 'DeskChangerProfileDesktop',
    Extends: DeskChangerProfileBase,

    _init: function(settings, sensitive=true) {
        this.parent(_('Desktop Profile'), 'current_profile', settings, sensitive);
    },
});

const DeskChangerProfileLockscreen = new Lang.Class({
    Name: 'DeskChangerProfileLockscreen',
    Extends: DeskChangerProfileBase,

    _init: function(settings, sensitive=true) {
        this.parent(_('Lock Screen Profile'), 'lockscreen_profile', settings, sensitive);
    },

    setLabel: function () {
        let value = this._settings[this._key];

        if (value === '' || value === this._settings.current_profile) {
            value = _('(inherited)');
        }

        this.label.text = _('Lock Screen Profile') + value;
    },

    _populate_profiles: function () {
        this.parent();
        let inherit = new DeskChangerProfileItem(_('(inherit from desktop)'), '', this._settings, this._key);
        this.menu.addMenuItem(inherit);
    }
});

const DeskChangerProfileItem = new Lang.Class({
    Name: 'DeskChangerProfileItem',
    Extends: PopupMenu.PopupMenuItem,

    _init: function (label, value, settings, key) {
        this.parent(label);
        this._value = value;
        this._settings = settings;
        this._key = key;
        this._settingKey = key.replace('_', '-');

        if (this._settings[this._key] === this._value) {
            this.setOrnament(PopupMenu.Ornament.DOT);
        }

        this._handler_key_changed = this._settings.connect('changed::'+this._settingKey, Lang.bind(this, function () {
            if (this._settings[this._key] === this._value) {
                this.setOrnament(PopupMenu.Ornament.DOT);
            } else {
                this.setOrnament(PopupMenu.Ornament.NONE);
            }
        }));
        this._handler_id = this.connect('activate', Lang.bind(this, function () {
            this._settings[this._key] = this._value;
        }));
    },

    destroy: function () {
        this._settings.disconnect(this._handler_key_changed);
        this.disconnect(this._handler_id);
        this.parent();
    }
});

const DeskChangerSwitch = new Lang.Class({
    Name: 'DeskChangerSwitch',
    Extends: PopupMenu.PopupSwitchMenuItem,

    _init: function (label, setting, settings) {
        this._setting = setting;
        this._settings = settings;
        this.parent(label);
        this.setToggleState(this._settings[setting]);
        this._handler_changed = this._settings.connect('changed::' + this._setting.replace('_', '-'), Lang.bind(this, this._changed));
        this._handler_toggled = this.connect('toggled', Lang.bind(this, this._toggled));
    },

    destroy: function () {
        if (this._handler_changed) {
            debug('removing changed::' + this._setting + ' handler ' + this._handler_changed);
            this._settings.disconnect(this._handler_changed);
        }

        debug('removing swtich toggled handler ' + this._handler_toggled);
        this.disconnect(this._handler_toggled);
    },

    _changed: function (settings, key) {
        this.setToggleState(this._settings[this._setting]);
    },

    _toggled: function () {
        debug('setting ' + this._setting + ' to ' + this.state);
        this._settings[this._setting] = this.state;
    }
});
<|endoftext|>


<commit_before>var AtomScadPreview, AtomScadPreviewView, CompositeDisposable;

AtomScadPreviewView = require('./atom-scad-preview-view');

CompositeDisposable = require('atom').CompositeDisposable;

module.exports = AtomScadPreview = {
  atomScadPreviewView: null,
  modalPanel: null,
  subscriptions: null,
  activate: function(state) {
    this.atomScadPreviewView = new AtomScadPreviewView(state.atomScadPreviewViewState);
    this.modalPanel = atom.workspace.addModalPanel({
      item: this.atomScadPreviewView.getElement(),
      visible: false
    });
    this.subscriptions = new CompositeDisposable;
    return this.subscriptions.add(atom.commands.add('atom-workspace', {
      'atom-scad-preview:toggle': (function(_this) {
        return function() {
          return _this.toggle();
        };
      })(this)
    }));
  },
  deactivate: function() {
    this.modalPanel.destroy();
    this.subscriptions.dispose();
    return this.atomScadPreviewView.destroy();
  },
  serialize: function() {
    return {
      atomScadPreviewViewState: this.atomScadPreviewView.serialize()
    };
  },
  toggle: function() {
    console.log('AtomScadPreview was toggled!');
    if (this.modalPanel.isVisible()) {
      return this.modalPanel.hide();
    } else {
      return this.modalPanel.show();
    }
  }
};
<commit_msg>rightpanel!<commit_after>var AtomScadPreview, AtomScadPreviewView, CompositeDisposable;

AtomScadPreviewView = require('./atom-scad-preview-view');

CompositeDisposable = require('atom').CompositeDisposable;

module.exports = AtomScadPreview = {
  atomScadPreviewView: null,
  modalPanel: null,
  subscriptions: null,
  activate: function(state) {
    this.atomScadPreviewView = new AtomScadPreviewView(state.atomScadPreviewViewState);
    this.modalPanel = atom.workspace.addRightPanel({
      item: this.atomScadPreviewView.getElement(),
      visible: false
    });
    this.subscriptions = new CompositeDisposable;
    return this.subscriptions.add(atom.commands.add('atom-workspace', {
      'atom-scad-preview:toggle': (function(_this) {
        return function() {
          return _this.toggle();
        };
      })(this)
    }));
  },
  deactivate: function() {
    this.modalPanel.destroy();
    this.subscriptions.dispose();
    return this.atomScadPreviewView.destroy();
  },
  serialize: function() {
    return {
      atomScadPreviewViewState: this.atomScadPreviewView.serialize()
    };
  },
  toggle: function() {
    console.log('AtomScadPreview was toggled!');
    if (this.modalPanel.isVisible()) {
      return this.modalPanel.hide();
    } else {
      return this.modalPanel.show();
    }
  }
};
<|endoftext|>