<commit_before>var async = require('async');
var fs = require('fs');
var spawn = require('child_process').spawn;
var EventEmitter = require('events').EventEmitter;
var util = require('util');

util.inherits(Antr, EventEmitter);

function Antr (options, cb) {
  var self = this;
  this._cb = cb || function () {};

  this._options =  {
    dirname: options.dirname || '',
    filter: options.filter || /^(?!\.)(.+)\.js$/,
    batchSize: options.batchSize || 8
  }
}

Antr.prototype.run = function() {
  var self = this;

  self.getFiles(function (err, files) {
    self.emit('start', files);

    var stack = [];
    var complete = [];
    var totalTests = files.length;

    while(stack.length < self._options.batchSize && files.length > 0) {
      var current = files.pop();
      stack.push(current);

      var handle = function (err, data) {
        complete.push(stack.splice(stack.indexOf(data.item), 1)[0]);
        if(complete.length == totalTests) {
          self.emit('complete');
        } else {
          var current = files.pop();
          if(typeof current !== 'undefined') {
            stack.push(current);
            self.runSingleTest(current, self, handle);
          }
        }
      }
      self.runSingleTest(current, self, handle);
    }
  });
}

Antr.prototype.runSingleTest = function(item, self, callback){
  var runTest = spawn('node', [ self._options.dirname + '/' + item ]);
  var storedData = [];

  runTest.stdout.setEncoding('utf8');
  runTest.stdout.on('data', function (data) {
    storedData.push({type: 'stdout', data: data});
  });

  runTest.stderr.setEncoding('utf8');
  runTest.stderr.on('data', function (data) {
    storedData.push({type: 'stderr', data: data});
  });
  runTest.on('exit', function (code) {
    callback(null, { item: item, code: code, data: storedData });
    self.emit('fileComplete', code, item, storedData);
  });
}

Antr.prototype.getFiles = function (cb) {
  var self = this;
  fs.readdir(self._options.dirname, function (err, files) {
    if(err) return self._cb('Not a valid dir');

    async.filter(files, function (item, callback) {
      callback(self._options.filter.test(item));
    }, function (filteredFiles) {
      cb(null, filteredFiles);
    });
  });
}

module.exports = Antr;<commit_msg>Bug fix<commit_after>var async = require('async');
var fs = require('fs');
var spawn = require('child_process').spawn;
var EventEmitter = require('events').EventEmitter;
var util = require('util');

util.inherits(Antr, EventEmitter);

function Antr (options, cb) {
  var self = this;
  this._cb = cb || function () {};

  this._options =  {
    dirname: options.dirname || '',
    filter: options.filter || /^(?!\.)(.+)\.js$/,
    batchSize: options.batchSize || 8
  }
}

Antr.prototype.run = function() {
  var self = this;

  self.getFiles(function (err, files) {
    self.emit('start', files);

    if(files.length == 0) self.emit('complete');

    var stack = [];
    var complete = [];
    var totalTests = files.length;

    while(stack.length < self._options.batchSize && files.length > 0) {
      var current = files.pop();
      stack.push(current);

      var handle = function (err, data) {
        complete.push(stack.splice(stack.indexOf(data.item), 1)[0]);
        if(complete.length == totalTests) {
          self.emit('complete');
        } else {
          var current = files.pop();
          if(typeof current !== 'undefined') {
            stack.push(current);
            self.runSingleTest(current, self, handle);
          }
        }
      }
      self.runSingleTest(current, self, handle);
    }
  });
}

Antr.prototype.runSingleTest = function(item, self, callback){
  var runTest = spawn('node', [ self._options.dirname + '/' + item ]);
  var storedData = [];

  runTest.stdout.setEncoding('utf8');
  runTest.stdout.on('data', function (data) {
    storedData.push({type: 'stdout', data: data});
  });

  runTest.stderr.setEncoding('utf8');
  runTest.stderr.on('data', function (data) {
    storedData.push({type: 'stderr', data: data});
  });
  runTest.on('exit', function (code) {
    callback(null, { item: item, code: code, data: storedData });
    self.emit('fileComplete', code, item, storedData);
  });
}

Antr.prototype.getFiles = function (cb) {
  var self = this;
  fs.readdir(self._options.dirname, function (err, files) {
    if(err) return self._cb('Not a valid dir');

    async.filter(files, function (item, callback) {
      callback(self._options.filter.test(item));
    }, function (filteredFiles) {
      cb(null, filteredFiles);
    });
  });
}

module.exports = Antr;<|endoftext|>


<commit_before>/**
 * Copyright (c) 2015 Scott Bishop
 * BluJagu, LLC - www.blujagu.com
 * MIT License (MIT) - This header must remain intact.
 **/
define(['./eventHub'], function(eventHub) {
    'use strict';

    var model = {}; // stub for model

    eventHub.connect(model);

    // create some new javaScript Object types :)
    var DataObj = window.DataObject = function DataObject () {
        this.value = undefined;
        this._model = undefined;
    };

    DataObj.prototype._initialized = false;

    DataObj.prototype.nodeFromPath = function (path, create) {
        var paths = path.split('.'),
            prop = '',
            node = this.value,
            cache = this;

        for (; paths.length > 0;) {
            prop = paths.shift();
            // do we have a node?
            if (node && node[prop]) {
                node = node[prop];
            } else if (create) {
                if (cache instanceof DataObject) {
                    cache.set({}, prop, false);
                    node = cache.value[prop];
                } else {
                    cache[prop] = {};
                    node = cache[prop];
                }
                cache = node;
            }
        }

        return node;
    };

    DataObj.prototype.get = function (prop, preventExecution) {
        var result = prop ? this.nodeFromPath(prop, false) : this.value;

        if (typeof result === 'function' && !preventExecution) {
            result = result(this);
        }

        return result;
    };

    // TODO: create setMany method => accepts an object and translates property/values into set calls
    DataObj.prototype.set = function (val, prop, preventDispatch) {
        var detail = {},
            path = '';

        if (prop) {
            if (prop.indexOf('.') !== -1) {
                path = prop.split('.');
                path.pop();
                path = path.join('.');
                this.nodeFromPath(path, true)[prop] = val;
            } else {
                if (this.value === undefined) {
                    this.value = {};
                }
                this.value[prop] = val;
            }
        } else {
            this.value = val;
        }

        if (!this._initialized) {
            this._initialized = true;
            detail.type = 'add';
        } else {
            detail.type = 'update';
        }

        detail.value = val;
        detail.target = prop ? this[prop] : this;

        if (preventDispatch !== false) {
            // TODO: replace model2 stub with actual model
            // if (this._model)
            model.dispatch(prop || 'modelUpdate', detail);
        }

        return this.get(prop);
    };

    Object.assign(DataObj.prototype, {});

    DataObj.prototype.constructor = DataObj;

    // define special storage types: dataArray, dataObject, dataCollection, dataModel
/*    var dataModel = function (mixins) {
        var model = Object.create({
            isDataModel: true,
            modelName: '',
            from: function (tableName) {
                return this[tableName];
            },
            add: function (tableName, data) {
                if (this[tableName]) {
                    // throw an error and return something (the error?)
                } else {

                }
            }
        }, mixins);
    };*/

    var _storage = {
        /*
           'amazon': {    // model or DB name
             'books': {   // collection or table name
                'j$_validate': {    // dataObjects within the 'books' collection will use these to validate
                    'book-id': /[\d\w\-]+/i,
                    'book-title': /[\w\d\-_\s'\.\?!&]{2,60}/i,
                    'book-price': /^\$\d{1,9}\.\d{2}$/i
                },
                    // the data structure looks like this... or it can be further nested in [sub] collections
                    // unique keys are automatically generated for a plain array of objects
                'CF10239843-fr987': {   // SQLish dataObject => SQL primary key val or Object name
                    'data': {
                        'book-id': 'CF10239843-fr987',    // SQL row name or Object property
                        'book-title': 'One Flew Over the Cuckoo\'s Nest',
                        'book-price': '$15.00'
                    }
                },
                '555-abc-12-us54323': {
                    'data': {
                        'book-id': '555-abc-12-us54323',
                        'book-title': 'Tommy Knockers',
                        'book-price': '$29.95'
                    }
                },
                'KF0009878US': {
                    'data': {
                        'book-id': 'KF0009878US',
                        'book-title': 'One Fish Two Fish Red Fish Blue Fish',
                        'book-price': '$29.95'
                    }
                },
                'j$_objectArray': [   // dataArray created by SQLish! => pointers to the objects above
                    {CF10239843-fr987}, {555-abc-12-us54323}, {KF0009878US}
                ]
             }
           }
        */
    };

    return {model: model, model2: model2};
});<commit_msg>Added tests for DataObjects and synthetic model events (provided by eventHub.js)<commit_after>/**
 * Copyright (c) 2015 Scott Bishop
 * BluJagu, LLC - www.blujagu.com
 * MIT License (MIT) - This header must remain intact.
 **/
define(['./eventHub'], function(eventHub) {
    'use strict';

    var model = {}; // stub for model

    eventHub.connect(model);

    // create some new javaScript Object types :)
    var DataObj = window.DataObject = function DataObject () {
        this.value = undefined;
        this._model = undefined;
    };

    DataObj.prototype._initialized = false;

    DataObj.prototype.nodeFromPath = function (path, create) {
        var paths = path.split('.'),
            prop = '',
            node = this.value,
            cache = this;

        for (; paths.length > 0;) {
            prop = paths.shift();
            // do we have a node?
            if (node && node[prop]) {
                node = node[prop];
            } else if (create) {
                if (cache instanceof DataObject) {
                    cache.set({}, prop, false);
                    node = cache.value[prop];
                } else {
                    cache[prop] = {};
                    node = cache[prop];
                }
                cache = node;
            }
        }

        return node;
    };

    DataObj.prototype.get = function (prop, preventExecution) {
        var result = prop ? this.nodeFromPath(prop, false) : this.value;

        if (typeof result === 'function' && !preventExecution) {
            result = result(this);
        }

        return result;
    };

    // TODO: create setMany method => accepts an object and translates property/values into set calls
    DataObj.prototype.set = function (val, prop, preventDispatch) {
        var detail = {},
            path = '';

        if (prop) {
            if (prop.indexOf('.') !== -1) {
                path = prop.split('.');
                path.pop();
                path = path.join('.');
                this.nodeFromPath(path, true)[prop] = val;
            } else {
                if (this.value === undefined) {
                    this.value = {};
                }
                this.value[prop] = val;
            }
        } else {
            this.value = val;
        }

        if (!this._initialized) {
            this._initialized = true;
            detail.type = 'add';
        } else {
            detail.type = 'update';
        }

        detail.value = val;
        detail.target = prop ? this[prop] : this;

        if (preventDispatch !== false) {
            // TODO: replace model2 stub with actual model
            // if (this._model)
            model.dispatch(prop || 'modelUpdate', detail);
        }

        return this.get(prop);
    };

    Object.assign(DataObj.prototype, {});

    DataObj.prototype.constructor = DataObj;

    // define special storage types: dataArray, dataObject, dataCollection, dataModel
/*    var dataModel = function (mixins) {
        var model = Object.create({
            isDataModel: true,
            modelName: '',
            from: function (tableName) {
                return this[tableName];
            },
            add: function (tableName, data) {
                if (this[tableName]) {
                    // throw an error and return something (the error?)
                } else {

                }
            }
        }, mixins);
    };*/

    var _storage = {
        /*
           'amazon': {    // model or DB name
             'books': {   // collection or table name
                'j$_validate': {    // dataObjects within the 'books' collection will use these to validate
                    'book-id': /[\d\w\-]+/i,
                    'book-title': /[\w\d\-_\s'\.\?!&]{2,60}/i,
                    'book-price': /^\$\d{1,9}\.\d{2}$/i
                },
                    // the data structure looks like this... or it can be further nested in [sub] collections
                    // unique keys are automatically generated for a plain array of objects
                'CF10239843-fr987': {   // SQLish dataObject => SQL primary key val or Object name
                    'data': {
                        'book-id': 'CF10239843-fr987',    // SQL row name or Object property
                        'book-title': 'One Flew Over the Cuckoo\'s Nest',
                        'book-price': '$15.00'
                    }
                },
                '555-abc-12-us54323': {
                    'data': {
                        'book-id': '555-abc-12-us54323',
                        'book-title': 'Tommy Knockers',
                        'book-price': '$29.95'
                    }
                },
                'KF0009878US': {
                    'data': {
                        'book-id': 'KF0009878US',
                        'book-title': 'One Fish Two Fish Red Fish Blue Fish',
                        'book-price': '$29.95'
                    }
                },
                'j$_objectArray': [   // dataArray created by SQLish! => pointers to the objects above
                    {CF10239843-fr987}, {555-abc-12-us54323}, {KF0009878US}
                ]
             }
           }
        */
    };

    return {model: model};
});<|endoftext|>


<commit_before>var Layer = require('./Layer');
var Kit = require('./Kit');
var Params = require('./Params');
var BufferHelper = require('./BufferHelper');
var sampleParams = require('./mixins/sampleParams');
var uniqueId = require('./utils/uniqueId');
var slotIds = 'QWERTYUIOPASDFGHJKLZXCVBNM';

var buffers = {};
var loading = {};

var Sample = Layer.extend({

  type: 'sample',

  props: {
    loaded: ['boolean', true, false],
    sliceExpression: 'string',
    buffer: 'any'
  },

  constructor: function (src, params, options) {
    if (typeof src === 'string') {
      params = params || {};
      params.src = src;
    }
    else {
      options = params;
      params = src;
    }
    Layer.call(this, params, options);
  },

  initialize: function (src, options) {
    Layer.prototype.initialize.call(this, src, options);
    this.on('change:src', this._loadSample);
    this.on('change:loaded', this._configureSlice);
    this.on('change:sliceExpression', this._configureSlice);
    this.src && this._loadSample();
    this._configureSlice();
  },

  _getSlotId: function (index) {
    if (index < slotIds.length) {
      return slotIds[index];
    }
    else {
      var cycles = Math.floor(index / slotIds.length);
      index -= (cycles * slotIds.length);
      return slotIds[cycles - 1] + slotIds[index];
    }
  },

  slice: function (pieces) {
    // not clean, but I'm not sure of another way of solving this circular dependency
    var kit = new this.vent.bap.kit();
    for (var i = 0; i < pieces; i++) {
      kit.slot(this._getSlotId(i)).layer(this.with({
        sliceExpression: '' + (i + 1) + '/' + pieces
      }));
    }
    return kit;
  },

  _params: function () {
    var params = Layer.prototype._params.apply(this, arguments);

    params.length = params.length || this.buffer && (this.buffer.duration - params.offset) || 0;

    var playbackRate = params.playbackRate = this._getPlaybackRate(params);

    if (!params.hasLengthFromDuration) {
      var adjustedLength = params.length / playbackRate;
      var durationLength = params.duration && Params.lengthFromDuration(params, params.duration);

      if (durationLength && durationLength < adjustedLength) {
        params.hasLengthFromDuration = true;
        params.length = durationLength;
      }
    }

    return params;
  },

  _source: function (params) {
    if (!this.src) {
      throw new Error('Cannot start sample without source url');
    }
    if (!this.buffer) {
      throw new Error('Cannot start sample without loaded buffer');
    }

    var buffer = BufferHelper.makeBuffer(params, this.buffer, this.context);

    if (buffer) {
      var source = this.context.createBufferSource();
      source.buffer = buffer;

      source.playbackRate.value = params.playbackRate;

      if (params.loop > 0) {
        source.loop = true;
        source.loopStart = params.offset;
        source.loopEnd = params.offset + params.loop;
      }

      return source;
    }
  },

  _getStopTime: function (time, params, source) {
    var duration = params.hasLengthFromDuration ? source.buffer.duration : source.buffer.duration / params.playbackRate;
    return time + duration;
  },

  _getPlaybackRate: function (params) {
    return Math.pow(2, params.pitch / 12);
  },

  _loadSample: function (countLoading) {
    var buffer = buffers[this.src];
    if (!buffer) {
      this.loaded = false;

      if (loading[this.src]) {
        if (countLoading !== false) {
          loading[this.src]++;
        }
        return setTimeout(this._loadSample.bind(this, false), 10);
      }

      this.vent.trigger('loadingState:add', this.src);
      loading[this.src] = 1;

      var request = new XMLHttpRequest();
      request.open('GET', this.src, true);
      request.responseType = 'arraybuffer';
      request.onload = function soundWasLoaded () {
        this.context.decodeAudioData(request.response, function (buffer) {
          buffer.uid = uniqueId('buffer');
          this.buffer = buffers[this.src] = buffer;
          loading[this.src]--;
          this.loaded = true;
          if (!loading[this.src]) {
            this.vent.trigger('loadingState:remove', this.src);
          }
          request.onload = null;
        }.bind(this));
      }.bind(this);
      request.send();
    }
    else {
      this.buffer = buffer;
      loading[this.src]--;
      this.loaded = true;
      if (!loading[this.src]) {
        this.vent.trigger('loadingState:remove', this.src);
      }
    }
  },

  _configureRelease: function (time, params, gain) {
    if (params.trimToZeroCrossingPoint) {
      time -= BufferHelper.silentTailSize / 44100;
    }
    Layer.prototype._configureRelease.call(this, time, params, gain);
  },

  _configureSlice: function () {
    if (this.buffer && this.loaded && this.sliceExpression) {
      var length = this.buffer.duration;
      var parts = this.sliceExpression.split('/');
      var index = parseInt(parts[0], 10) - 1;
      var total = parseInt(parts[1], 10);
      var sliceLength = length / total;
      var offset = index * sliceLength;

      this.length = sliceLength;
      this.offset = offset;
    }
  }
}, sampleParams);

module.exports = Sample;
<commit_msg>Resume audio context before decoding loaded sound file<commit_after>var Layer = require('./Layer');
var Kit = require('./Kit');
var Params = require('./Params');
var BufferHelper = require('./BufferHelper');
var sampleParams = require('./mixins/sampleParams');
var uniqueId = require('./utils/uniqueId');
var slotIds = 'QWERTYUIOPASDFGHJKLZXCVBNM';

var buffers = {};
var loading = {};

var Sample = Layer.extend({

  type: 'sample',

  props: {
    loaded: ['boolean', true, false],
    sliceExpression: 'string',
    buffer: 'any'
  },

  constructor: function (src, params, options) {
    if (typeof src === 'string') {
      params = params || {};
      params.src = src;
    }
    else {
      options = params;
      params = src;
    }
    Layer.call(this, params, options);
  },

  initialize: function (src, options) {
    Layer.prototype.initialize.call(this, src, options);
    this.on('change:src', this._loadSample);
    this.on('change:loaded', this._configureSlice);
    this.on('change:sliceExpression', this._configureSlice);
    this.src && this._loadSample();
    this._configureSlice();
  },

  _getSlotId: function (index) {
    if (index < slotIds.length) {
      return slotIds[index];
    }
    else {
      var cycles = Math.floor(index / slotIds.length);
      index -= (cycles * slotIds.length);
      return slotIds[cycles - 1] + slotIds[index];
    }
  },

  slice: function (pieces) {
    // not clean, but I'm not sure of another way of solving this circular dependency
    var kit = new this.vent.bap.kit();
    for (var i = 0; i < pieces; i++) {
      kit.slot(this._getSlotId(i)).layer(this.with({
        sliceExpression: '' + (i + 1) + '/' + pieces
      }));
    }
    return kit;
  },

  _params: function () {
    var params = Layer.prototype._params.apply(this, arguments);

    params.length = params.length || this.buffer && (this.buffer.duration - params.offset) || 0;

    var playbackRate = params.playbackRate = this._getPlaybackRate(params);

    if (!params.hasLengthFromDuration) {
      var adjustedLength = params.length / playbackRate;
      var durationLength = params.duration && Params.lengthFromDuration(params, params.duration);

      if (durationLength && durationLength < adjustedLength) {
        params.hasLengthFromDuration = true;
        params.length = durationLength;
      }
    }

    return params;
  },

  _source: function (params) {
    if (!this.src) {
      throw new Error('Cannot start sample without source url');
    }
    if (!this.buffer) {
      throw new Error('Cannot start sample without loaded buffer');
    }

    var buffer = BufferHelper.makeBuffer(params, this.buffer, this.context);

    if (buffer) {
      var source = this.context.createBufferSource();
      source.buffer = buffer;

      source.playbackRate.value = params.playbackRate;

      if (params.loop > 0) {
        source.loop = true;
        source.loopStart = params.offset;
        source.loopEnd = params.offset + params.loop;
      }

      return source;
    }
  },

  _getStopTime: function (time, params, source) {
    var duration = params.hasLengthFromDuration ? source.buffer.duration : source.buffer.duration / params.playbackRate;
    return time + duration;
  },

  _getPlaybackRate: function (params) {
    return Math.pow(2, params.pitch / 12);
  },

  _loadSample: function (countLoading) {
    var buffer = buffers[this.src];
    this.context.resume();
    if (!buffer) {
      this.loaded = false;

      if (loading[this.src]) {
        if (countLoading !== false) {
          loading[this.src]++;
        }
        return setTimeout(this._loadSample.bind(this, false), 10);
      }

      this.vent.trigger('loadingState:add', this.src);
      loading[this.src] = 1;

      var request = new XMLHttpRequest();
      request.open('GET', this.src, true);
      request.responseType = 'arraybuffer';
      request.onload = function soundWasLoaded () {
        this.context.decodeAudioData(request.response, function (buffer) {
          buffer.uid = uniqueId('buffer');
          this.buffer = buffers[this.src] = buffer;
          loading[this.src]--;
          this.loaded = true;
          if (!loading[this.src]) {
            this.vent.trigger('loadingState:remove', this.src);
          }
          request.onload = null;
        }.bind(this));
      }.bind(this);
      request.send();
    }
    else {
      this.buffer = buffer;
      loading[this.src]--;
      this.loaded = true;
      if (!loading[this.src]) {
        this.vent.trigger('loadingState:remove', this.src);
      }
    }
  },

  _configureRelease: function (time, params, gain) {
    if (params.trimToZeroCrossingPoint) {
      time -= BufferHelper.silentTailSize / 44100;
    }
    Layer.prototype._configureRelease.call(this, time, params, gain);
  },

  _configureSlice: function () {
    if (this.buffer && this.loaded && this.sliceExpression) {
      var length = this.buffer.duration;
      var parts = this.sliceExpression.split('/');
      var index = parseInt(parts[0], 10) - 1;
      var total = parseInt(parts[1], 10);
      var sliceLength = length / total;
      var offset = index * sliceLength;

      this.length = sliceLength;
      this.offset = offset;
    }
  }
}, sampleParams);

module.exports = Sample;
<|endoftext|>


<commit_before>var angular_multi_select = angular.module('angular-multi-select', [
	'angular-multi-select-engine',
	'angular-multi-select-constants',
	'angular-multi-select-styles-helper',
	'angular-multi-select-data-converter'
]);

angular_multi_select.directive('angularMultiSelect', [
	'$window',
	'$timeout',
	'angularMultiSelectEngine',
	'angularMultiSelectConstants',
	'angularMultiSelectStylesHelper',
	'angularMultiSelectDataConverter',
	function ($window, $timeout, angularMultiSelectEngine, angularMultiSelectConstants, angularMultiSelectStylesHelper, angularMultiSelectDataConverter) {
		return {
			restrict: 'AE',

			scope: {
				inputModel: '=',
				outputModel: '='
			},

			templateUrl: '../src/angular-multi-select.tpl',

			link: function ($scope, element, attrs) {
				var el_attrs = element[0].attributes;
				var el_attrs_vals = Object.keys(el_attrs).map((key) => el_attrs[key]);

				/*
				 █████  ████████ ████████ ██████  ██ ██████  ██    ██ ████████ ███████ ███████
				██   ██    ██       ██    ██   ██ ██ ██   ██ ██    ██    ██    ██      ██
				███████    ██       ██    ██████  ██ ██████  ██    ██    ██    █████   ███████
				██   ██    ██       ██    ██   ██ ██ ██   ██ ██    ██    ██    ██           ██
				██   ██    ██       ██    ██   ██ ██ ██████   ██████     ██    ███████ ███████
				*/
				/*
				* Find out what are the properties names of the important bits
				* of the input data.
				*/
				var ops = {
					DEBUG             : attrs.debug            === "true" ? true : false,
					ID_PROPERTY       : attrs.idProperty       || angularMultiSelectConstants.ID_PROPERTY,
					OPEN_PROPERTY     : attrs.openProperty     || angularMultiSelectConstants.OPEN_PROPERTY,
					CHECKED_PROPERTY  : attrs.checkedProperty  || angularMultiSelectConstants.CHECKED_PROPERTY,
					CHILDREN_PROPERTY : attrs.childrenProperty || angularMultiSelectConstants.CHILDREN_PROPERTY,
				};

				/*
				 * Find out if the input data should be threated in some special way.
				 */
				this.do_not_check_data   = attrs.doNotCheckData   === "true" ? true : false;
				this.do_not_convert_data = attrs.doNotConvertData === "true" ? true : false;

				/*
				 * Find out if the output data should be converted in some special way.
				 */
				this.output_type = attrs.outputType === undefined ? 'objects' : attrs.outputType;

				/*
				 * Find out which field to use for the 'search' functionality.
				 */
				this.search_field = attrs.searchField === undefined ? null : attrs.searchField;

				/*
				 █████  ███    ███ ███████      ██████  ██████       ██ ███████  ██████ ████████ ███████
				██   ██ ████  ████ ██          ██    ██ ██   ██      ██ ██      ██         ██    ██
				███████ ██ ████ ██ ███████     ██    ██ ██████       ██ █████   ██         ██    ███████
				██   ██ ██  ██  ██      ██     ██    ██ ██   ██ ██   ██ ██      ██         ██         ██
				██   ██ ██      ██ ███████      ██████  ██████   █████  ███████  ██████    ██    ███████
				*/
				var amse = new angularMultiSelectEngine(ops);
				var amssh = new angularMultiSelectStylesHelper(ops, attrs);
				var amsdc = new angularMultiSelectDataConverter(ops);
				$scope.amse = amse;
				$scope.amssh = amssh;

				/*
				██    ██ ██ ███████ ██ ██████  ██ ██      ██ ████████ ██    ██
				██    ██ ██ ██      ██ ██   ██ ██ ██      ██    ██     ██  ██
				██    ██ ██ ███████ ██ ██████  ██ ██      ██    ██      ████
				 ██  ██  ██      ██ ██ ██   ██ ██ ██      ██    ██       ██
				  ████   ██ ███████ ██ ██████  ██ ███████ ██    ██       ██
				*/
				$scope.open = false;
				$window.onclick = function (event) {
					if (!event.target) {
						return;
					}

					var p = angular.element(event.target).parent();
					while (p.length > 0) {
						if (
							p[0].className !== undefined &&
							(
								p.hasClass("ams-container") ||
								p.hasClass("ams-button") ||
								p.hasClass("ams")
							)
						) {
							return;
						}
						p = p.parent();
					}

					$scope.open = false;
					$scope.$apply();
				};
				//$scope.$watch('open', function () {

				//});

				/*
				██   ██ ███████ ██      ██████  ███████ ██████  ███████
				██   ██ ██      ██      ██   ██ ██      ██   ██ ██
				███████ █████   ██      ██████  █████   ██████  ███████
				██   ██ ██      ██      ██      ██      ██   ██      ██
				██   ██ ███████ ███████ ██      ███████ ██   ██ ███████
				*/
				/*
				 * The 'reset_model' will be filled in with the first available
				 * data from the input model and will be used when the 'reset'
				 * function is triggered.
				 */
				$scope.reset_model = null;
				$scope.check_all   = function () { amse.check_all(); };
				$scope.check_none  = function () { amse.uncheck_all(); };
				$scope.reset       = function () {
					amse.insert($scope.reset_model);
					$scope.items = amse.get_visible_tree();
				};

				/*
				███████ ███████  █████  ██████   ██████ ██   ██
				██      ██      ██   ██ ██   ██ ██      ██   ██
				███████ █████   ███████ ██████  ██      ███████
				     ██ ██      ██   ██ ██   ██ ██      ██   ██
				███████ ███████ ██   ██ ██   ██  ██████ ██   ██
				*/
				$scope.search = "";
				$scope.search_promise = null;
				$scope.$watch('search', function (_new, _old) {
					if (_new === _old && _new === "") {
						return;
					}

					if($scope.searchField === null) {
						return;
					}

					/*
					 * This means that there was a search, but it was deleted
					 * and now the normal tree should be repainted.
					 */
					if (_new === "") {
						if ($scope.search_promise !== null) {
							$timeout.cancel($scope.search_promise);
						}
						$scope.items = amse.get_visible_tree();
						return;
					}

					/*
					 * If the code execution gets here, it means that there is
					 * a search that should be performed
					 */
					if ($scope.search_promise !== null) {
						$timeout.cancel($scope.search_promise);
					}

					$scope.search_promise = $timeout(function (query) {
						//TODO: this needs a lot of improving
						var filter = [];
						filter.push({
							field: this.search_field,
							query: query
						});

						$scope.items = amse.get_filtered_tree(filter);
					}, 2300, true, _new);
				});

				/*
				 ██████  ███    ██     ██████   █████  ████████  █████       ██████ ██   ██  █████  ███    ██  ██████  ███████
				██    ██ ████   ██     ██   ██ ██   ██    ██    ██   ██     ██      ██   ██ ██   ██ ████   ██ ██       ██
				██    ██ ██ ██  ██     ██   ██ ███████    ██    ███████     ██      ███████ ███████ ██ ██  ██ ██   ███ █████
				██    ██ ██  ██ ██     ██   ██ ██   ██    ██    ██   ██     ██      ██   ██ ██   ██ ██  ██ ██ ██    ██ ██
				 ██████  ██   ████     ██████  ██   ██    ██    ██   ██      ██████ ██   ██ ██   ██ ██   ████  ██████  ███████
				*/
				amse.on_data_change(function () {
					/*
					 * Will be triggered every time the internal model data is changed.
					 * That could happen on check/uncheck, for example.
					 */

					/*
					 * Get the visible tree only once. Consecutive calls on un/check
					 * will automatically propagate to the rendered tree.
					 */
					if ($scope.items === undefined) {
						$scope.items = amse.get_visible_tree();
					}

					if ($scope.outputModel !== undefined) {
						var checked_tree = amse.get_checked_tree();

						var res = checked_tree;
						switch (this.output_type) {
							case 'objects':
								res = amsdc.to_array_of_objects(checked_tree);
								break;
							case 'arrays':
								res = amsdc.to_array_of_arrays(checked_tree);
								break;
							case 'object':
								res = amsdc.to_object(checked_tree);
								break;
							case 'array':
								res = amsdc.to_array(checked_tree);
								break;
							case 'value':
								res = amsdc.to_array(checked_tree);
								break;
						}

						$scope.outputModel = res;
					}
				});

				/*
				 ██████  ███    ██     ██    ██ ██ ███████ ██    ██  █████  ██           ██████ ██   ██  █████  ███    ██  ██████  ███████
				██    ██ ████   ██     ██    ██ ██ ██      ██    ██ ██   ██ ██          ██      ██   ██ ██   ██ ████   ██ ██       ██
				██    ██ ██ ██  ██     ██    ██ ██ ███████ ██    ██ ███████ ██          ██      ███████ ███████ ██ ██  ██ ██   ███ █████
				██    ██ ██  ██ ██      ██  ██  ██      ██ ██    ██ ██   ██ ██          ██      ██   ██ ██   ██ ██  ██ ██ ██    ██ ██
				 ██████  ██   ████       ████   ██ ███████  ██████  ██   ██ ███████      ██████ ██   ██ ██   ██ ██   ████  ██████  ███████
				*/
				amse.on_visual_change(function () {
					/*
					 * Will be triggered when a change that requires a visual change happende.
					 * This is normaly on open/close actions.
					 */
					$scope.items = amse.get_visible_tree();
				});

				/*
				███    ███  █████  ██ ███    ██
				████  ████ ██   ██ ██ ████   ██
				██ ████ ██ ███████ ██ ██ ██  ██
				██  ██  ██ ██   ██ ██ ██  ██ ██
				██      ██ ██   ██ ██ ██   ████
				*/
				$scope.$watch('inputModel', function (_new, _old) {
					/*
					* The entry point of the directive. This monitors the input data and
					* decides when to populate the internal data model and how to do it.
					*/
					var checked_data  = this.do_not_check_data   ? _new         : amsdc.check_prerequisites(_new);
					var internal_data = this.do_not_convert_data ? checked_data : amsdc.to_internal(checked_data);

					if (_new !== undefined && $scope.reset_model === null) {
						$scope.reset_model = angular.copy(internal_data);
					}

					amse.insert(internal_data);
				});
			}
		};
	}
]);
<commit_msg>Wrong scope when seeking results<commit_after>var angular_multi_select = angular.module('angular-multi-select', [
	'angular-multi-select-engine',
	'angular-multi-select-constants',
	'angular-multi-select-styles-helper',
	'angular-multi-select-data-converter'
]);

angular_multi_select.directive('angularMultiSelect', [
	'$window',
	'$timeout',
	'angularMultiSelectEngine',
	'angularMultiSelectConstants',
	'angularMultiSelectStylesHelper',
	'angularMultiSelectDataConverter',
	function ($window, $timeout, angularMultiSelectEngine, angularMultiSelectConstants, angularMultiSelectStylesHelper, angularMultiSelectDataConverter) {
		return {
			restrict: 'AE',

			scope: {
				inputModel: '=',
				outputModel: '='
			},

			templateUrl: '../src/angular-multi-select.tpl',

			link: function ($scope, element, attrs) {
				var el_attrs = element[0].attributes;
				var el_attrs_vals = Object.keys(el_attrs).map((key) => el_attrs[key]);

				/*
				 █████  ████████ ████████ ██████  ██ ██████  ██    ██ ████████ ███████ ███████
				██   ██    ██       ██    ██   ██ ██ ██   ██ ██    ██    ██    ██      ██
				███████    ██       ██    ██████  ██ ██████  ██    ██    ██    █████   ███████
				██   ██    ██       ██    ██   ██ ██ ██   ██ ██    ██    ██    ██           ██
				██   ██    ██       ██    ██   ██ ██ ██████   ██████     ██    ███████ ███████
				*/
				/*
				* Find out what are the properties names of the important bits
				* of the input data.
				*/
				var ops = {
					DEBUG             : attrs.debug            === "true" ? true : false,
					ID_PROPERTY       : attrs.idProperty       || angularMultiSelectConstants.ID_PROPERTY,
					OPEN_PROPERTY     : attrs.openProperty     || angularMultiSelectConstants.OPEN_PROPERTY,
					CHECKED_PROPERTY  : attrs.checkedProperty  || angularMultiSelectConstants.CHECKED_PROPERTY,
					CHILDREN_PROPERTY : attrs.childrenProperty || angularMultiSelectConstants.CHILDREN_PROPERTY,
				};

				/*
				 * Find out if the input data should be threated in some special way.
				 */
				this.do_not_check_data   = attrs.doNotCheckData   === "true" ? true : false;
				this.do_not_convert_data = attrs.doNotConvertData === "true" ? true : false;

				/*
				 * Find out if the output data should be converted in some special way.
				 */
				this.output_type = attrs.outputType === undefined ? 'objects' : attrs.outputType;

				/*
				 * Find out which field to use for the 'search' functionality.
				 */
				this.search_field = attrs.searchField === undefined ? null : attrs.searchField;

				/*
				 █████  ███    ███ ███████      ██████  ██████       ██ ███████  ██████ ████████ ███████
				██   ██ ████  ████ ██          ██    ██ ██   ██      ██ ██      ██         ██    ██
				███████ ██ ████ ██ ███████     ██    ██ ██████       ██ █████   ██         ██    ███████
				██   ██ ██  ██  ██      ██     ██    ██ ██   ██ ██   ██ ██      ██         ██         ██
				██   ██ ██      ██ ███████      ██████  ██████   █████  ███████  ██████    ██    ███████
				*/
				var amse = new angularMultiSelectEngine(ops);
				var amssh = new angularMultiSelectStylesHelper(ops, attrs);
				var amsdc = new angularMultiSelectDataConverter(ops);
				$scope.amse = amse;
				$scope.amssh = amssh;

				/*
				██    ██ ██ ███████ ██ ██████  ██ ██      ██ ████████ ██    ██
				██    ██ ██ ██      ██ ██   ██ ██ ██      ██    ██     ██  ██
				██    ██ ██ ███████ ██ ██████  ██ ██      ██    ██      ████
				 ██  ██  ██      ██ ██ ██   ██ ██ ██      ██    ██       ██
				  ████   ██ ███████ ██ ██████  ██ ███████ ██    ██       ██
				*/
				$scope.open = false;
				$window.onclick = function (event) {
					if (!event.target) {
						return;
					}

					var p = angular.element(event.target).parent();
					while (p.length > 0) {
						if (
							p[0].className !== undefined &&
							(
								p.hasClass("ams-container") ||
								p.hasClass("ams-button") ||
								p.hasClass("ams")
							)
						) {
							return;
						}
						p = p.parent();
					}

					$scope.open = false;
					$scope.$apply();
				};
				//$scope.$watch('open', function () {

				//});

				/*
				██   ██ ███████ ██      ██████  ███████ ██████  ███████
				██   ██ ██      ██      ██   ██ ██      ██   ██ ██
				███████ █████   ██      ██████  █████   ██████  ███████
				██   ██ ██      ██      ██      ██      ██   ██      ██
				██   ██ ███████ ███████ ██      ███████ ██   ██ ███████
				*/
				/*
				 * The 'reset_model' will be filled in with the first available
				 * data from the input model and will be used when the 'reset'
				 * function is triggered.
				 */
				$scope.reset_model = null;
				$scope.check_all   = function () { amse.check_all(); };
				$scope.check_none  = function () { amse.uncheck_all(); };
				$scope.reset       = function () {
					amse.insert($scope.reset_model);
					$scope.items = amse.get_visible_tree();
				};

				/*
				███████ ███████  █████  ██████   ██████ ██   ██
				██      ██      ██   ██ ██   ██ ██      ██   ██
				███████ █████   ███████ ██████  ██      ███████
				     ██ ██      ██   ██ ██   ██ ██      ██   ██
				███████ ███████ ██   ██ ██   ██  ██████ ██   ██
				*/
				$scope.search = "";
				$scope.search_promise = null;
				$scope.$watch('search', function (_new, _old) {
					if (_new === _old && _new === "") {
						return;
					}

					if($scope.searchField === null) {
						return;
					}

					/*
					 * This means that there was a search, but it was deleted
					 * and now the normal tree should be repainted.
					 */
					if (_new === "") {
						if ($scope.search_promise !== null) {
							$timeout.cancel($scope.search_promise);
						}
						$scope.items = amse.get_visible_tree();
						return;
					}

					/*
					 * If the code execution gets here, it means that there is
					 * a search that should be performed
					 */
					if ($scope.search_promise !== null) {
						$timeout.cancel($scope.search_promise);
					}

					$scope.search_promise = $timeout(function (query) {
						//TODO: this needs a lot of improving
						var filter = [];
						filter.push({
							field: this.search_field,
							query: query
						});

						$scope.items = amse.get_filtered_tree(filter);
					}, 2300, true, _new);
				});

				/*
				 ██████  ███    ██     ██████   █████  ████████  █████       ██████ ██   ██  █████  ███    ██  ██████  ███████
				██    ██ ████   ██     ██   ██ ██   ██    ██    ██   ██     ██      ██   ██ ██   ██ ████   ██ ██       ██
				██    ██ ██ ██  ██     ██   ██ ███████    ██    ███████     ██      ███████ ███████ ██ ██  ██ ██   ███ █████
				██    ██ ██  ██ ██     ██   ██ ██   ██    ██    ██   ██     ██      ██   ██ ██   ██ ██  ██ ██ ██    ██ ██
				 ██████  ██   ████     ██████  ██   ██    ██    ██   ██      ██████ ██   ██ ██   ██ ██   ████  ██████  ███████
				*/
				amse.on_data_change(() => {
					/*
					 * Will be triggered every time the internal model data is changed.
					 * That could happen on check/uncheck, for example.
					 */

					/*
					 * Get the visible tree only once. Consecutive calls on un/check
					 * will automatically propagate to the rendered tree.
					 */
					if ($scope.items === undefined) {
						$scope.items = amse.get_visible_tree();
					}

					if ($scope.outputModel !== undefined) {
						var checked_tree = amse.get_checked_tree();

						var res = checked_tree;
						switch (this.output_type) {
							case 'objects':
								res = amsdc.to_array_of_objects(checked_tree);
								break;
							case 'arrays':
								res = amsdc.to_array_of_arrays(checked_tree);
								break;
							case 'object':
								res = amsdc.to_object(checked_tree);
								break;
							case 'array':
								res = amsdc.to_array(checked_tree);
								break;
							case 'value':
								res = amsdc.to_array(checked_tree);
								break;
						}

						$scope.outputModel = res;
					}
				});

				/*
				 ██████  ███    ██     ██    ██ ██ ███████ ██    ██  █████  ██           ██████ ██   ██  █████  ███    ██  ██████  ███████
				██    ██ ████   ██     ██    ██ ██ ██      ██    ██ ██   ██ ██          ██      ██   ██ ██   ██ ████   ██ ██       ██
				██    ██ ██ ██  ██     ██    ██ ██ ███████ ██    ██ ███████ ██          ██      ███████ ███████ ██ ██  ██ ██   ███ █████
				██    ██ ██  ██ ██      ██  ██  ██      ██ ██    ██ ██   ██ ██          ██      ██   ██ ██   ██ ██  ██ ██ ██    ██ ██
				 ██████  ██   ████       ████   ██ ███████  ██████  ██   ██ ███████      ██████ ██   ██ ██   ██ ██   ████  ██████  ███████
				*/
				amse.on_visual_change(function () {
					/*
					 * Will be triggered when a change that requires a visual change happende.
					 * This is normaly on open/close actions.
					 */
					$scope.items = amse.get_visible_tree();
				});

				/*
				███    ███  █████  ██ ███    ██
				████  ████ ██   ██ ██ ████   ██
				██ ████ ██ ███████ ██ ██ ██  ██
				██  ██  ██ ██   ██ ██ ██  ██ ██
				██      ██ ██   ██ ██ ██   ████
				*/
				$scope.$watch('inputModel', function (_new, _old) {
					/*
					* The entry point of the directive. This monitors the input data and
					* decides when to populate the internal data model and how to do it.
					*/
					var checked_data  = this.do_not_check_data   ? _new         : amsdc.check_prerequisites(_new);
					var internal_data = this.do_not_convert_data ? checked_data : amsdc.to_internal(checked_data);

					if (_new !== undefined && $scope.reset_model === null) {
						$scope.reset_model = angular.copy(internal_data);
					}

					amse.insert(internal_data);
				});
			}
		};
	}
]);
<|endoftext|>


<commit_before>require('enyo');

var
	kind = require('./kind'),
	platform = require('./platform'),
	dispatcher = require('./dispatcher');
var
	Control = require('./Control'),
	Option = require('./Option');

/**
* {@link enyo.Select} implements an HTML [selection]{@glossary select} widget, using
* {@link enyo.Option} instances by default.
*
* ```
* {kind: 'Select', onchange: 'selectChanged', components: [
*	{content: 'Descending', value: 'd'},
*	{content: 'Ascending', value: 'a'}
* ]}
*
* selectChanged: function(inSender, inEvent) {
*	var s = inSender.getValue();
*	if (s == 'd') {
*		this.sortListDescending();
*	} else {
*		this.sortListAscending();
*	}
* }
* ```
*
* Note: This uses the [<select>]{@glossary select} tag, which isn't implemented for
* native webOS applications, although it does work in the webOS Web browser.
*
* @class enyo.Select
* @extends enyo.Control
* @ui
* @public
*/
module.exports = kind(
	/** @lends enyo.Select.prototype */ {

	/**
	* @private
	*/
	name: 'enyo.Select',

	/**
	* @private
	*/
	kind: Control,

	/**
	* @private
	*/
	published:
		/** @lends enyo.Select.prototype */ {

		/**
		* The index of the selected [option]{@link enyo.Option} in the list.
		*
		* @type {Number}
		* @default 0
		* @public
		*/
		selected: 0,

		/**
		* The value of the selected [option]{@link enyo.Option}.
		*
		* @type {Object}
		* @default null
		* @public
		*/
		value: null
	},

	/**
	* @private
	*/
	handlers: {
		onchange: 'change'
	},

	/**
	* @private
	*/
	tag: 'select',

	/**
	* @private
	*/
	defaultKind: Option,

	/**
	* @method
	* @private
	*/
	rendered: kind.inherit(function (sup) {
		return function() {
			sup.apply(this, arguments);
			//Trick to force IE8 onchange event bubble
			if(platform.ie == 8){
				this.setAttribute('onchange', dispatcher.bubbler);
			}
			this.change();
			this.selectedChanged();
		};
	}),

	/**
	* @private
	*/
	getSelected: function () {
		return Number(this.getNodeProperty('selectedIndex', this.selected));
	},

	/**
	* @private
	*/
	selectedChanged: function () {
		this.setNodeProperty('selectedIndex', this.selected);
	},

	/**
	* @private
	*/
	change: function () {
		this.selected = this.getSelected();
		if (this.hasNode()) {
			this.set('value', this.node.value);
		}
	},

	/**
	* @method
	* @private
	*/
	render: kind.inherit(function (sup) {
		return function() {
			// work around IE bug with innerHTML setting of <select>, rerender parent instead
			// http://support.microsoft.com/default.aspx?scid=kb;en-us;276228
			if (platform.ie) {
				this.parent.render();
			} else {
				sup.apply(this, arguments);
			}
		};
	})
});<commit_msg>index 30816bb..5fcf923 100644 --- a/source/ui/Select.js +++ b/source/ui/Select.js<commit_after>require('enyo');

var
	kind = require('./kind'),
	platform = require('./platform'),
	dispatcher = require('./dispatcher');
var
	Control = require('./Control'),
	Option = require('./Option');

/**
* {@link enyo.Select} implements an HTML [selection]{@glossary select} widget, using
* {@link enyo.Option} instances by default.
*
* ```
* {kind: 'Select', onchange: 'selectChanged', components: [
*	{content: 'Descending', value: 'd'},
*	{content: 'Ascending', value: 'a'}
* ]}
*
* selectChanged: function(inSender, inEvent) {
*	var s = inSender.getValue();
*	if (s == 'd') {
*		this.sortListDescending();
*	} else {
*		this.sortListAscending();
*	}
* }
* ```
*
* Note: This uses the [<select>]{@glossary select} tag, which isn't implemented for
* native webOS applications, although it does work in the webOS Web browser.
*
* @class enyo.Select
* @extends enyo.Control
* @ui
* @public
*/
module.exports = kind(
	/** @lends enyo.Select.prototype */ {

	/**
	* @private
	*/
	name: 'enyo.Select',

	/**
	* @private
	*/
	kind: Control,

	/**
	* @private
	*/
	published:
		/** @lends enyo.Select.prototype */ {

		/**
		* The index of the selected [option]{@link enyo.Option} in the list.
		*
		* @type {Number}
		* @default 0
		* @public
		*/
		selected: 0,

		/**
		* The value of the selected [option]{@link enyo.Option}.
		*
		* @type {Object}
		* @default null
		* @public
		*/
		value: null,

		/**
		* The size of the select box in rows.
		*
		* @type {Number}
		* @default 0
		* @public
		*/
		size: 0,

		/**
		* Sets whether the enyo.Select can select multiple options
		*
		* @type {Boolean}
		* @default false
		* @public
		*/
		multiple: false,

		/**
		* Sets whether the enyo.Select is disabled, or not
		*
		* @type {Boolean}
		* @default false
		* @public
		*/
		disabled: false
	},

	/**
	* @private
	*/
	handlers: {
		onchange: 'change'
	},

	/**
	* @private
	*/
	tag: 'select',

	/**
	* @private
	*/
	defaultKind: Option,

	/**
	* @method
	* @private
	*/
	rendered: kind.inherit(function (sup) {
		return function() {
			sup.apply(this, arguments);
			//Trick to force IE8 onchange event bubble
			if(platform.ie == 8){
				this.setAttribute('onchange', dispatcher.bubbler);
			}
			this.selectedChanged();
			this.valueChanged();
			this.sizeChanged();
			this.multipleChanged();
			this.disabledChanged();
		};
	}),

	/**
	* @private
	*/
	getSelected: function () {
		return Number(this.getNodeProperty('selectedIndex', this.selected));
	},

	/**
	* @private
	*/
	selectedChanged: function () {
		this.setNodeProperty('selectedIndex', this.selected);
		this.set('value', this.getNodeProperty('value', this.value));
	},
	/**
	* @private
	*/
	valueChanged: function() {
		this.setNodeProperty('value', this.value);
		this.set('selected', this.getSelected());
	},
	/**
	* @private
	*/
	sizeChanged: function() {
		this.setNodeProperty('size', this.size);
	},
	/**
	* @private
	*/
	multipleChanged: function() {
		this.setNodeProperty('multiple', this.multiple);
	},
	/**
	* @private
	*/
	disabledChanged: function() {
		this.setNodeProperty('disabled', this.disabled);
	},

	/**
	* @private
	*/
	change: function () {
		this.set('selected', this.getSelected());
	},

	/**
	* @method
	* @private
	*/
	render: kind.inherit(function (sup) {
		return function() {
			// work around IE bug with innerHTML setting of <select>, rerender parent instead
			// http://support.microsoft.com/default.aspx?scid=kb;en-us;276228
			if (platform.ie) {
				this.parent.render();
			} else {
				sup.apply(this, arguments);
			}
		};
	})
});<|endoftext|>


<commit_before>'use strict';

angular.module('ui.dashboard', ['ui.bootstrap', 'ui.sortable']);

angular.module('ui.dashboard')
  .factory('DashboardState', ['WidgetModel', function (WidgetModel) {
      function DashboardState(useLocalStorage, widgetDefinitions) {
        this.useLocalStorage = !!useLocalStorage;
        this.widgetDefinitions = widgetDefinitions;
      }
      DashboardState.prototype = {
        // Takes array of widgets, serializes, and saves state.
        // (currently stored in localStorage)
        save: function (lsKey, widgets) {
          console.log('saving dash state');
          if (!this.useLocalStorage) {
            return true;
          }
          if (arguments.length === 1) {
            widgets = lsKey;
            lsKey = 'default';
          }
          var serialized = _.map(widgets, function(widget) {
            var widgetObject = {};
            _.each(['title', 'name', 'style'], function(key) {
              widgetObject[key] = widget[key];
            });
            return widgetObject;
          });
          serialized = JSON.stringify(serialized);
          localStorage.setItem('widgets.' + lsKey, serialized);
          return true;
        },

        // Returns array of instantiated widget objects
        load: function (key) {

          if (!this.useLocalStorage) {
            return true;
          }

          var serialized, deserialized, result = [];
          key = key || 'default';

          // try loading localStorage item
          if (!(serialized = localStorage.getItem('widgets.' + key))) {
            return false;
          }

          try { // to deserialize the string
            deserialized = JSON.parse(serialized);
          } catch (e) {
            // bad JSON, clear localStorage
            localStorage.removeItem('widgets.' + key);
            return false;
          }

          // instantiate widgets from stored data
          for (var i = 0; i < deserialized.length; i++) {

            // deserialized object
            var widgetObject = deserialized[i];
            // widget definition to use
            var widgetDefinition = _.find(this.widgetDefinitions, function(def) {
              return def.name === widgetObject.name;
            });
            // check for no widget
            if (!widgetDefinition) {
              // no widget definition found, remove and return false
              localStorage.removeItem('widgets.' + key);
              return false;
            }

            // push instantiated widget to result array
            result.push(new WidgetModel(widgetDefinition,widgetObject));
          }

          return result;
        }
      };
      return DashboardState;
    }])
  .factory('WidgetDataSource', function () {
    function WidgetDataSource() {
    }

    WidgetDataSource.prototype = {
      setup: function (widget, scope) {
        this.dataAttrName = widget.dataAttrName;
        this.dataSourceOptions = widget.dataSourceOptions;
        this.widgetScope = scope;
      },

      updateScope: function (data) {
        this.widgetScope.widgetData = data;
      },

      init: function () {
        // to be overridden by subclasses
      },

      destroy: function () {
        // to be overridden by subclasses
      }
    };

    return WidgetDataSource;
  })
  .factory('WidgetModel', function () {

    // constructor for widget model instances
    function WidgetModel(Class, overrides) {
      overrides = overrides || {};
      angular.extend(this, {
        title: 'Widget',
        name: Class.name,
        attrs: Class.attrs,
        dataAttrName: Class.dataAttrName,
        ds: Class.ds,
        dataSourceType: Class.dataSourceType,
        dataSource: Class.dataSource,
        style: Class.style
      }, overrides);
      this.style = this.style || { width: '33%' };
      this.setWidth(this.style.width);

      if (Class.templateUrl) {
        this.templateUrl = Class.templateUrl;
      } else if (Class.template) {
        this.template = Class.template;
      } else {
        var directive = Class.directive || Class.name;
        this.directive = directive;
      }
    }

    WidgetModel.prototype = {

      // sets the width (and widthUnits)
      setWidth: function (width, units) {
        width = width.toString();
        units = units || width.replace(/^[-\.\d]+/, '') || '%';
        this.widthUnits = units;
        width = parseFloat(width);

        if (width < 0) {
          return false;
        }

        if (units === '%') {
          width = Math.min(100, width);
          width = Math.max(0, width);
        }
        this.style.width = width + '' + units;
        return true;
      }

    };

    return WidgetModel;
  })
  .controller('WidgetDialogCtrl', function ($scope, $modalInstance, widget, optionsTemplateUrl) {
    // add widget to scope
    $scope.widget = widget;

    // set up result object
    $scope.result = {
      title: widget.title
    };

    // look for optionsTemplateUrl on widget
    $scope.optionsTemplateUrl = optionsTemplateUrl || 'template/widget-default-content.html';

    $scope.ok = function () {
      $modalInstance.close($scope.result);
    };

    $scope.cancel = function () {
      $modalInstance.dismiss('cancel');
    };
  })
  .directive('dashboard', ['WidgetModel', '$modal', 'DashboardState', function (WidgetModel, $modal, DashboardState) {
    return {
      restrict: 'A',
      templateUrl: 'template/dashboard.html',
      scope: true,
      controller: function ($scope) {
        $scope.sortableOptions = {
          stop: function () {
            //TODO store active widgets in local storage on add/remove/reorder
            $scope.dashboardState.save($scope.widgets);
          },
          handle: '.widget-header'
        };
      },
      link: function (scope, element, attrs) {
        scope.options = scope.$eval(attrs.dashboard);
        var count = 1;
        var dashboardState = scope.dashboardState = new DashboardState(
          // !!scope.options.useLocalStorage,
          true,
          scope.options.defaultWidgets
        );

        scope.addWidget = function (widgetDef) {
          var widget = new WidgetModel(widgetDef, {
            title: 'Widget ' + count++
          });

          scope.widgets.push(widget);
          scope.saveDashboard();
        };

        scope.removeWidget = function (widget) {
          scope.widgets.splice(_.indexOf(scope.widgets, widget), 1);
          scope.saveDashboard();
        };

        scope.openWidgetDialog = function (widget) {
          var options = widget.editModalOptions;

          // use default options when none are supplied by widget
          if (!options) {
            options = {
              templateUrl: 'template/widget-template.html',
              resolve: {
                widget: function () {
                  return widget;
                },
                optionsTemplateUrl: function () {
                  return scope.options.optionsTemplateUrl;
                }
              },
              controller: 'WidgetDialogCtrl'
            };
          }
          var modalInstance = $modal.open(options);

          // Set resolve and reject callbacks for the result promise
          modalInstance.result.then(
            function (result) {
              console.log('widget dialog closed');
              console.log('result: ', result);
              widget.title = result.title;
            },
            function (reason) {
              console.log('widget dialog dismissed: ', reason);

            }
          );

        };

        scope.clear = function () {
          scope.widgets = [];
        };

        scope.addWidgetInternal = function (event, widgetDef) {
          event.preventDefault();
          scope.addWidget(widgetDef);
        };

        scope.saveDashboard = function () {
          dashboardState.save(scope.widgets);
        };

        scope.resetWidgetsToDefault = function () {
          scope.clear();
          _.each(scope.options.defaultWidgets, function (widgetDef) {
            scope.addWidget(widgetDef);
          });
        };

        // Set default widgets array
        if (!(scope.widgets = dashboardState.load())) {
          console.log('no dashboard state loaded');
          scope.resetWidgetsToDefault();
        } else {
          console.log('dashboard state loaded');
        }

        // allow adding widgets externally
        scope.options.addWidget = scope.addWidget;

      }
    };
  }
  ])
  .directive('widget', ['$compile', function ($compile) {
    function findWidgetPlaceholder(element) {
      // widget placeholder is the first (and only) child of .widget-content
      return angular.element(element.find('.widget-content').children()[0]);
    }

    return {

      link: function (scope, element) {
        // first child of .widget-content
        var elm = findWidgetPlaceholder(element);

        // the widget model/definition object
        var widget = scope.widget;

        // set up data source
        if (widget.dataSourceType) {
          //var ds = widget.ds;
          var ds = new widget.dataSourceType();
          widget.dataSource = ds;
          ds.setup(widget, scope);
          ds.init();
          scope.$on('$destroy', ds.destroy.bind(ds));
        }

        // .widget element (element is .widget-container)
        var widgetElm = element.find('.widget');

        // check for a template in widget def
        if (widget.templateUrl) {
          var includeTemplate = '

';
          var templateElm = angular.element(includeTemplate);
          elm.replaceWith(templateElm);
          elm = templateElm;
        } else if (widget.template) {
          elm.replaceWith(widget.template);
          elm = findWidgetPlaceholder(element);
        } else {
          elm.attr(widget.directive, '');

          if (widget.attrs) {
            _.each(widget.attrs, function (value, attr) {
              elm.attr(attr, value);
            });
          }

          if (widget.dataAttrName) {
            elm.attr(widget.dataAttrName, 'widgetData');
          }
        }

        scope.grabResizer = function (e) {

          // ignore middle- and right-click
          if (e.which !== 1) {
            return;
          }

          e.stopPropagation();
          e.originalEvent.preventDefault();

          // get the starting horizontal position
          var initX = e.clientX;
          // console.log('initX', initX);

          // Get the current width of the widget and dashboard
          var pixelWidth = widgetElm.width();
          var pixelHeight = widgetElm.height();
          var widgetStyleWidth = widget.style.width;
          var widthUnits = widget.widthUnits;
          var unitWidth = parseFloat(widgetStyleWidth);

          // create marquee element for resize action
          var $marquee = angular.element('

');
          widgetElm.append($marquee);

          // determine the unit/pixel ratio
          var transformMultiplier = unitWidth / pixelWidth;

          // updates marquee with preview of new width
          var mousemove = function (e) {
            var curX = e.clientX;
            var pixelChange = curX - initX;
            var newWidth = pixelWidth + pixelChange;
            $marquee.css('width', newWidth + 'px');
          };

          // sets new widget width on mouseup
          var mouseup = function (e) {
            // remove listener and marquee
            jQuery(window).off('mousemove', mousemove);
            $marquee.remove();

            // calculate change in units
            var curX = e.clientX;
            var pixelChange = curX - initX;
            var unitChange = Math.round(pixelChange * transformMultiplier * 100) / 100;

            // add to initial unit width
            var newWidth = unitWidth * 1 + unitChange;
            widget.setWidth(newWidth + widthUnits);
            scope.$apply();
          };

          jQuery(window).on('mousemove', mousemove).one('mouseup', mouseup);

        };

        // replaces widget title with input
        scope.editTitle = function (widget) {
          widget.editingTitle = true;
          // HACK: get the input to focus after being displayed.
          setTimeout(function () {
            widgetElm.find('form.widget-title input:eq(0)').focus()[0].setSelectionRange(0, 9999);
          }, 0);
        };

        // saves whatever is in the title input as the new title
        scope.saveTitleEdit = function (widget) {

          widget.editingTitle = false;
        };

        $compile(elm)(scope);
      }
    };
  }]);<commit_msg>widgets data source options<commit_after>'use strict';

angular.module('ui.dashboard', ['ui.bootstrap', 'ui.sortable']);

angular.module('ui.dashboard')
  .factory('DashboardState', ['WidgetModel', function (WidgetModel) {
      function DashboardState(useLocalStorage, widgetDefinitions) {
        this.useLocalStorage = !!useLocalStorage;
        this.widgetDefinitions = widgetDefinitions;
      }
      DashboardState.prototype = {
        // Takes array of widgets, serializes, and saves state.
        // (currently stored in localStorage)
        save: function (lsKey, widgets) {
          console.log('saving dash state');
          if (!this.useLocalStorage) {
            return true;
          }
          if (arguments.length === 1) {
            widgets = lsKey;
            lsKey = 'default';
          }
          var serialized = _.map(widgets, function(widget) {
            var widgetObject = {};
            _.each(['title', 'name', 'style'], function(key) {
              widgetObject[key] = widget[key];
            });
            return widgetObject;
          });
          serialized = JSON.stringify(serialized);
          localStorage.setItem('widgets.' + lsKey, serialized);
          return true;
        },

        // Returns array of instantiated widget objects
        load: function (key) {

          if (!this.useLocalStorage) {
            return true;
          }

          var serialized, deserialized, result = [];
          key = key || 'default';

          // try loading localStorage item
          if (!(serialized = localStorage.getItem('widgets.' + key))) {
            return false;
          }

          try { // to deserialize the string
            deserialized = JSON.parse(serialized);
          } catch (e) {
            // bad JSON, clear localStorage
            localStorage.removeItem('widgets.' + key);
            return false;
          }

          // instantiate widgets from stored data
          for (var i = 0; i < deserialized.length; i++) {

            // deserialized object
            var widgetObject = deserialized[i];
            // widget definition to use
            var widgetDefinition = _.find(this.widgetDefinitions, function(def) {
              return def.name === widgetObject.name;
            });
            // check for no widget
            if (!widgetDefinition) {
              // no widget definition found, remove and return false
              localStorage.removeItem('widgets.' + key);
              return false;
            }

            // push instantiated widget to result array
            result.push(new WidgetModel(widgetDefinition,widgetObject));
          }

          return result;
        }
      };
      return DashboardState;
    }])
  .factory('WidgetDataSource', function () {
    function WidgetDataSource() {
    }

    WidgetDataSource.prototype = {
      setup: function (widget, scope) {
        this.dataAttrName = widget.dataAttrName;
        this.dataSourceOptions = widget.dataSourceOptions;
        this.widgetScope = scope;
      },

      updateScope: function (data) {
        this.widgetScope.widgetData = data;
      },

      init: function () {
        // to be overridden by subclasses
      },

      destroy: function () {
        // to be overridden by subclasses
      }
    };

    return WidgetDataSource;
  })
  .factory('WidgetModel', function () {

    // constructor for widget model instances
    function WidgetModel(Class, overrides) {
      overrides = overrides || {};
      angular.extend(this, {
        title: 'Widget',
        name: Class.name,
        attrs: Class.attrs,
        dataAttrName: Class.dataAttrName,
        dataSourceType: Class.dataSourceType,
        dataSourceOptions: Class.dataSourceOptions,
        style: Class.style
      }, overrides);
      this.style = this.style || { width: '33%' };
      this.setWidth(this.style.width);

      if (Class.templateUrl) {
        this.templateUrl = Class.templateUrl;
      } else if (Class.template) {
        this.template = Class.template;
      } else {
        var directive = Class.directive || Class.name;
        this.directive = directive;
      }
    }

    WidgetModel.prototype = {

      // sets the width (and widthUnits)
      setWidth: function (width, units) {
        width = width.toString();
        units = units || width.replace(/^[-\.\d]+/, '') || '%';
        this.widthUnits = units;
        width = parseFloat(width);

        if (width < 0) {
          return false;
        }

        if (units === '%') {
          width = Math.min(100, width);
          width = Math.max(0, width);
        }
        this.style.width = width + '' + units;
        return true;
      }

    };

    return WidgetModel;
  })
  .controller('WidgetDialogCtrl', function ($scope, $modalInstance, widget, optionsTemplateUrl) {
    // add widget to scope
    $scope.widget = widget;

    // set up result object
    $scope.result = {
      title: widget.title
    };

    // look for optionsTemplateUrl on widget
    $scope.optionsTemplateUrl = optionsTemplateUrl || 'template/widget-default-content.html';

    $scope.ok = function () {
      $modalInstance.close($scope.result);
    };

    $scope.cancel = function () {
      $modalInstance.dismiss('cancel');
    };
  })
  .directive('dashboard', ['WidgetModel', '$modal', 'DashboardState', function (WidgetModel, $modal, DashboardState) {
    return {
      restrict: 'A',
      templateUrl: 'template/dashboard.html',
      scope: true,
      controller: function ($scope) {
        $scope.sortableOptions = {
          stop: function () {
            //TODO store active widgets in local storage on add/remove/reorder
            $scope.dashboardState.save($scope.widgets);
          },
          handle: '.widget-header'
        };
      },
      link: function (scope, element, attrs) {
        scope.options = scope.$eval(attrs.dashboard);
        var count = 1;
        var dashboardState = scope.dashboardState = new DashboardState(
          // !!scope.options.useLocalStorage,
          true,
          scope.options.defaultWidgets
        );

        scope.addWidget = function (widgetDef) {
          var widget = new WidgetModel(widgetDef, {
            title: 'Widget ' + count++
          });

          scope.widgets.push(widget);
          scope.saveDashboard();
        };

        scope.removeWidget = function (widget) {
          scope.widgets.splice(_.indexOf(scope.widgets, widget), 1);
          scope.saveDashboard();
        };

        scope.openWidgetDialog = function (widget) {
          var options = widget.editModalOptions;

          // use default options when none are supplied by widget
          if (!options) {
            options = {
              templateUrl: 'template/widget-template.html',
              resolve: {
                widget: function () {
                  return widget;
                },
                optionsTemplateUrl: function () {
                  return scope.options.optionsTemplateUrl;
                }
              },
              controller: 'WidgetDialogCtrl'
            };
          }
          var modalInstance = $modal.open(options);

          // Set resolve and reject callbacks for the result promise
          modalInstance.result.then(
            function (result) {
              console.log('widget dialog closed');
              console.log('result: ', result);
              widget.title = result.title;
            },
            function (reason) {
              console.log('widget dialog dismissed: ', reason);

            }
          );

        };

        scope.clear = function () {
          scope.widgets = [];
        };

        scope.addWidgetInternal = function (event, widgetDef) {
          event.preventDefault();
          scope.addWidget(widgetDef);
        };

        scope.saveDashboard = function () {
          dashboardState.save(scope.widgets);
        };

        scope.resetWidgetsToDefault = function () {
          scope.clear();
          _.each(scope.options.defaultWidgets, function (widgetDef) {
            scope.addWidget(widgetDef);
          });
        };

        // Set default widgets array
        if (!(scope.widgets = dashboardState.load())) {
          console.log('no dashboard state loaded');
          scope.resetWidgetsToDefault();
        } else {
          console.log('dashboard state loaded');
        }

        // allow adding widgets externally
        scope.options.addWidget = scope.addWidget;

      }
    };
  }
  ])
  .directive('widget', ['$compile', function ($compile) {
    function findWidgetPlaceholder(element) {
      // widget placeholder is the first (and only) child of .widget-content
      return angular.element(element.find('.widget-content').children()[0]);
    }

    return {

      link: function (scope, element) {
        // first child of .widget-content
        var elm = findWidgetPlaceholder(element);

        // the widget model/definition object
        var widget = scope.widget;

        // set up data source
        if (widget.dataSourceType) {
          //var ds = widget.ds;
          var ds = new widget.dataSourceType();
          widget.dataSource = ds;
          ds.setup(widget, scope);
          ds.init();
          scope.$on('$destroy', ds.destroy.bind(ds));
        }

        // .widget element (element is .widget-container)
        var widgetElm = element.find('.widget');

        // check for a template in widget def
        if (widget.templateUrl) {
          var includeTemplate = '

';
          var templateElm = angular.element(includeTemplate);
          elm.replaceWith(templateElm);
          elm = templateElm;
        } else if (widget.template) {
          elm.replaceWith(widget.template);
          elm = findWidgetPlaceholder(element);
        } else {
          elm.attr(widget.directive, '');

          if (widget.attrs) {
            _.each(widget.attrs, function (value, attr) {
              elm.attr(attr, value);
            });
          }

          if (widget.dataAttrName) {
            elm.attr(widget.dataAttrName, 'widgetData');
          }
        }

        scope.grabResizer = function (e) {

          // ignore middle- and right-click
          if (e.which !== 1) {
            return;
          }

          e.stopPropagation();
          e.originalEvent.preventDefault();

          // get the starting horizontal position
          var initX = e.clientX;
          // console.log('initX', initX);

          // Get the current width of the widget and dashboard
          var pixelWidth = widgetElm.width();
          var pixelHeight = widgetElm.height();
          var widgetStyleWidth = widget.style.width;
          var widthUnits = widget.widthUnits;
          var unitWidth = parseFloat(widgetStyleWidth);

          // create marquee element for resize action
          var $marquee = angular.element('

');
          widgetElm.append($marquee);

          // determine the unit/pixel ratio
          var transformMultiplier = unitWidth / pixelWidth;

          // updates marquee with preview of new width
          var mousemove = function (e) {
            var curX = e.clientX;
            var pixelChange = curX - initX;
            var newWidth = pixelWidth + pixelChange;
            $marquee.css('width', newWidth + 'px');
          };

          // sets new widget width on mouseup
          var mouseup = function (e) {
            // remove listener and marquee
            jQuery(window).off('mousemove', mousemove);
            $marquee.remove();

            // calculate change in units
            var curX = e.clientX;
            var pixelChange = curX - initX;
            var unitChange = Math.round(pixelChange * transformMultiplier * 100) / 100;

            // add to initial unit width
            var newWidth = unitWidth * 1 + unitChange;
            widget.setWidth(newWidth + widthUnits);
            scope.$apply();
          };

          jQuery(window).on('mousemove', mousemove).one('mouseup', mouseup);

        };

        // replaces widget title with input
        scope.editTitle = function (widget) {
          widget.editingTitle = true;
          // HACK: get the input to focus after being displayed.
          setTimeout(function () {
            widgetElm.find('form.widget-title input:eq(0)').focus()[0].setSelectionRange(0, 9999);
          }, 0);
        };

        // saves whatever is in the title input as the new title
        scope.saveTitleEdit = function (widget) {

          widget.editingTitle = false;
        };

        $compile(elm)(scope);
      }
    };
  }]);<|endoftext|>