<commit_before>'use strict';
// gulp
var gulp = require('gulp');
var paths = gulp.paths;
var options = gulp.options;
// plugins
var $ = require('gulp-load-plugins')();
// modules
var wiredep = require('wiredep');
var mainBowerFiles = require('main-bower-files');

// inject app/**/*.js, bower components, css into index.html
// inject environment variables into config.js constant
gulp.task('inject-all', ['styles', 'wiredep', 'bower-fonts', 'environment', 'build-vars'], function () {

  return gulp.src('app/index.html')
    .pipe(
      $.inject( // app/**/*.js files
        gulp.src(paths.jsFiles)
          .pipe($.plumber()) // use plumber so watch can start despite js errors
          .pipe($.naturalSort())
          .pipe($.angularFilesort()),
        {relative: true}))
    .pipe(
      $.inject( // inject compiled css
        gulp.src('.tmp/*/styles/*.css', {read: false}),
        {
          ignorePath: '../.tmp',
          relative: true,
        }))
    .pipe(gulp.dest('app'));
});

// build styles to tmp
gulp.task('styles', ['clean'], function () {

  // compile css starting from each module's scss
  return gulp.src('app/*/styles/!(_)*.scss')
    .pipe($.plumber())
    .pipe($.sourcemaps.init())
    .pipe($.sass.sync().on('error', $.sass.logError))
    .pipe($.autoprefixer({ browsers: ['last 2 versions'], remove: false}))
    .pipe($.sourcemaps.write('.'))
    .pipe(gulp.dest('.tmp/'));
});

// inject bower components into index.html
gulp.task('wiredep', function () {

  return gulp.src('app/index.html')
<% if (answers.ionicCss) { -%>
    // we're not excluding the ionic css here
    .pipe(wiredep.stream())
<% } else { -%>
    // exclude ionic scss since we're using ionic sass
    .pipe(wiredep.stream({exclude: ['bower_components/ionic/release/css']}))
<% } -%>
    .pipe(gulp.dest('app/'));
});

// copy bower fonts
<% if (answers.ionicCss) { -%>
gulp.task('bower-fonts', ['clean'], function () {
  // to do www/fonts (ionic css requires it to be in this folder)
  var DEST = 'www/fonts';
  var fontFiles = mainBowerFiles({filter: /\.(eot|otf|svg|ttf|woff|woff2)$/i});
<% } else { -%>
gulp.task('bower-fonts', function () {
  // to app/main/assets/fonts (path can be set in app/main/styles/<module>.scss)
  var DEST = 'app/main/assets/fonts';
  var fontFiles = mainBowerFiles({filter: /\.(eot|otf|svg|ttf|woff|woff2)$/i})
    .concat('app/main/assets/fonts/**/*');
<% } -%>

  return gulp.src(fontFiles)
    .pipe($.changed(DEST))
    .pipe(gulp.dest(DEST));
});

/**
 * transforms object into a string format that is ready for injection at indentation 4
 * @param  {Object} obj Object with values to inject
 * @return {String}     properly formatted string
 */
var injectFormat = function (obj) {
  // indentation of 2
  obj = JSON.stringify(obj, null, 2);
  // replace all doublequotes with singlequotes
  obj = obj.replace(/\"/g, '\'');
  // remove first and last line curly braces
  obj = obj.replace(/^\{\n/, '').replace(/\n\}$/, '');
  // remove indentation of first line
  obj = obj.replace(/^( ){2}/, '');
  // insert padding for all remaining lines
  obj = obj.replace(/\n( ){2}/g, '\n    ');

  return obj;
};

gulp.task('environment', function () {
  return gulp.src('app/*/constants/*config-const.js')
    .pipe(
      $.inject(
        gulp.src('app/main/constants/env-' + options.env + '.json'),
        {
          starttag: '/*inject-env*/',
          endtag: '/*endinject*/',
          transform: function (filePath, file) {
            var json;
            try {
              json = JSON.parse(file.contents.toString('utf8'));
            }
            catch (e) {
              console.log(e);
            }

            if (json) {
              json = injectFormat(json);
            }
            return json;
          }
        }))
    .pipe(gulp.dest('app/'));
});

gulp.task('build-vars', ['environment'], function () {
  return gulp.src('app/*/constants/*config-const.js')
    .pipe(
      $.inject(
        gulp.src(''),
        {
          starttag: '/*inject-build*/',
          endtag: '/*endinject*/',
          transform: function () {
            var obj = {};
            var buildVars = options.buildVars;

            if (buildVars) {
              // loop over build variables
              var variables = buildVars.split(',');
              for (var i = 0, variable; ((variable = variables[i])); i++) {
                var splits = variable.split(':');
                // add key and value to object
                obj[splits[0]] = splits[1];
              }
              return injectFormat(obj);
            }
            else {
              return;
            }
          }
        }))
    .pipe(gulp.dest('app/'));
});
<commit_msg>fixes #256: inconsistent injection order of css files<commit_after>'use strict';
// gulp
var gulp = require('gulp');
var paths = gulp.paths;
var options = gulp.options;
// plugins
var $ = require('gulp-load-plugins')();
// modules
var wiredep = require('wiredep');
var mainBowerFiles = require('main-bower-files');

// inject app/**/*.js, bower components, css into index.html
// inject environment variables into config.js constant
gulp.task('inject-all', ['styles', 'wiredep', 'bower-fonts', 'environment', 'build-vars'], function () {

  return gulp.src('app/index.html')
    .pipe(
      $.inject( // app/**/*.js files
        gulp.src(paths.jsFiles)
          .pipe($.plumber()) // use plumber so watch can start despite js errors
          .pipe($.naturalSort())
          .pipe($.angularFilesort()),
        {relative: true}))
    .pipe(
      $.inject( // inject compiled css
        gulp.src('.tmp/*/styles/*.css', {read: false})
          .pipe($.naturalSort()),
        {
          ignorePath: '../.tmp',
          relative: true,
        }))
    .pipe(gulp.dest('app'));
});

// build styles to tmp
gulp.task('styles', ['clean'], function () {

  // compile css starting from each module's scss
  return gulp.src('app/*/styles/!(_)*.scss')
    .pipe($.plumber())
    .pipe($.sourcemaps.init())
    .pipe($.sass.sync().on('error', $.sass.logError))
    .pipe($.autoprefixer({ browsers: ['last 2 versions'], remove: false}))
    .pipe($.sourcemaps.write('.'))
    .pipe(gulp.dest('.tmp/'));
});

// inject bower components into index.html
gulp.task('wiredep', function () {

  return gulp.src('app/index.html')
<% if (answers.ionicCss) { -%>
    // we're not excluding the ionic css here
    .pipe(wiredep.stream())
<% } else { -%>
    // exclude ionic scss since we're using ionic sass
    .pipe(wiredep.stream({exclude: ['bower_components/ionic/release/css']}))
<% } -%>
    .pipe(gulp.dest('app/'));
});

// copy bower fonts
<% if (answers.ionicCss) { -%>
gulp.task('bower-fonts', ['clean'], function () {
  // to do www/fonts (ionic css requires it to be in this folder)
  var DEST = 'www/fonts';
  var fontFiles = mainBowerFiles({filter: /\.(eot|otf|svg|ttf|woff|woff2)$/i});
<% } else { -%>
gulp.task('bower-fonts', function () {
  // to app/main/assets/fonts (path can be set in app/main/styles/<module>.scss)
  var DEST = 'app/main/assets/fonts';
  var fontFiles = mainBowerFiles({filter: /\.(eot|otf|svg|ttf|woff|woff2)$/i})
    .concat('app/main/assets/fonts/**/*');
<% } -%>

  return gulp.src(fontFiles)
    .pipe($.changed(DEST))
    .pipe(gulp.dest(DEST));
});

/**
 * transforms object into a string format that is ready for injection at indentation 4
 * @param  {Object} obj Object with values to inject
 * @return {String}     properly formatted string
 */
var injectFormat = function (obj) {
  // indentation of 2
  obj = JSON.stringify(obj, null, 2);
  // replace all doublequotes with singlequotes
  obj = obj.replace(/\"/g, '\'');
  // remove first and last line curly braces
  obj = obj.replace(/^\{\n/, '').replace(/\n\}$/, '');
  // remove indentation of first line
  obj = obj.replace(/^( ){2}/, '');
  // insert padding for all remaining lines
  obj = obj.replace(/\n( ){2}/g, '\n    ');

  return obj;
};

gulp.task('environment', function () {
  return gulp.src('app/*/constants/*config-const.js')
    .pipe(
      $.inject(
        gulp.src('app/main/constants/env-' + options.env + '.json'),
        {
          starttag: '/*inject-env*/',
          endtag: '/*endinject*/',
          transform: function (filePath, file) {
            var json;
            try {
              json = JSON.parse(file.contents.toString('utf8'));
            }
            catch (e) {
              console.log(e);
            }

            if (json) {
              json = injectFormat(json);
            }
            return json;
          }
        }))
    .pipe(gulp.dest('app/'));
});

gulp.task('build-vars', ['environment'], function () {
  return gulp.src('app/*/constants/*config-const.js')
    .pipe(
      $.inject(
        gulp.src(''),
        {
          starttag: '/*inject-build*/',
          endtag: '/*endinject*/',
          transform: function () {
            var obj = {};
            var buildVars = options.buildVars;

            if (buildVars) {
              // loop over build variables
              var variables = buildVars.split(',');
              for (var i = 0, variable; ((variable = variables[i])); i++) {
                var splits = variable.split(':');
                // add key and value to object
                obj[splits[0]] = splits[1];
              }
              return injectFormat(obj);
            }
            else {
              return;
            }
          }
        }))
    .pipe(gulp.dest('app/'));
});
<|endoftext|>


<commit_before>describe("ShareDialog", function() {

  describe("sharemapdialog", function() {

    var view;

    beforeEach(function() {
      var map = new cdb.admin.Map();
      map.setBounds([1,2], [3,4]);
      var table = TestUtil.createTable('test');
      map.layers.add(new cdb.geo.MapLayer());
      map.layers.add(new cdb.geo.CartoDBLayer({ query: 'select * from rambo'} ));

      view = new cdb.admin.ShareMapDialog({
        map: map,
        table: table,
        user: { account_type: "FREE" }
      });

    });

    it("should render a map without the CartoDB logo control", function() {
      view.render().show();
      expect(view.$('li.cartodb_logo').length > 0).toBeFalsy();
    });

  });

  describe("dialog", function() {
    var view, table, map;

    beforeEach(function() {

      map = new cdb.admin.Map();

      map.setBounds([[1,2], [3,4]]);

      map.layers.add(new cdb.geo.TileLayer({
        urlTemplate: 'http://test.com'}
      ));

      map.layers.add(new cdb.geo.CartoDBLayer({cartodb_logo: 'false', query: 'select * from rambo', table_name: 'test', user_name: 'test'} ));

      table = TestUtil.createTable('test');

      mapOptions = new cdb.core.Model({
        title: true,
        description: true,
        search: false,
        shareable: false,
        sql: ''
      });

      config = { custom_com_hosted: false };

      view = new cdb.admin.ShareMapDialog({
        map: map,
        table: table,
        user: { account_type: "CORONELLI" }
      });

    });

    it("should render a map with CartoDB logo control", function() {
      view.render().show();
      expect(view.$('li.cartodb_logo').length > 0).toEqual(true);
    });

    it("should render a map with the CartoDB logo", function() {
      view.render().show();

      // Map needs 300 miliseconds to be rendered
      waits(301);

      runs(function(){
        expect(view.mapView.$('a.cartodb_logo').length > 0).toEqual(true);
      });
    });

    it("should render a map with controls", function() {
      view.render().show();
      expect(view.$('#zoom').length).toEqual(1);
      expect(view.$('div.header h1').html()).toEqual('test');
      expect(view.$('div.header p').html()).toEqual('test description');
      expect(view.$('.form_switch').length).toEqual(6); // With cartodb_logo there are 5
    });

    it("should set embed URL with bounds", function() {

      view.render().show();
      view.model.set("method", "embed");

      expect(view.$('.url').val().indexOf('title=true')).not.toEqual(-1);
      view.mapOptions.set({title: false});

      expect(view.$('.url').val().indexOf('title=false')).not.toEqual(-1);
      expect(view.$('.url').val().indexOf('sql')).not.toEqual(-1);

      // wait to map is shown
      waits(1200);

      runs(function() {
        // change center after the map set the bounds (there is some timeout)
        view.map.set('zoom', 10);
        expect(view.$('.url').val().indexOf('sw_lat')).not.toEqual(-1);
      });
    });

    it("should set embed URL with zoom and center", function() {

      view.render().show();
      view.model.set("method", "embed");

      expect(view.$('.url').val().indexOf('title=true')).not.toEqual(-1);
      view.mapOptions.set({title: false});
      expect(view.$('.url').val().indexOf('title=false')).not.toEqual(-1);
      expect(view.$('.url').val().indexOf('sql')).not.toEqual(-1);

      // wait to map is shown
      waits(1200);

      runs(function() {
        view.map.set('zoom', 0);
        expect(view.$('.url').val().indexOf('zoom')).not.toEqual(-1);
        expect(view.$('.url').val().indexOf('center_lat')).not.toEqual(-1);
      });
    });

    it("should set embed URL with SQL from layer", function() {

      var sql;
      var sqlView = new cdb.admin.SQLViewData();

      sqlView.setSQL(sql='select * from charlies limit 1');
      table.useSQLView(sqlView);
      view.render().show();
      view.model.set("method", "embed");

      expect(view.$('.url').val().indexOf('sql=' + encodeURIComponent("select * from rambo"))).not.toEqual(-1);
    });

    it("should hide header when change options", function() {
      view.render().show();
      view.mapOptions.set({title: false});
      view.mapOptions.set({description: false});
      view.mapOptions.set({shareable: false});
      expect(view.$('div.header').css('display')).toEqual('none');
    });
  });

});
<commit_msg>Fixes spec for the new transitions in the share dialog<commit_after>describe("ShareDialog", function() {

  describe("sharemapdialog", function() {

    var view;

    beforeEach(function() {
      var map = new cdb.admin.Map();
      map.setBounds([1,2], [3,4]);
      var table = TestUtil.createTable('test');
      map.layers.add(new cdb.geo.MapLayer());
      map.layers.add(new cdb.geo.CartoDBLayer({ query: 'select * from rambo'} ));

      view = new cdb.admin.ShareMapDialog({
        map: map,
        table: table,
        user: { account_type: "FREE" }
      });

    });

    it("should render a map without the CartoDB logo control", function() {
      view.render().show();
      expect(view.$('li.cartodb_logo').length > 0).toBeFalsy();
    });

  });

  describe("dialog", function() {
    var view, table, map;

    beforeEach(function() {

      map = new cdb.admin.Map();

      map.setBounds([[1,2], [3,4]]);

      map.layers.add(new cdb.geo.TileLayer({
        urlTemplate: 'http://test.com'}
      ));

      map.layers.add(new cdb.geo.CartoDBLayer({cartodb_logo: 'false', query: 'select * from rambo', table_name: 'test', user_name: 'test'} ));

      table = TestUtil.createTable('test');

      mapOptions = new cdb.core.Model({
        title: true,
        description: true,
        search: false,
        shareable: false,
        sql: ''
      });

      config = { custom_com_hosted: false };

      view = new cdb.admin.ShareMapDialog({
        map: map,
        table: table,
        user: { account_type: "CORONELLI" }
      });

    });

    it("should render a map with CartoDB logo control", function() {
      view.render().show();
      expect(view.$('li.cartodb_logo').length > 0).toEqual(true);
    });

    it("should render a map with the CartoDB logo", function() {
      view.render().show();

      // Map needs 300 miliseconds to be rendered
      waits(301);

      runs(function(){
        expect(view.mapView.$('a.cartodb_logo').length > 0).toEqual(true);
      });
    });

    it("should render a map with controls", function() {
      view.render().show();
      expect(view.$('#zoom').length).toEqual(1);
      expect(view.$('div.header h1').html()).toEqual('test');
      expect(view.$('div.header p').html()).toEqual('test description');
      expect(view.$('.form_switch').length).toEqual(6); // With cartodb_logo there are 5
    });

    it("should set embed URL with bounds", function() {

      view.render().show();
      view.model.set("method", "embed");

      expect(view.$('.url').val().indexOf('title=true')).not.toEqual(-1);
      view.mapOptions.set({title: false});

      expect(view.$('.url').val().indexOf('title=false')).not.toEqual(-1);
      expect(view.$('.url').val().indexOf('sql')).not.toEqual(-1);

      // wait to map is shown
      waits(1200);

      runs(function() {
        // change center after the map set the bounds (there is some timeout)
        view.map.set('zoom', 10);
        expect(view.$('.url').val().indexOf('sw_lat')).not.toEqual(-1);
      });
    });

    it("should set embed URL with zoom and center", function() {

      view.render().show();
      view.model.set("method", "embed");

      expect(view.$('.url').val().indexOf('title=true')).not.toEqual(-1);
      view.mapOptions.set({title: false});
      expect(view.$('.url').val().indexOf('title=false')).not.toEqual(-1);
      expect(view.$('.url').val().indexOf('sql')).not.toEqual(-1);

      // wait to map is shown
      waits(1200);

      runs(function() {
        view.map.set('zoom', 0);
        expect(view.$('.url').val().indexOf('zoom')).not.toEqual(-1);
        expect(view.$('.url').val().indexOf('center_lat')).not.toEqual(-1);
      });
    });

    it("should set embed URL with SQL from layer", function() {

      var sql;
      var sqlView = new cdb.admin.SQLViewData();

      sqlView.setSQL(sql='select * from charlies limit 1');
      table.useSQLView(sqlView);
      view.render().show();
      view.model.set("method", "embed");

      expect(view.$('.url').val().indexOf('sql=' + encodeURIComponent("select * from rambo"))).not.toEqual(-1);
    });

    it("should hide header when change options", function() {
      view.render().show();

      view.mapOptions.set({ title: false });
      view.mapOptions.set({ description: false });
      view.mapOptions.set({ shareable: false });

      waitsFor(function() {
        return (($('.header').css('opacity') == 0) || $('.header').css('opacity') == undefined);
      }, "The element won't ever be hidden", 10000);

      runs(function () {
        expect($('.header').size()).toEqual(0);
      });
    });

  });

});
<|endoftext|>


<commit_before>/*globals define*/
/*
 * @author brollb / https://github/brollb
 *
 * Stretching functionality for Snap SVG Decorator
 */

define(['js/Widgets/SnapEditor/SnapEditorWidget.Constants'], function(SNAP_CONSTANTS){

    /*
     * This SVG was created with Snap! (byob) in mind.
     *
     * This SVG is dynamic and has the following features:
     *      - contains it's name in the svg itself
     *      - gets wider as the name increases in length
     *      - gets taller as elements are added inside
     *
     * The SVG used must have the following id(s):
     *      - "name" (text)
     *
     * The SVG used must have a couple elements with given
     * classes:
     *      - "x-shift-PTR_NAME"
     *      - "x-stretch-PTR_NAME"
     *      - "y-shift-PTR_NAME"
     *      - "y-stretch-PTR_NAME"
     *
     */

    "use strict";

    var AXIS = { X:'x', Y:'y' };//constants for stretching

    var SVGDecoratorSnapEditorWidgetStretch = function(){
    };

    /**
     * Initialize variables for stretching.
     *
     * @private
     * @return {undefined}
     */
    SVGDecoratorSnapEditorWidgetStretch.prototype.initializeStretchability = function () {
        //Stretching stuff
        this._calculatedTransforms = {};//The calculated abs stretch of any svg element in the SVG TODO
        this._transforms = {};//The current abs stretch of any svg element in the SVG

        this._classTransforms = {};//The calculated abs stretch of any class in the SVG
        this._minDims = {};

        this.svgContainerWidth = 0;
        this.svgWidth = 0;
        this.svgHeight = 0;
        this.svgBorderWidth = 0;
        this.svgInitialStretch = {};//Initial stretch values to allow for snug fit
    };

    /**
     * Apply the stored transformations.
     *
     * @return {undefined}
     */
    SVGDecoratorSnapEditorWidgetStretch.prototype._applyTransforms = function () {
        //Stored transformations: this._transforms
        //Current transformations: this._currentTransforms
        //TODO
    };

    /**
     * Set the text of a DOM element and stretch by the change in size
     *
     * @param {DOM Element} element
     * @param {String} newText
     * @param {String} stretchId
     * @return {Boolean} return true if size has changed
     */
    SVGDecoratorSnapEditorWidgetStretch.prototype._setTextAndStretch = function (element, newText, stretchId) {
        //TODO Change this to a READ-ONLY function wrt the DOM FIXME
        var oldText = element.text(),
            oldWidth = element.width(),
            newWidth;

        if (oldText === newText){
            return false;
        }

        element.text(newText);

        if(element.width() === 0 && oldWidth === 0){
            //Assume that it hasn't been drawn yet.
            //Approx the pixel length by relative name change
            //FIXME Find a better way to approximate this...
            //I could add a "name container" invisible rect...
            var bBox = this.$svgContent.find("#" + stretchId +"-bounding-box"),
                approxWidth;

            if (bBox.length){
                bBox = bBox[0];
                approxWidth = parseFloat(bBox.getAttribute("width"));
                newWidth = approxWidth * (element.text().length/oldText.length);
                return this.stretchTo(stretchId, { x: newWidth });
            }

        }else{
            return this.stretchTo(stretchId, { x: element.width() });
        }

        return false;
    };

    /* * * * Manipulating the SVG * * * */
    //Stretching
    /**
     * Stretch the svg to a given x,y with respect to a stretching id
     *
     * @param {String} id
     * @param {Object} size {x: x, y: y}
     * @return {Boolean} true if the svg has changed in size
     */
    SVGDecoratorSnapEditorWidgetStretch.prototype.stretchTo = function (id, size) {
        //TODO Change this to a READ-ONLY function wrt the DOM FIXME
        //Stretch according to the x,y values where x,y are
        //dimensions of the items pointed to by "id"
        var changed = false,
            x = size.x,
            y = size.y,
            dx,
            dy;

        if (x < 0 || y < 0){
            this.logger.warn("Cannot resize svg to negative size!");
        }

        //classTransforms keeps track of the current size of the stuff
        //associated with the given pointer
        if (!this._classTransforms[id]){
            if (this.svgInitialStretch[id]){
                this._classTransforms[id] = { x: this.svgInitialStretch[id].x,
                    y: this.svgInitialStretch[id].y };
            } else {
                this._classTransforms[id] = { x: 0, y: 0 };
            }
        }

        //Set initial stretch values if undefined
        if (!this.svgInitialStretch[id]){
            this.svgInitialStretch[id] = { x: 0, y: 0 };
        }

        //stretch x
        if (x !== undefined){//Don't shrink past initial
            x = Math.max(x, this.svgInitialStretch[id].x);
            dx = x - this._classTransforms[id].x;

            if (dx){
                this.stretch(id, AXIS.X, dx);
                this._classTransforms[id].x = x;
                changed = true;
            }
        }

        //stretch y
        if (y !== undefined){

            y = Math.max(y, this.svgInitialStretch[id].y);//Don't shrink past initial
            dy = y - this._classTransforms[id].y;

            //update size attached to ptr
            if (dy){
                this.stretch(id, AXIS.Y, dy);
                this._classTransforms[id].y = y;
                changed = true;
            }
        }

        return changed;
    };

    /**
     * Stretch the svg by delta along the coordinate plane, axis, with respect to the id
     *
     * @param {String} id
     * @param {String} axis
     * @param {Number} delta
     * @return {Number} Current size of the svg along the given axis
     */
    SVGDecoratorSnapEditorWidgetStretch.prototype.stretch = function (id, axis, delta) {
        //WILL WRITE TO THE DOM
        var stretchClass = axis + "-stretch-" + id,
            shiftClass = axis + "-shift-" + id,
            stretchElements = this.$svgContent.find("." + stretchClass),
            shiftElements = this.$svgContent.find("." + shiftClass),
            maxSize = 0,
            dim = axis === AXIS.X ? "width" : "height",
            width,
            height,
            svgId,
            shift = {},
            stretch = {},
            i;

        shift[axis] = delta;
        this._shiftConnectionAreas(shiftClass, shift);

        stretch[axis] = delta;
        this._stretchCustomConnectionHighlightAreas(stretchClass, stretch);

        //Initialize elements as needed
        this._initializeSvgElements(stretchElements);
        this._initializeSvgElements(shiftElements);
        //Done initializing

        //Stretch the SVG by delta
        var displacement = {},
            x,
            y;

        i = stretchElements.length;
        while(i--){
            svgId = stretchElements[i].getAttribute("id");

            if(!svgId){
                this.logger.error("SVG should have an ID");
            }

            //Update the stretch of the given svg element
            this._transforms[svgId][dim] += delta;
            maxSize = Math.max(maxSize, this._transforms[svgId][dim]);

            this._updateSVGTransforms( stretchElements[i], svgId);
        }

        i = shiftElements.length;
        while(i--){
            svgId = shiftElements[i].getAttribute("id");

            this._transforms[svgId].shift[axis] += delta;

            this._updateSVGTransforms(shiftElements[i], svgId);
        }

        //Adjust the overall svg if necessary
        var currentSVGSize = parseFloat(this.$svgElement[0].getAttribute(dim)) + delta;

        if(stretchElements.length || shiftElements.length){
            this.$svgElement[0].setAttribute(dim, Math.max(currentSVGSize, maxSize));//Expand if needed
            return Math.max(currentSVGSize, maxSize);
        }

        return currentSVGSize;
    };

    /**
     * Initialize variables
     *
     * @param {Array} elements
     */
    SVGDecoratorSnapEditorWidgetStretch.prototype._initializeSvgElements = function (elements) {
        var svgId,
            width,
            height,
            i = elements.length;

        while(i--){
            svgId = elements[i].getAttribute("id");

            if(!svgId){
                svgId = this._genSVGId();
                elements[i].setAttribute("id", svgId);
            }

            if(!this._transforms[svgId]){//Initialize transform if needed

                if(elements[i].tagName === "line"){
                    width = parseFloat(elements[i].getAttribute("x2")) - parseFloat(elements[i].getAttribute("x1"));
                    height = parseFloat(elements[i].getAttribute("y2")) - parseFloat(elements[i].getAttribute("y1"));
                }else if(elements[i].tagName === "rect"){
                    width = parseFloat(elements[i].getAttribute("width"));
                    height = parseFloat(elements[i].getAttribute("height"));
                }else if(elements[i].tagName === "path"){
                    width = null;
                    height = null;
                }
                    this._transforms[svgId] = { shift: { x: 0, y: 0 }, width: width, height: height };
                    this._minDims[svgId] = { width: width, height: height };
            }
        }
    };

    /**
     * Shift the connection areas by "shift" with respect to shiftClass
     *
     * @param {String} shiftClass
     * @param {Number} shift
     */
    SVGDecoratorSnapEditorWidgetStretch.prototype._shiftConnectionAreas = function (shiftClass, shift) {
        if (this._customConnectionAreas){

            var i = this._customConnectionAreas.length;

            while(i--){
                if(this._customConnectionAreas[i].shift && this._customConnectionAreas[i].shift.indexOf(shiftClass) !== -1){
                       //shift the connection area
                       this._customConnectionAreas[i].x1 += shift.x || 0;
                       this._customConnectionAreas[i].x2 += shift.x || 0;

                       this._customConnectionAreas[i].y1 += shift.y || 0;
                       this._customConnectionAreas[i].y2 += shift.y || 0;
                   }
            }
        }

        this._shiftCustomConnectionHighlightAreas(shiftClass, shift);
    };

    /**
     * Shift the connection highlight areas by "shift" with respect to shiftClass
     *
     * @param {String} shiftClass
     * @param {Number} shift
     */
    SVGDecoratorSnapEditorWidgetStretch.prototype._shiftCustomConnectionHighlightAreas = function (shiftClass, shift) {
        if (this[SNAP_CONSTANTS.CONNECTION_HIGHLIGHT]){

            var i = this[SNAP_CONSTANTS.CONNECTION_HIGHLIGHT].length;

            while(i--){
                if(this[SNAP_CONSTANTS.CONNECTION_HIGHLIGHT][i].class && this[SNAP_CONSTANTS.CONNECTION_HIGHLIGHT][i].class.indexOf(shiftClass) !== -1){
                       //shift the connection area highlight
                       this[SNAP_CONSTANTS.CONNECTION_HIGHLIGHT][i].x1 += shift.x || 0;
                       this[SNAP_CONSTANTS.CONNECTION_HIGHLIGHT][i].x2 += shift.x || 0;

                       this[SNAP_CONSTANTS.CONNECTION_HIGHLIGHT][i].y1 += shift.y || 0;
                       this[SNAP_CONSTANTS.CONNECTION_HIGHLIGHT][i].y2 += shift.y || 0;
                   }
            }
        }
    };

    /**
     * Stretch custom connection highlight areas of the item.
     *
     * @private
     * @param {String} stretchClass
     * @param {Number} stretch
     * @return {undefined}
     */
    SVGDecoratorSnapEditorWidgetStretch.prototype._stretchCustomConnectionHighlightAreas = function (stretchClass, stretch) {
        if (this[SNAP_CONSTANTS.CONNECTION_HIGHLIGHT]){

            var i = this[SNAP_CONSTANTS.CONNECTION_HIGHLIGHT].length;

            while(i--){
                if(this[SNAP_CONSTANTS.CONNECTION_HIGHLIGHT][i].class && this[SNAP_CONSTANTS.CONNECTION_HIGHLIGHT][i].class.indexOf(stretchClass) !== -1){
                       //stretch the connection area highlight
                       this[SNAP_CONSTANTS.CONNECTION_HIGHLIGHT][i].x2 += stretch.x || 0;
                       this[SNAP_CONSTANTS.CONNECTION_HIGHLIGHT][i].y2 += stretch.y || 0;
                   }
            }
        }
    };

    SVGDecoratorSnapEditorWidgetStretch.prototype._updateSVGTransforms = function (svg, id) {
        var width = Math.max(this._minDims[id].width, this._transforms[id].width),
            height = Math.max(this._minDims[id].height, this._transforms[id].height);

        if(svg.tagName === "line"){
            var x1 = parseFloat(svg.getAttribute("x1")),
                x2 = parseFloat(svg.getAttribute("x2")),
                y1 = parseFloat(svg.getAttribute("y1")),
                y2 = parseFloat(svg.getAttribute("y2"));

            svg.setAttribute("x2", x1+width);
            svg.setAttribute("y2", y1+height);

        }else if(svg.tagName === "rect"){

            svg.setAttribute("width", width);
            svg.setAttribute("height", height);
        }

        svg.setAttribute("transform",
                "translate(" + this._transforms[id].shift.x + "," + this._transforms[id].shift.y + ")");
    };

    return SVGDecoratorSnapEditorWidgetStretch;
});
<commit_msg>Refactored to separate reading from DOM and writing to DOM<commit_after>/*globals define,_*/
/*
 * @author brollb / https://github/brollb
 *
 * Stretching functionality for Snap SVG Decorator
 */

define(['js/Widgets/SnapEditor/SnapEditorWidget.Constants'], function(SNAP_CONSTANTS){

    /*
     * This SVG was created with Snap! (byob) in mind.
     *
     * This SVG is dynamic and has the following features:
     *      - contains it's name in the svg itself
     *      - gets wider as the name increases in length
     *      - gets taller as elements are added inside
     *
     * The SVG used must have the following id(s):
     *      - "name" (text)
     *
     * The SVG used must have a couple elements with given
     * classes:
     *      - "x-shift-PTR_NAME"
     *      - "x-stretch-PTR_NAME"
     *      - "y-shift-PTR_NAME"
     *      - "y-stretch-PTR_NAME"
     *
     */

    "use strict";

    var AXIS = { X:'x', Y:'y' };//constants for stretching

    var SVGDecoratorSnapEditorWidgetStretch = function(){
    };

    /**
     * Initialize variables for stretching.
     *
     * @private
     * @return {undefined}
     */
    SVGDecoratorSnapEditorWidgetStretch.prototype.initializeStretchability = function () {
        //Stretching stuff
        this._transforms = {};//The current abs stretch of any svg element in the SVG
        this._updatedSVGElements = {};//The elements that have been updated since last render
        this._classTransforms = {};//The calculated abs stretch of any class in the SVG
        this._minDims = {};
        this._svgDims = {};

        this.svgContainerWidth = 0;
        this.svgWidth = 0;
        this.svgHeight = 0;
        this.svgBorderWidth = 0;
        this.svgInitialStretch = {};//Initial stretch values to allow for snug fit
    };

    /**
     * Initialize ids of all svg elements.
     *
     * @return {undefined}
     */
    SVGDecoratorSnapEditorWidgetStretch.prototype._initializeSVGElements = function () {
        var self = this,
            svgId,
            width,
            height,
            x,
            y,
            idCreator = function(key, value){
                svgId = value.getAttribute("id");

                if(!svgId){
                    svgId = self._genSVGId();
                    value.setAttribute("id", svgId);
                }

                if(!self._transforms[svgId]){//Initialize transform if needed

                    if(value.tagName === "line"){
                        x = parseFloat(value.getAttribute("x1"));
                        y = parseFloat(value.getAttribute("y1"));
                        width = parseFloat(value.getAttribute("x2")) - x;
                        height = parseFloat(value.getAttribute("y2")) - y;
                    }else if(value.tagName === "rect"){
                        x = parseFloat(value.getAttribute("x"));
                        y = parseFloat(value.getAttribute("y"));
                        width = parseFloat(value.getAttribute("width"));
                        height = parseFloat(value.getAttribute("height"));
                    }else if(value.tagName === "path"){
                        width = null;
                        height = null;
                    }
                    self._transforms[svgId] = { original: { x: x, y: y }, shift: { x: 0, y: 0 }, width: width, height: height };
                    self._minDims[svgId] = { width: width, height: height };
                }

                //Recurse if applicable
                if (value.children){
                    $.each(value.children, idCreator);
                }
            };

        this.$svgElement.each(idCreator);
    };

    /**
     * Randomly generate ID between 0,10000 for identifying svg elements.
     *
     * @private
     * @return {String} id
     */
    SVGDecoratorSnapEditorWidgetStretch.prototype._genSVGId = function () {

        var id = "SVG_" + Math.floor(Math.random()*10000);

        while(this._transforms[id]){
            id = "SVG_" + Math.floor(Math.random()*10000);
        }

        return id;
    };

    /**
     * Apply the stored transformations.
     *
     * @return {undefined}
     */
    SVGDecoratorSnapEditorWidgetStretch.prototype._applyTransforms = function () {
        var elements = Object.keys(this._updatedSVGElements),
            element;

        while (elements.length){
            element = elements.pop();
            this._updateSVGTransforms(this._updatedSVGElements[element]);
            delete this._updatedSVGElements[element];
        }

        //Set the height/width as needed

        for (var dim in this._svgDims){
            if (this._svgDims.hasOwnProperty(dim)){
                this.$svgElement[0].setAttribute(dim, this._svgDims[dim]);
            }
        }

        this.hostDesignerItem.clearCalculatedSize();
    };

    /**
     * Set the text of a DOM element and stretch by the change in size
     *
     * @param {DOM Element} element
     * @param {String} newText
     * @param {String} stretchId
     * @return {Boolean} return true if size has changed
     */
    SVGDecoratorSnapEditorWidgetStretch.prototype._setTextAndStretch = function (element, newText, stretchId) {
        //TODO Change this to a READ-ONLY function wrt the DOM FIXME
        var oldText = element.text(),
            oldWidth = element.width(),
            newWidth;

        if (oldText === newText){
            return false;
        }

        element.text(newText);

        if(element.width() === 0 && oldWidth === 0){
            //Assume that it hasn't been drawn yet.
            //Approx the pixel length by relative name change
            //FIXME Find a better way to approximate this...
            //I could add a "name container" invisible rect...
            var bBox = this.$svgContent.find("#" + stretchId +"-bounding-box"),
                approxWidth;

            if (bBox.length){
                bBox = bBox[0];
                approxWidth = parseFloat(bBox.getAttribute("width"));
                newWidth = approxWidth * (element.text().length/oldText.length);
                return this.stretchTo(stretchId, { x: newWidth });
            }

        }else{
            return this.stretchTo(stretchId, { x: element.width() });
        }

        return false;
    };

    /* * * * Manipulating the SVG * * * */
    //Stretching
    /**
     * Stretch the svg to a given x,y with respect to a stretching id
     *
     * @param {String} id
     * @param {Object} size {x: x, y: y}
     * @return {Boolean} true if the svg has changed in size
     */
    SVGDecoratorSnapEditorWidgetStretch.prototype.stretchTo = function (id, size) {
        //TODO Change this to a READ-ONLY function wrt the DOM FIXME
        //Stretch according to the x,y values where x,y are
        //dimensions of the items pointed to by "id"
        var changed = false,
            x = size.x,
            y = size.y,
            dx,
            dy;

        if (x < 0 || y < 0){
            this.logger.warn("Cannot resize svg to negative size!");
        }

        //classTransforms keeps track of the current size of the stuff
        //associated with the given pointer
        if (!this._classTransforms[id]){
            if (this.svgInitialStretch[id]){
                this._classTransforms[id] = { x: this.svgInitialStretch[id].x,
                    y: this.svgInitialStretch[id].y };
            } else {
                this._classTransforms[id] = { x: 0, y: 0 };
            }
        }

        //Set initial stretch values if undefined
        if (!this.svgInitialStretch[id]){
            this.svgInitialStretch[id] = { x: 0, y: 0 };
        }

        //stretch x
        if (x !== undefined){//Don't shrink past initial
            x = Math.max(x, this.svgInitialStretch[id].x);
            dx = x - this._classTransforms[id].x;

            if (dx){
                this.stretch(id, AXIS.X, dx);//FIXME
                this._classTransforms[id].x = x;
                changed = true;
            }
        }

        //stretch y
        if (y !== undefined){

            y = Math.max(y, this.svgInitialStretch[id].y);//Don't shrink past initial
            dy = y - this._classTransforms[id].y;

            //update size attached to ptr
            if (dy){
                this.stretch(id, AXIS.Y, dy);//FIXME
                this._classTransforms[id].y = y;
                changed = true;
            }
        }

        return changed;
    };

    /**
     * Stretch the svg by delta along the coordinate plane, axis, with respect to the id
     *
     * @param {String} id
     * @param {String} axis
     * @param {Number} delta
     * @return {Number} Current size of the svg along the given axis
     */
    SVGDecoratorSnapEditorWidgetStretch.prototype.stretch = function (id, axis, delta) {
        var stretchClass = axis + "-stretch-" + id,
            shiftClass = axis + "-shift-" + id,
            stretchElements = this.$svgContent.find("." + stretchClass),
            shiftElements = this.$svgContent.find("." + shiftClass),
            maxSize = 0,
            dim = axis === AXIS.X ? "width" : "height",
            width,
            height,
            svgId,
            shift = {},
            stretch = {},
            i;

        shift[axis] = delta;
        this._shiftConnectionAreas(shiftClass, shift);

        stretch[axis] = delta;
        this._stretchCustomConnectionHighlightAreas(stretchClass, stretch);

        //Stretch the SVG by delta
        var displacement = {},
            x,
            y;

        i = stretchElements.length;
        while(i--){
            svgId = stretchElements[i].getAttribute("id");

            if(!svgId){
                this.logger.error("SVG should have an ID");
            }

            //Update the stretch of the given svg element
            this._transforms[svgId][dim] += delta;
            maxSize = Math.max(maxSize, this._transforms[svgId][dim]);

            //this._updateSVGTransforms( stretchElements[i] );//WRITE
            this._updatedSVGElements[svgId] = stretchElements[i];
        }

        i = shiftElements.length;
        while(i--){
            svgId = shiftElements[i].getAttribute("id");

            this._transforms[svgId].shift[axis] += delta;

            //this._updateSVGTransforms(shiftElements[i] );//WRITE
            this._updatedSVGElements[svgId] = shiftElements[i];
        }

        //Adjust the overall svg if necessary
        if ( this._svgDims[dim] === undefined){
            this._svgDims[dim] = parseFloat(this.$svgElement[0].getAttribute(dim));
        }

        if(stretchElements.length || shiftElements.length){
            this._svgDims[dim] = Math.max(this._svgDims[dim] + delta, maxSize);

            //Update the host item  FIXME
            //this.hostDesignerItem['_' + dim] = this._svgDims[dim];
        }

        return this._svgDims[dim];
    };

    /**
     * Shift the connection areas by "shift" with respect to shiftClass
     *
     * @param {String} shiftClass
     * @param {Number} shift
     */
    SVGDecoratorSnapEditorWidgetStretch.prototype._shiftConnectionAreas = function (shiftClass, shift) {
        if (this._customConnectionAreas){

            var i = this._customConnectionAreas.length;

            while(i--){
                if(this._customConnectionAreas[i].shift && this._customConnectionAreas[i].shift.indexOf(shiftClass) !== -1){
                       //shift the connection area
                       this._customConnectionAreas[i].x1 += shift.x || 0;
                       this._customConnectionAreas[i].x2 += shift.x || 0;

                       this._customConnectionAreas[i].y1 += shift.y || 0;
                       this._customConnectionAreas[i].y2 += shift.y || 0;
                   }
            }
        }

        this._shiftCustomConnectionHighlightAreas(shiftClass, shift);
    };

    /**
     * Shift the connection highlight areas by "shift" with respect to shiftClass
     *
     * @param {String} shiftClass
     * @param {Number} shift
     */
    SVGDecoratorSnapEditorWidgetStretch.prototype._shiftCustomConnectionHighlightAreas = function (shiftClass, shift) {
        if (this[SNAP_CONSTANTS.CONNECTION_HIGHLIGHT]){

            var i = this[SNAP_CONSTANTS.CONNECTION_HIGHLIGHT].length;

            while(i--){
                if(this[SNAP_CONSTANTS.CONNECTION_HIGHLIGHT][i].class && this[SNAP_CONSTANTS.CONNECTION_HIGHLIGHT][i].class.indexOf(shiftClass) !== -1){
                       //shift the connection area highlight
                       this[SNAP_CONSTANTS.CONNECTION_HIGHLIGHT][i].x1 += shift.x || 0;
                       this[SNAP_CONSTANTS.CONNECTION_HIGHLIGHT][i].x2 += shift.x || 0;

                       this[SNAP_CONSTANTS.CONNECTION_HIGHLIGHT][i].y1 += shift.y || 0;
                       this[SNAP_CONSTANTS.CONNECTION_HIGHLIGHT][i].y2 += shift.y || 0;
                   }
            }
        }
    };

    /**
     * Stretch custom connection highlight areas of the item.
     *
     * @private
     * @param {String} stretchClass
     * @param {Number} stretch
     * @return {undefined}
     */
    SVGDecoratorSnapEditorWidgetStretch.prototype._stretchCustomConnectionHighlightAreas = function (stretchClass, stretch) {
        if (this[SNAP_CONSTANTS.CONNECTION_HIGHLIGHT]){

            var i = this[SNAP_CONSTANTS.CONNECTION_HIGHLIGHT].length;

            while(i--){
                if(this[SNAP_CONSTANTS.CONNECTION_HIGHLIGHT][i].class && this[SNAP_CONSTANTS.CONNECTION_HIGHLIGHT][i].class.indexOf(stretchClass) !== -1){
                       //stretch the connection area highlight
                       this[SNAP_CONSTANTS.CONNECTION_HIGHLIGHT][i].x2 += stretch.x || 0;
                       this[SNAP_CONSTANTS.CONNECTION_HIGHLIGHT][i].y2 += stretch.y || 0;
                   }
            }
        }
    };

    /**
     * Apply the current svg transforms to the svg.
     *
     * @param svg
     * @param id
     * @return {undefined}
     */
    SVGDecoratorSnapEditorWidgetStretch.prototype._updateSVGTransforms = function (svg) {
        //WRITE ONLY
        var id = svg.id,
            width = Math.max(this._minDims[id].width, this._transforms[id].width),
            height = Math.max(this._minDims[id].height, this._transforms[id].height);

        if(svg.tagName === "line"){
            var x = this._transforms.original.x,
                y = this._transforms.original.y;

            svg.setAttribute("x2", x + width);
            svg.setAttribute("y2", y + height);

        }else if(svg.tagName === "rect"){

            svg.setAttribute("width", width);
            svg.setAttribute("height", height);
        }

        svg.setAttribute("transform",
                "translate(" + this._transforms[id].shift.x + "," + this._transforms[id].shift.y + ")");
    };

    return SVGDecoratorSnapEditorWidgetStretch;
});
<|endoftext|>


<commit_before>import _ from 'lodash'

/**
 * Create GLSL program for merge.Concatenate layer
 *
 * @param {number} numInputs
 * @param {number[]} inputShape
 * @param {number[]} outputShape
 */
export default function concatenate(numInputs, inputShape, outputShape, concatAxis) {
  let block = 'outColor = vec4(0.0);'

  if (concatAxis === 0) {
    // prettier-ignore
    block = `
  int n = int(floor(float(out_y) / float(${inputShape[0]})));
  if (n == 0) {
    outColor = vec4(texelFetch(inputs[0], ivec2(out_x, out_y), 0).r);
  }${_.range(1, numInputs).map(
    i => ` else if (n == ${i}) {
    outColor = vec4(texelFetch(inputs[${i}], ivec2(out_x, out_y - ${i * inputShape[0]}), 0).r);
  }`).join('')}
`
  } else if (concatAxis === 1) {
    // prettier-ignore
    block = `
  int n = int(floor(float(out_x) / float(${inputShape[1]})));
  if (n == 0) {
    outColor = vec4(texelFetch(inputs[0], ivec2(out_x, out_y), 0).r);
  }${_.range(1, numInputs).map(
    i => ` else if (n == ${i}) {
    outColor = vec4(texelFetch(inputs[${i}], ivec2(out_x - ${i * inputShape[1]}, out_y), 0).r);
  }`).join('')}
`
  }

  const source = `#version 300 es
precision highp float;

in vec2 outTex;
uniform sampler2D inputs[${numInputs}];
out vec4 outColor;

void main() {
  int out_y = int(float(${outputShape[0]}) * outTex.y);
  int out_x = int(float(${outputShape[1]}) * outTex.x);
${block}
}
`

  return source
}
<commit_msg>fix concatenate shader program to handle different input concat axis dims<commit_after>import _ from 'lodash'

/**
 * Create GLSL program for merge.Concatenate layer
 *
 * @param {number} numInputs
 * @param {number[][]} inputShapes
 * @param {number[]} outputShape
 * @param {number} concatAxis
 */
export default function concatenate(numInputs, inputShapes, outputShape, concatAxis) {
  const dims = inputShapes.map(shape => shape[concatAxis])
  const offsets = _.range(numInputs + 1).map(i => _.sum(_.take(dims, i)))

  const outVar = concatAxis === 0 ? 'out_y' : 'out_x'
  let getOffset = `
  int n = 0;
  int offset = 0;
  if (${outVar} >= ${offsets[1]} && ${outVar} < ${offsets[2]}) {
    n = 1;
    offset = ${offsets[1]};
  }`
  if (numInputs > 2) {
    // prettier-ignore
    getOffset += `${_.range(2, numInputs).map(i =>
      ` else if (${outVar} >= ${offsets[i]} && ${outVar} < ${offsets[i + 1]}) {
    n = ${i};
    offset = ${offsets[i]};
  }`).join('')}
`
  }

  let outBlock = 'outColor = vec4(0.0);'
  if (concatAxis === 0 || concatAxis === 1) {
    const xCoord = i => `out_x${concatAxis === 1 ? ` - ${offsets[i]}` : ''}`
    const yCoord = i => `out_y${concatAxis === 0 ? ` - ${offsets[i]}` : ''}`
    // prettier-ignore
    outBlock = `
  if (n == 0) {
    outColor = vec4(texelFetch(inputs[0], ivec2(out_x, out_y), 0).r);
  }${_.range(1, numInputs).map(i => ` else if (n == ${i}) {
    outColor = vec4(texelFetch(inputs[${i}], ivec2(${xCoord(i)}, ${yCoord(i)}), 0).r);
  }`).join('')}
`
  }

  const source = `#version 300 es
precision highp float;

in vec2 outTex;
uniform sampler2D inputs[${numInputs}];
out vec4 outColor;

void main() {
  int out_y = int(float(${outputShape[0]}) * outTex.y);
  int out_x = int(float(${outputShape[1]}) * outTex.x);
${getOffset}
${outBlock}
}
`

  return source
}
<|endoftext|>


<commit_before>/**
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
var grails = grails || {};
(function ($) {
    if (!grails.Events) {
        grails.Events = function (root, options) {

            var that = this;
            var socket = $.atmosphere;

            that.root = (root && (typeof root == "string")) ? root : (window.location.protocol + '//' + window.location.hostname + ':' + window.location.port);
            var hasOptions = (options && (typeof options == "object"));

            that.globalTopicName = hasOptions && options.globalTopicName && (typeof options.globalTopicName == "string") ? options.globalTopicName : "eventsbus";
            that.path = hasOptions && options.path && (typeof options.path == "string") ? option.path : "g-eventsbus";
            that.binary = hasOptions && options.binary && (typeof options.binary == "boolean") ? options.binary : false;

            var loadingRequest = {};
            var state = grails.Events.CONNECTING;
            that.onopen = null;
            that.onglobalmessage = null;
            that.onclose = null;
            var handlerMap = {};

            var localId = "";

            that.send = function (topic, message) {
                checkSpecified("topic", 'string', topic);
                //checkSpecified("message", 'object', message);
                //checkOpen();
                var envelope;
                if (that.binary && (message instanceof ArrayBuffer || message instanceof Blob)) {
                    envelope = appendBuffer(stringToUint8(topic + '|' + '2'), message);
                    that.globalTopicSocket.push(envelope);
                } else {
                    envelope = topic + '|' + (typeof (message) === 'object' ?
                        '1' + '|' + jQuery.stringifyJSON(message) :
                        '0' + '|' + message)
                    if (!that.binary) {
                        that.globalTopicSocket.push({data: envelope});
                    } else {
                        that.globalTopicSocket.push(stringToUint8(envelope).buffer);
                    }
                }
                return envelope;
            };

            that.on = function (topic, handler, request) {
                checkSpecified("topic", 'string', topic);
                checkSpecified("handler", 'function', handler);

                var subscribe = jQuery.isEmptyObject(handlerMap) || !jQuery.isEmptyObject(request);
                var handlers = handlerMap[topic];
                if (handlers) {
                    handlers[handlers.length] = handler;
                } else {
                    handlerMap[topic] = [handler];
                }
                if (subscribe) {
                    var topics = "";
                    for (var _topic in handlerMap) {
                        topics += _topic + ',';
                    }
                    //request.shared = true;
                    var rq = {
                        url: that.root + '/' + that.path + '/' + that.globalTopicName,
                        transport: "websocket",
                        contentType: "application/json",
                        fallbackTransport: "streaming",
                        reconnectInterval: 4000
                    };

                    if (that.binary && !!window.ArrayBuffer) {
                        rq.webSocketBinaryType = "arraybuffer"
                        rq.headers = { "X-Atmosphere-Binary" : true };
                    }

                    if (!!window.EventSource) {
                        rq.fallbackTransport = 'sse';
                    }

                    // Allow the user to extend/override the request
                    rq = jQuery.extend(true, rq, options);
                    rq = jQuery.extend(true, rq, request);

                    rq.onTransportFailure = function (e, r) {
                        rq.onOpen = null;
                        r.headers = {
                            topics: Object.keys(handlerMap)
                        };
                        that.binary = false;
                    };

                    rq.onOpen = function (response) {
                        rq.onOpen = null;
                        for (var val in handlerMap) {
                            console.log("defer connecting topic: " + val);
                            _registerRequest(val);
                        }
                        loadingRequest = null;
                    };
                    loadingRequest = rq;
                    that.globalTopicSocket = socket.subscribe(rq);
                } else {
                    if (loadingRequest == null) {
                        console.log("connecting topic: " + topic);
                        _registerRequest(topic);
                    }
                }

                return handler;
            };

            that.unregisterHandlers = function (topic) {
                checkSpecified("topic", 'string', topic);
                delete handlerMap[topic];
                socket.unsubscribeUrl(that.root + '/' + that.path + '/' + that.globalTopicName);
                init();
            };

            that.unregisterHandler = function (topic, handler) {
                checkSpecified("topic", 'string', topic);
                checkSpecified("handler", 'function', handler);
                checkOpen();
                var handlers = handlerMap[topic];
                if (handlers) {
                    var idx = handlers.indexOf(handler);
                    if (idx != -1) handlers.splice(idx, 1);
                    if (handlers.length == 0) {
                        that.unregisterHandlers(topic);
                    }
                }
            };

            that.close = function () {
                state = grails.Events.CLOSING;
                socket.unsubscribe();
            };

            that.readyState = function () {
                return state;
            };

            function _registerRequest(topic) {
                if (!that.binary) {
                    that.globalTopicSocket.push(topic + '|' + "4");
                } else {
                    that.globalTopicSocket.push(stringToUint8(topic + '|' + "4").buffer);
                }
            }

            function init() {
                var connecting = function () {
                    state = grails.Events.OPEN;
                    if (that.onopen) {
                        that.onopen();
                    }
                };

                socket.onOpen = connecting;
                socket.onClose = function (e) {
                    state = grails.Events.CLOSED;
                    if (that.onclose) {
                        that.onclose();
                    }
                };

                var buffer;

                socket.onMessage = function (response) {
                    if (response.status == 200) {
                        var topic, type, data, envelope;
                        try {
                            if (response.responseBody instanceof ArrayBuffer) {
                                var dataUint = new Uint8Array(response.responseBody);
                                var endPacket = dataUint.length >= 3 &&
                                    "E".charCodeAt(0) == dataUint[dataUint.length - 3] &&
                                    "N".charCodeAt(0) == dataUint[dataUint.length - 2] &&
                                    "D".charCodeAt(0) == dataUint[dataUint.length - 1];

                                if (dataUint.length > 0 && !endPacket) {
                                    if (buffer == null) {
                                        buffer = response.responseBody;
                                    } else {
                                        var tmp = new Uint8Array(dataUint.length + buffer.byteLength);
                                        tmp.set(new Uint8Array(buffer), 0);
                                        tmp.set(dataUint, buffer.byteLength);
                                        buffer = tmp.buffer;
                                    }
                                }

                                if (!endPacket) {
                                    return;
                                }

                                if (!buffer || buffer.byteLength == 0) {
                                    buffer = null;
                                    return;
                                }

                                var bytes = new Uint8Array(buffer);
                                var tmp = "";
                                for (var i = 0; i < bytes.length; i++) {
                                    if (bytes[i] == "|".charCodeAt(0)) {
                                        if (tmp.length > 0) {
                                            if (!topic) {
                                                topic = tmp;
                                                tmp = "";
                                            } else if (!type) {
                                                type = tmp;
                                                data = bytes.buffer.slice(i + 1);
                                                break;
                                            }
                                        }
                                    } else {
                                        tmp += String.fromCharCode(bytes[i]);
                                    }
                                }
                                buffer = null;

                            } else if (response.responseBody.length > 0) {
                                var _data = response.responseBody.split('|')
                                topic = _data[0];
                                type = _data[1];
                                data = _data[2];
                            }
                        } catch (e) {
                            if (console != 'undefined') {
                                console.log(e.stack);
                                console.log('discarded message: ' + response.responseBody);
                                buffer = null;
                            }
                            return;
                        }
                        var handlers = handlerMap[topic ? topic : that.globalTopicName];
                        if (handlers) {
                            if (type == '5') {
                                that.unregisterHandlers(topic);
                                return;
                            }
                            // We make a copy since the handler might get unregistered from within the
                            // handler itself, which would screw up our iteration
                            var copy = handlers.slice(0);
                            if (type == '0' || type == '2') {
                                envelope = data;
                            } else if (type == '1') {
                                envelope = jQuery.parseJSON(
                                    data instanceof ArrayBuffer ?
                                        String.fromCharCode.apply(null, new Uint8Array(data)) :
                                        data);
                            }
                            for (var i = 0; i < copy.length; i++) {
                                copy[i](envelope, topic, response, type);
                            }
                        }
                    }
                };

                that.on(that.globalTopicName, function (data, e) {
                    if (that.onglobalmessage) {
                        that.onglobalmessage(data);
                    }
                });
            }

            function checkOpen() {
                if (state != grails.Events.OPEN) {
                    throw new Error('INVALID_STATE_ERR');
                }
            }

            function stringToUint8(str) {
                str = str + '|';
                var uint = new Uint8Array(str.length);
                for (var i = 0, j = str.length; i < j; ++i) {
                    uint[i] = str.charCodeAt(i);
                }
                return uint;
            }

            function appendBuffer(uint, buffer) {
                var tmp = new Uint8Array(uint.length + buffer.byteLength);
                tmp.set(uint, 0);
                tmp.set(new Uint8Array(buffer), uint.length);
                return tmp.buffer;
            }

            function checkSpecified(paramName, paramType, param, optional) {
                if (!optional && !param) {
                    throw new Error("Parameter " + paramName + " must be specified");
                }
                if (param && typeof param != paramType) {
                    throw new Error("Parameter " + paramName + " must be of type " + paramType);
                }
            }

            init();

        };

        grails.Events.CONNECTING = 0;
        grails.Events.OPEN = 1;
        grails.Events.CLOSING = 2;
        grails.Events.CLOSED = 3;

    }
})(jQuery);<commit_msg>corrected typo when a user specifies an options.path option<commit_after>/**
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
var grails = grails || {};
(function ($) {
    if (!grails.Events) {
        grails.Events = function (root, options) {

            var that = this;
            var socket = $.atmosphere;

            that.root = (root && (typeof root == "string")) ? root : (window.location.protocol + '//' + window.location.hostname + ':' + window.location.port);
            var hasOptions = (options && (typeof options == "object"));

            that.globalTopicName = hasOptions && options.globalTopicName && (typeof options.globalTopicName == "string") ? options.globalTopicName : "eventsbus";
            that.path = hasOptions && options.path && (typeof options.path == "string") ? options.path : "g-eventsbus";
            that.binary = hasOptions && options.binary && (typeof options.binary == "boolean") ? options.binary : false;

            var loadingRequest = {};
            var state = grails.Events.CONNECTING;
            that.onopen = null;
            that.onglobalmessage = null;
            that.onclose = null;
            var handlerMap = {};

            var localId = "";

            that.send = function (topic, message) {
                checkSpecified("topic", 'string', topic);
                //checkSpecified("message", 'object', message);
                //checkOpen();
                var envelope;
                if (that.binary && (message instanceof ArrayBuffer || message instanceof Blob)) {
                    envelope = appendBuffer(stringToUint8(topic + '|' + '2'), message);
                    that.globalTopicSocket.push(envelope);
                } else {
                    envelope = topic + '|' + (typeof (message) === 'object' ?
                        '1' + '|' + jQuery.stringifyJSON(message) :
                        '0' + '|' + message)
                    if (!that.binary) {
                        that.globalTopicSocket.push({data: envelope});
                    } else {
                        that.globalTopicSocket.push(stringToUint8(envelope).buffer);
                    }
                }
                return envelope;
            };

            that.on = function (topic, handler, request) {
                checkSpecified("topic", 'string', topic);
                checkSpecified("handler", 'function', handler);

                var subscribe = jQuery.isEmptyObject(handlerMap) || !jQuery.isEmptyObject(request);
                var handlers = handlerMap[topic];
                if (handlers) {
                    handlers[handlers.length] = handler;
                } else {
                    handlerMap[topic] = [handler];
                }
                if (subscribe) {
                    var topics = "";
                    for (var _topic in handlerMap) {
                        topics += _topic + ',';
                    }
                    //request.shared = true;
                    var rq = {
                        url: that.root + '/' + that.path + '/' + that.globalTopicName,
                        transport: "websocket",
                        contentType: "application/json",
                        fallbackTransport: "streaming",
                        reconnectInterval: 4000
                    };

                    if (that.binary && !!window.ArrayBuffer) {
                        rq.webSocketBinaryType = "arraybuffer"
                        rq.headers = { "X-Atmosphere-Binary" : true };
                    }

                    if (!!window.EventSource) {
                        rq.fallbackTransport = 'sse';
                    }

                    // Allow the user to extend/override the request
                    rq = jQuery.extend(true, rq, options);
                    rq = jQuery.extend(true, rq, request);

                    rq.onTransportFailure = function (e, r) {
                        rq.onOpen = null;
                        r.headers = {
                            topics: Object.keys(handlerMap)
                        };
                        that.binary = false;
                    };

                    rq.onOpen = function (response) {
                        rq.onOpen = null;
                        for (var val in handlerMap) {
                            console.log("defer connecting topic: " + val);
                            _registerRequest(val);
                        }
                        loadingRequest = null;
                    };
                    loadingRequest = rq;
                    that.globalTopicSocket = socket.subscribe(rq);
                } else {
                    if (loadingRequest == null) {
                        console.log("connecting topic: " + topic);
                        _registerRequest(topic);
                    }
                }

                return handler;
            };

            that.unregisterHandlers = function (topic) {
                checkSpecified("topic", 'string', topic);
                delete handlerMap[topic];
                socket.unsubscribeUrl(that.root + '/' + that.path + '/' + that.globalTopicName);
                init();
            };

            that.unregisterHandler = function (topic, handler) {
                checkSpecified("topic", 'string', topic);
                checkSpecified("handler", 'function', handler);
                checkOpen();
                var handlers = handlerMap[topic];
                if (handlers) {
                    var idx = handlers.indexOf(handler);
                    if (idx != -1) handlers.splice(idx, 1);
                    if (handlers.length == 0) {
                        that.unregisterHandlers(topic);
                    }
                }
            };

            that.close = function () {
                state = grails.Events.CLOSING;
                socket.unsubscribe();
            };

            that.readyState = function () {
                return state;
            };

            function _registerRequest(topic) {
                if (!that.binary) {
                    that.globalTopicSocket.push(topic + '|' + "4");
                } else {
                    that.globalTopicSocket.push(stringToUint8(topic + '|' + "4").buffer);
                }
            }

            function init() {
                var connecting = function () {
                    state = grails.Events.OPEN;
                    if (that.onopen) {
                        that.onopen();
                    }
                };

                socket.onOpen = connecting;
                socket.onClose = function (e) {
                    state = grails.Events.CLOSED;
                    if (that.onclose) {
                        that.onclose();
                    }
                };

                var buffer;

                socket.onMessage = function (response) {
                    if (response.status == 200) {
                        var topic, type, data, envelope;
                        try {
                            if (response.responseBody instanceof ArrayBuffer) {
                                var dataUint = new Uint8Array(response.responseBody);
                                var endPacket = dataUint.length >= 3 &&
                                    "E".charCodeAt(0) == dataUint[dataUint.length - 3] &&
                                    "N".charCodeAt(0) == dataUint[dataUint.length - 2] &&
                                    "D".charCodeAt(0) == dataUint[dataUint.length - 1];

                                if (dataUint.length > 0 && !endPacket) {
                                    if (buffer == null) {
                                        buffer = response.responseBody;
                                    } else {
                                        var tmp = new Uint8Array(dataUint.length + buffer.byteLength);
                                        tmp.set(new Uint8Array(buffer), 0);
                                        tmp.set(dataUint, buffer.byteLength);
                                        buffer = tmp.buffer;
                                    }
                                }

                                if (!endPacket) {
                                    return;
                                }

                                if (!buffer || buffer.byteLength == 0) {
                                    buffer = null;
                                    return;
                                }

                                var bytes = new Uint8Array(buffer);
                                var tmp = "";
                                for (var i = 0; i < bytes.length; i++) {
                                    if (bytes[i] == "|".charCodeAt(0)) {
                                        if (tmp.length > 0) {
                                            if (!topic) {
                                                topic = tmp;
                                                tmp = "";
                                            } else if (!type) {
                                                type = tmp;
                                                data = bytes.buffer.slice(i + 1);
                                                break;
                                            }
                                        }
                                    } else {
                                        tmp += String.fromCharCode(bytes[i]);
                                    }
                                }
                                buffer = null;

                            } else if (response.responseBody.length > 0) {
                                var _data = response.responseBody.split('|')
                                topic = _data[0];
                                type = _data[1];
                                data = _data[2];
                            }
                        } catch (e) {
                            if (console != 'undefined') {
                                console.log(e.stack);
                                console.log('discarded message: ' + response.responseBody);
                                buffer = null;
                            }
                            return;
                        }
                        var handlers = handlerMap[topic ? topic : that.globalTopicName];
                        if (handlers) {
                            if (type == '5') {
                                that.unregisterHandlers(topic);
                                return;
                            }
                            // We make a copy since the handler might get unregistered from within the
                            // handler itself, which would screw up our iteration
                            var copy = handlers.slice(0);
                            if (type == '0' || type == '2') {
                                envelope = data;
                            } else if (type == '1') {
                                envelope = jQuery.parseJSON(
                                    data instanceof ArrayBuffer ?
                                        String.fromCharCode.apply(null, new Uint8Array(data)) :
                                        data);
                            }
                            for (var i = 0; i < copy.length; i++) {
                                copy[i](envelope, topic, response, type);
                            }
                        }
                    }
                };

                that.on(that.globalTopicName, function (data, e) {
                    if (that.onglobalmessage) {
                        that.onglobalmessage(data);
                    }
                });
            }

            function checkOpen() {
                if (state != grails.Events.OPEN) {
                    throw new Error('INVALID_STATE_ERR');
                }
            }

            function stringToUint8(str) {
                str = str + '|';
                var uint = new Uint8Array(str.length);
                for (var i = 0, j = str.length; i < j; ++i) {
                    uint[i] = str.charCodeAt(i);
                }
                return uint;
            }

            function appendBuffer(uint, buffer) {
                var tmp = new Uint8Array(uint.length + buffer.byteLength);
                tmp.set(uint, 0);
                tmp.set(new Uint8Array(buffer), uint.length);
                return tmp.buffer;
            }

            function checkSpecified(paramName, paramType, param, optional) {
                if (!optional && !param) {
                    throw new Error("Parameter " + paramName + " must be specified");
                }
                if (param && typeof param != paramType) {
                    throw new Error("Parameter " + paramName + " must be of type " + paramType);
                }
            }

            init();

        };

        grails.Events.CONNECTING = 0;
        grails.Events.OPEN = 1;
        grails.Events.CLOSING = 2;
        grails.Events.CLOSED = 3;

    }
})(jQuery);
<|endoftext|>


<commit_before>
    export default ['hs.layerEditor.sublayerService','hs.layermanager.service',
    function(subLayerService, LayMan){
        return {
                template: require('./partials/sub-layer-checkboxes.html'),

            controller: ['$scope', function ($scope) {

                $scope.checkedSubLayers = subLayerService.checkedSubLayers;
                $scope.withChildren = subLayerService.withChildren;
                $scope.expanded = false;

                $scope.getSubLayers = function (){
                   return subLayerService.getSubLayers();
                }

                $scope.subLayerIsString= function(subLayer){
                    return typeof subLayer == 'string'
                };

                $scope.toggleExpanded = function(){
                        $scope.expanded = !$scope.expanded
                };
                $scope.subLayerSelected = function (sublayer,state){
                    if (angular.isDefined(sublayer) && sublayer.Layer){
                        angular.forEach(sublayer.Layer,function(children){
                            angular.extend($scope.checkedSubLayers, {[children.Name]:state});
                        })
                    }
                    return subLayerService.subLayerSelected();
                };

            }]
        };
    }]
<commit_msg>Lint checkboxes directive<commit_after>
export default ['hs.layerEditor.sublayerService', 'hs.layermanager.service',
  function(subLayerService, LayMan) {
    return {
      template: require('./partials/sub-layer-checkboxes.html'),

      controller: ['$scope', function ($scope) {

        $scope.checkedSubLayers = subLayerService.checkedSubLayers;
        $scope.withChildren = subLayerService.withChildren;
        $scope.expanded = false;

        $scope.getSubLayers = function () {
          return subLayerService.getSubLayers();
        };

        $scope.subLayerIsString = function(subLayer) {
          return typeof subLayer == 'string';
        };

        $scope.toggleExpanded = function() {
          $scope.expanded = !$scope.expanded;
        };
        $scope.subLayerSelected = function (sublayer, state) {
          if (angular.isDefined(sublayer) && sublayer.Layer) {
            angular.forEach(sublayer.Layer, (children) => {
              angular.extend($scope.checkedSubLayers, {[children.Name]: state});
            });
          }
          return subLayerService.subLayerSelected();
        };

      }]
    };
  }];
<|endoftext|>


<commit_before>const Cron = require('../util/Cron');

/**
 * The structure for future tasks to be run
 */
class ScheduledTask {

	/**
	 * @typedef  {Object} ScheduledTaskOptions
	 * @property {string} [id] The ID for the task. By default, it generates one in base36
	 * @property {boolean} [catchUp=true] If the task should try to catch up if the bot is down
	 * @property {*} [data] The data to pass to the Task piece when the ScheduledTask is ready for execution
	 */

	/**
	 * @typedef  {Object} ScheduledTaskUpdateOptions
	 * @property {string} [repeat] The {@link Cron} pattern
	 * @property {Date} [time] The time the current task ends at
	 * @property {boolean} [catchUp] If the task should try to catch up if the bot is down
	 * @property {*} [data] The data to pass to the Task piece when the ScheduledTask is ready for execution
	 */

	/**
	 * @typedef  {Object} ScheduledTaskJSON
	 * @property {string} id The task's ID
	 * @property {string} taskName The name of the Task piece this will execute
	 * @property {number} time The UNIX timestamp for when this task ends at
	 * @property {boolean} catchUp If the task should try to catch up if the bot is down
	 * @property {string} [repeat] The {@link Cron} pattern
	 * @property {*} [data] The data to pass to the Task piece when the ScheduledTask is ready for execution
	 */

	/**
	 * Initializes a new ScheduledTask
	 * @since 0.5.0
	 * @param {KlasaClient} client The client that initialized this instance
	 * @param {string} taskName The name of the task this ScheduledTask is for
	 * @param {(Date|number|string)} time The time or Cron pattern
	 * @param {ScheduledTaskOptions} [options={}] The options for this ScheduledTask instance
	 */
	constructor(client, taskName, time, options = {}) {
		const [_time, _recurring] = this.constructor._resolveTime(time);

		/**
		 * The Client instance that initialized this instance
		 * @since 0.5.0
		 * @name ScheduledTask#client
		 * @type {KlasaClient}
		 * @readonly
		 */
		Object.defineProperty(this, 'client', { value: client });

		/**
		 * The name of the Task this scheduled task will run
		 * @since 0.5.0
		 * @type {string}
		 */
		this.taskName = taskName;

		/**
		 * Whether this scheduled task is scheduled with the {@link Cron} pattern
		 * @since 0.5.0
		 * @type {?Cron}
		 */
		this.recurring = _recurring;

		/**
		 * The Date when this scheduled task ends
		 * @since 0.5.0
		 * @type {?Date}
		 */
		this.time = 'time' in options ? new Date(options.time) : _time;

		/**
		 * The id for this scheduled task
		 * @since 0.5.0
		 * @type {string}
		 */
		this.id = options.id || this.constructor._generateID(this.client);

		/**
		 * If the task should catch up in the event the bot is down
		 * @since 0.5.0
		 * @type {string}
		 */
		this.catchUp = 'catchUp' in options ? options.catchUp : true;

		/**
		 * The stored metadata to send to the Task
		 * @since 0.5.0
		 * @type {*}
		 */
		this.data = 'data' in options ? options.data : null;

		this.constructor._validate(this);
	}

	/**
	 * The Schedule class that manages all scheduled tasks
	 * @since 0.5.0
	 * @name ScheduledTask#store
	 * @type {Schedule}
	 * @readonly
	 */
	get store() {
		return this.client.schedule;
	}

	/**
	 * The Task instance this scheduled task will run
	 * @since 0.5.0
	 * @type {?Task}
	 * @readonly
	 */
	get task() {
		return this.client.tasks.get(this.taskName) || null;
	}

	/**
	 * Run the current task and bump it if needed
	 * @since 0.5.0
	 * @returns {this}
	 */
	async run() {
		if (!this.task || !this.task.enabled) return this;
		try {
			await this.task.run(this.data);
		} catch (err) {
			this.client.emit('taskError', this, this.task, err);
		}
		if (!this.recurring) return this.delete();
		return this.update({ time: this.recurring });
	}

	/**
	 * Update the task
	 * @since 0.5.0
	 * @param {ScheduledTaskUpdateOptions} options The options to update
	 * @returns {this}
	 * @example
	 * // Update the data from the current scheduled task. Let's say I want to change the reminder content to remind me
	 * // another thing
	 * ScheduledTask.update({ data: { content: 'Woo! I edited this reminder\'s content!' } });
	 *
	 * // But you can also update the time this will end at, for example, to change it so it ends in 1 hour:
	 * ScheduledTask.update({ time: Date.now() + 60000 * 60 });
	 */
	async update({ time, data, catchUp } = {}) {
		if (time) {
			const [_time, _cron] = this.constructor._resolveTime(time);
			this.time = _time;
			this.store.tasks.splice(this.store.tasks.indexOf(this), 1);
			this.store._insert(this);
			this.recurring = _cron;
		}
		if (data) this.data = data;
		if (typeof catchUp !== 'undefined') this.catchUp = catchUp;

		// Sync the database if some of the properties changed or the time changed manually
		// (recurring tasks bump the time automatically)
		const _index = this.store._tasks.findIndex(entry => entry.id === this.id);
		if (_index !== -1) await this.client.configs.update('schedules', this.toJSON(), { arrayPosition: _index });

		return this;
	}

	/**
	 * Delete the task
	 * @since 0.5.0
	 * @returns {Promise<Schedule>}
	 * @example
	 * ScheduledTask.delete()
	 *     .then(() => console.log('Successfully deleted the task'))
	 *     .catch(console.error);
	 */
	delete() {
		return this.store.delete(this.id);
	}

	/**
	 * Override for JSON.stringify
	 * @since 0.5.0
	 * @returns {ScheduledTaskJSON}
	 */
	toJSON() {
		const object = { id: this.id, taskName: this.taskName, time: this.time.getTime(), catchUp: this.catchUp };
		if (this.recurring) object.repeat = this.recurring.cron;
		if (typeof this.data !== 'undefined') object.data = this.data;

		return object;
	}

	/**
	 * Resolve the time and cron
	 * @since 0.5.0
	 * @param {(Date|number|Cron|string)} time The time or Cron pattern
	 * @returns {any[]}
	 * @private
	 */
	static _resolveTime(time) {
		if (time instanceof Date) return [time, null];
		if (time instanceof Cron) return [time.next(), time];
		if (typeof time === 'number') return [new Date(time), null];
		if (typeof time === 'string') {
			const cron = new Cron(time);
			return [cron.next(), cron];
		}
		throw new Error('invalid time passed');
	}

	/**
	 * Generate a new ID based on timestamp and shard
	 * @since 0.5.0
	 * @param {KlasaClient} client The Discord client
	 * @returns {string}
	 * @private
	 */
	static _generateID(client) {
		return Date.now().toString(36) + (client.shard ? client.shard.id.toString(36) : '') + String.fromCharCode((1 % 26) + 97);
	}

	/**
	 * Validate a task
	 * @since 0.5.0
	 * @param {ScheduledTask} st The task to validate
	 * @private
	 */
	static _validate(st) {
		if (!st.task) throw new Error('invalid task');
		if (!st.time) throw new Error('time or repeat option required');
	}

}

module.exports = ScheduledTask;
<commit_msg>Ensure long running tasks are run once<commit_after>const Cron = require('../util/Cron');

/**
 * The structure for future tasks to be run
 */
class ScheduledTask {

	/**
	 * @typedef  {Object} ScheduledTaskOptions
	 * @property {string} [id] The ID for the task. By default, it generates one in base36
	 * @property {boolean} [catchUp=true] If the task should try to catch up if the bot is down
	 * @property {*} [data] The data to pass to the Task piece when the ScheduledTask is ready for execution
	 */

	/**
	 * @typedef  {Object} ScheduledTaskUpdateOptions
	 * @property {string} [repeat] The {@link Cron} pattern
	 * @property {Date} [time] The time the current task ends at
	 * @property {boolean} [catchUp] If the task should try to catch up if the bot is down
	 * @property {*} [data] The data to pass to the Task piece when the ScheduledTask is ready for execution
	 */

	/**
	 * @typedef  {Object} ScheduledTaskJSON
	 * @property {string} id The task's ID
	 * @property {string} taskName The name of the Task piece this will execute
	 * @property {number} time The UNIX timestamp for when this task ends at
	 * @property {boolean} catchUp If the task should try to catch up if the bot is down
	 * @property {string} [repeat] The {@link Cron} pattern
	 * @property {*} [data] The data to pass to the Task piece when the ScheduledTask is ready for execution
	 */

	/**
	 * Initializes a new ScheduledTask
	 * @since 0.5.0
	 * @param {KlasaClient} client The client that initialized this instance
	 * @param {string} taskName The name of the task this ScheduledTask is for
	 * @param {(Date|number|string)} time The time or Cron pattern
	 * @param {ScheduledTaskOptions} [options={}] The options for this ScheduledTask instance
	 */
	constructor(client, taskName, time, options = {}) {
		const [_time, _recurring] = this.constructor._resolveTime(time);

		/**
		 * The Client instance that initialized this instance
		 * @since 0.5.0
		 * @name ScheduledTask#client
		 * @type {KlasaClient}
		 * @readonly
		 */
		Object.defineProperty(this, 'client', { value: client });

		/**
		 * The name of the Task this scheduled task will run
		 * @since 0.5.0
		 * @type {string}
		 */
		this.taskName = taskName;

		/**
		 * Whether this scheduled task is scheduled with the {@link Cron} pattern
		 * @since 0.5.0
		 * @type {?Cron}
		 */
		this.recurring = _recurring;

		/**
		 * The Date when this scheduled task ends
		 * @since 0.5.0
		 * @type {?Date}
		 */
		this.time = 'time' in options ? new Date(options.time) : _time;

		/**
		 * The id for this scheduled task
		 * @since 0.5.0
		 * @type {string}
		 */
		this.id = options.id || this.constructor._generateID(this.client);

		/**
		 * If the task should catch up in the event the bot is down
		 * @since 0.5.0
		 * @type {string}
		 */
		this.catchUp = 'catchUp' in options ? options.catchUp : true;

		/**
		 * The stored metadata to send to the Task
		 * @since 0.5.0
		 * @type {*}
		 */
		this.data = 'data' in options ? options.data : null;

		this.constructor._validate(this);
	}

	/**
	 * The Schedule class that manages all scheduled tasks
	 * @since 0.5.0
	 * @name ScheduledTask#store
	 * @type {Schedule}
	 * @readonly
	 */
	get store() {
		return this.client.schedule;
	}

	/**
	 * The Task instance this scheduled task will run
	 * @since 0.5.0
	 * @type {?Task}
	 * @readonly
	 */
	get task() {
		return this.client.tasks.get(this.taskName) || null;
	}

	/**
	 * Run the current task and bump it if needed
	 * @since 0.5.0
	 * @returns {this}
	 */
	async run() {
		if (!this.task || !this.task.enabled) return this;
		try {
			this.task.disable();
			await this.task.run(this.data);
			this.task.enable();
		} catch (err) {
			this.client.emit('taskError', this, this.task, err);
		}
		if (!this.recurring) return this.delete();
		return this.update({ time: this.recurring });
	}

	/**
	 * Update the task
	 * @since 0.5.0
	 * @param {ScheduledTaskUpdateOptions} options The options to update
	 * @returns {this}
	 * @example
	 * // Update the data from the current scheduled task. Let's say I want to change the reminder content to remind me
	 * // another thing
	 * ScheduledTask.update({ data: { content: 'Woo! I edited this reminder\'s content!' } });
	 *
	 * // But you can also update the time this will end at, for example, to change it so it ends in 1 hour:
	 * ScheduledTask.update({ time: Date.now() + 60000 * 60 });
	 */
	async update({ time, data, catchUp } = {}) {
		if (time) {
			const [_time, _cron] = this.constructor._resolveTime(time);
			this.time = _time;
			this.store.tasks.splice(this.store.tasks.indexOf(this), 1);
			this.store._insert(this);
			this.recurring = _cron;
		}
		if (data) this.data = data;
		if (typeof catchUp !== 'undefined') this.catchUp = catchUp;

		// Sync the database if some of the properties changed or the time changed manually
		// (recurring tasks bump the time automatically)
		const _index = this.store._tasks.findIndex(entry => entry.id === this.id);
		if (_index !== -1) await this.client.configs.update('schedules', this.toJSON(), { arrayPosition: _index });

		return this;
	}

	/**
	 * Delete the task
	 * @since 0.5.0
	 * @returns {Promise<Schedule>}
	 * @example
	 * ScheduledTask.delete()
	 *     .then(() => console.log('Successfully deleted the task'))
	 *     .catch(console.error);
	 */
	delete() {
		return this.store.delete(this.id);
	}

	/**
	 * Override for JSON.stringify
	 * @since 0.5.0
	 * @returns {ScheduledTaskJSON}
	 */
	toJSON() {
		const object = { id: this.id, taskName: this.taskName, time: this.time.getTime(), catchUp: this.catchUp };
		if (this.recurring) object.repeat = this.recurring.cron;
		if (typeof this.data !== 'undefined') object.data = this.data;

		return object;
	}

	/**
	 * Resolve the time and cron
	 * @since 0.5.0
	 * @param {(Date|number|Cron|string)} time The time or Cron pattern
	 * @returns {any[]}
	 * @private
	 */
	static _resolveTime(time) {
		if (time instanceof Date) return [time, null];
		if (time instanceof Cron) return [time.next(), time];
		if (typeof time === 'number') return [new Date(time), null];
		if (typeof time === 'string') {
			const cron = new Cron(time);
			return [cron.next(), cron];
		}
		throw new Error('invalid time passed');
	}

	/**
	 * Generate a new ID based on timestamp and shard
	 * @since 0.5.0
	 * @param {KlasaClient} client The Discord client
	 * @returns {string}
	 * @private
	 */
	static _generateID(client) {
		return Date.now().toString(36) + (client.shard ? client.shard.id.toString(36) : '') + String.fromCharCode((1 % 26) + 97);
	}

	/**
	 * Validate a task
	 * @since 0.5.0
	 * @param {ScheduledTask} st The task to validate
	 * @private
	 */
	static _validate(st) {
		if (!st.task) throw new Error('invalid task');
		if (!st.time) throw new Error('time or repeat option required');
	}

}

module.exports = ScheduledTask;
<|endoftext|>


<commit_before>'use strict';

var afDropzone = function(form, url, target) {
	var afdz = new Dropzone(form, {
		url: url,
		thumbnailWidth:200,
		thumbnailHeight:200,
		maxFilesize:32,
		clickable:target,
		dictDefaultMessage:'',
		withCredentials:true,
		init: function() {
			this.on('addedfile', function(file){
				$(file.previewElement).insertAfter(target);
			});
		},
		success: function(file, text) {
			$(text).insertAfter( $(file.previewElement) );
			$(file.previewElement).remove();
		},
		error: function(file, message) {
			if (file.retry === undefined) file.retry = 0;
			file.retry++;
			if (file.retry < 3) {
				afdz.uploadFile(file);
			} else {
				console.log(file, message);
				alert('File: ' + file.name + '\n\n' + message);
				$(file.previewElement).remove();
			}
		},
	});
}
<commit_msg>afDropzone now accepts an optional maxSize parameter maxSize is the largest possible filesize, in Megabytes<commit_after>'use strict';

var afDropzone = function(form, url, target, maxSize) {
	var afdz = new Dropzone(form, {
		url: url,
		thumbnailWidth:200,
		thumbnailHeight:200,
		maxFilesize:maxSize || 32,
		clickable:target,
		dictDefaultMessage:'',
		withCredentials:true,
		init: function() {
			this.on('addedfile', function(file){
				$(file.previewElement).insertAfter(target);
			});
		},
		success: function(file, text) {
			$(text).insertAfter( $(file.previewElement) );
			$(file.previewElement).remove();
		},
		error: function(file, message) {
			if (file.retry === undefined) file.retry = 0;
			file.retry++;
			if (file.retry < 3) {
				afdz.uploadFile(file);
			} else {
				console.log(file, message);
				alert('File: ' + file.name + '\n\n' + message);
				$(file.previewElement).remove();
			}
		},
	});
}
<|endoftext|>