<commit_before>var buster = require("buster-core");
var busterPromise = require("buster-promise");
var argument = require("./argument");

module.exports = {
    create: function (opts) {
        if (opts.length == 0) {
            throw new Error("createOption was called with no arguments.");
        }

        var instance = Object.create(this);
        buster.extend(instance, Object.create(argument));
        instance.assignOptions(opts);
        instance.reset();
        return instance;
    },

    value: function () {
        return ("actualValue" in this) ? this.actualValue : this.defaultValue;
    },

    assignOptions: function (opts) {
        for (var i = 0, ii = opts.length; i < ii; i++) {
            for (var j = i + 1, jj = opts.length; j < jj; j++) {
                if (opts[i] == opts[j]) {
                    throw new Error("Duplicate option (" + opts[i] + ")");
                }
            }
        }

        this.doubleDashOptions = [];
        this.singleDashOptions = [];

        for (var i = 0, ii = opts.length; i < ii; i++) {
            var option = opts[i];
            if (this.isDoubleDashOption(option)) {
                this.doubleDashOptions.push(option.slice(2));
            } else if (this.isSingleDashOption(option)) {
                if (!(/^\-[^-]$/).test(option)) {
                    throw new Error("A single dash option can only have one character ("
                                    + option + ").");
                }

                this.singleDashOptions.push(option.slice(1));
            } else {
                // Not handled yet.
            }
        }

        this.signature = opts.join("/");
    },

    reset: function () {
        this.handled = false;
        this.isSet = false;
        this.timesSet = 0;
        delete this.actualValue;
    },

    handle: function (args) {
        this.handled = true;

        var arg = args[0];

        var equalsPos = arg.indexOf("=");
        if (equalsPos != -1) {
            if (!this.hasValue) {
                args.shift();
                return this.shouldNotHaveValuePromise();
            }

            var valueFromEqualSign = arg.slice(equalsPos + 1)
            this.actualValue = valueFromEqualSign;
            this.isSet = true;
            args.shift();
            return this.validatorPromise();
        }

        if (this.isDoubleDashOption(arg)) {
            return this.handleDoubleDashOption(args, arg);
        } else if (this.isSingleDashOption(arg)) {
            return this.handleSingleDashOption(args, arg);
        }
    },

    handleSingleDashOption: function (args, arg) {
        for (var i = 0, ii = this.singleDashOptions.length; i < ii; i++) {
            var option = this.singleDashOptions[i];

            if (option == arg[1]) {
                args.shift();

                this.isSet = true;

                if (this.hasValue) {
                    if (arg.length > 2) {
                        this.actualValue = arg.slice(2);
                    } else {
                        if (!this.setValueFromNextArgument(args)) {
                            return this.missingValueErrorPromise();
                        }
                    }
                } else {
                    var argOptions = [];
                    for (var i = 1, ii = arg.length; i < ii; i++) {
                        argOptions.push(arg[i]);
                    }

                    for (var j = 0; j < argOptions.length; j++) {
                        if (argOptions[j] == option) {
                            this.timesSet++;
                            argOptions.splice(j, 1);
                        }
                    }

                    // The option contained options that weren't 'option', i.e.
                    // "-abc" for the option "-a". Move "-bc" back to args for
                    // further processing.
                    if (argOptions.length > 0) {
                        args.unshift("-" + argOptions.join(""));
                    }
                }

                return this.validatorPromise();
            }
        }
    },

    handleDoubleDashOption: function (args, arg) {
        var argOption = arg.slice(2);
        for (var i = 0, ii = this.doubleDashOptions.length; i < ii; i++) {
            var option = this.doubleDashOptions[i];

            if (option == argOption) {
                args.shift();

                this.isSet = true;
                this.timesSet++;

                if (this.hasValue) {
                    if (!this.setValueFromNextArgument(args)) {
                        return this.missingValueErrorPromise();
                    }
                }

                return this.validatorPromise();
            }
        }
    },

    setValueFromNextArgument: function (args) {
        if (args.length > 0 && !this.isOption(args[0])) {
            this.actualValue = args.shift();
        }

        return this.acceptsValueAbsence || ("actualValue" in this);
    },

    shouldNotHaveValuePromise: function () {
        var errorPromise = busterPromise.create();
        errorPromise.reject(this.signature + " does not have a value.");
        return errorPromise;
    },

    missingValueErrorPromise: function () {
        var errorPromise = busterPromise.create();
        errorPromise.reject("No value specified for " + this.signature);
        return errorPromise;
    },

    isDoubleDashOption: function (option) {
        return (/^\-\-./).test(option);
    },

    isSingleDashOption: function (option) {
        return (/^\-./).test(option);
    },

    isOption: function (option) {
        return this.isDoubleDashOption(option) || this.isSingleDashOption(option);
    },

    intersects: function (otherOption) {
        if (this.arrayIntersects(this.singleDashOptions, otherOption.singleDashOptions)) {
            return true;
        }

        if (this.arrayIntersects(this.doubleDashOptions, otherOption.doubleDashOptions)) {
            return true;
        }
    },

    arrayIntersects: function (a, b) {
        for (var i = 0, ii = a.length; i < ii; i++)
            for (var j = 0, jj = b.length; j < jj; j++)
                if (a[i] == b[j]) return true;
    },

    hasOption: function (option) {
        // A hack.
        // Creating something that looks like an option, so we can exploit
        // intersects(). Gotta slice "option" too, since it's -p and this
        // module stores "p" not "-p" in singleDashOptions etc.
        return this.intersects({singleDashOptions: [option.slice(1)], doubleDashOptions: [option.slice(2)]});
    },
};<commit_msg>Simplifying handling of grouped flags with no value.<commit_after>var buster = require("buster-core");
var busterPromise = require("buster-promise");
var argument = require("./argument");

module.exports = {
    create: function (opts) {
        if (opts.length == 0) {
            throw new Error("createOption was called with no arguments.");
        }

        var instance = Object.create(this);
        buster.extend(instance, Object.create(argument));
        instance.assignOptions(opts);
        instance.reset();
        return instance;
    },

    value: function () {
        return ("actualValue" in this) ? this.actualValue : this.defaultValue;
    },

    assignOptions: function (opts) {
        for (var i = 0, ii = opts.length; i < ii; i++) {
            for (var j = i + 1, jj = opts.length; j < jj; j++) {
                if (opts[i] == opts[j]) {
                    throw new Error("Duplicate option (" + opts[i] + ")");
                }
            }
        }

        this.doubleDashOptions = [];
        this.singleDashOptions = [];

        for (var i = 0, ii = opts.length; i < ii; i++) {
            var option = opts[i];
            if (this.isDoubleDashOption(option)) {
                this.doubleDashOptions.push(option.slice(2));
            } else if (this.isSingleDashOption(option)) {
                if (!(/^\-[^-]$/).test(option)) {
                    throw new Error("A single dash option can only have one character ("
                                    + option + ").");
                }

                this.singleDashOptions.push(option.slice(1));
            } else {
                // Not handled yet.
            }
        }

        this.signature = opts.join("/");
    },

    reset: function () {
        this.handled = false;
        this.isSet = false;
        this.timesSet = 0;
        delete this.actualValue;
    },

    handle: function (args) {
        this.handled = true;

        var arg = args[0];

        var equalsPos = arg.indexOf("=");
        if (equalsPos != -1) {
            if (!this.hasValue) {
                args.shift();
                return this.shouldNotHaveValuePromise();
            }

            var valueFromEqualSign = arg.slice(equalsPos + 1)
            this.actualValue = valueFromEqualSign;
            this.isSet = true;
            args.shift();
            return this.validatorPromise();
        }

        if (this.isDoubleDashOption(arg)) {
            return this.handleDoubleDashOption(args, arg);
        } else if (this.isSingleDashOption(arg)) {
            return this.handleSingleDashOption(args, arg);
        }
    },

    handleSingleDashOption: function (args, arg) {
        for (var i = 0, ii = this.singleDashOptions.length; i < ii; i++) {
            var option = this.singleDashOptions[i];

            if (option == arg[1]) {
                args.shift();

                this.isSet = true;

                if (this.hasValue) {
                    if (arg.length > 2) {
                        this.actualValue = arg.slice(2);
                    } else {
                        if (!this.setValueFromNextArgument(args)) {
                            return this.missingValueErrorPromise();
                        }
                    }
                } else {
                    var unusedFlags = [];

                    for (var j = 1; j < arg.length; j++) {
                        if (arg[j] == option) {
                            this.timesSet++;
                        } else {
                            unusedFlags.push(arg[j]);
                        }
                    }

                    if (unusedFlags.length > 0) {
                        args.unshift("-" + unusedFlags.join(""));
                    }
                }

                return this.validatorPromise();
            }
        }
    },

    handleDoubleDashOption: function (args, arg) {
        var argOption = arg.slice(2);
        for (var i = 0, ii = this.doubleDashOptions.length; i < ii; i++) {
            var option = this.doubleDashOptions[i];

            if (option == argOption) {
                args.shift();

                this.isSet = true;
                this.timesSet++;

                if (this.hasValue) {
                    if (!this.setValueFromNextArgument(args)) {
                        return this.missingValueErrorPromise();
                    }
                }

                return this.validatorPromise();
            }
        }
    },

    setValueFromNextArgument: function (args) {
        if (args.length > 0 && !this.isOption(args[0])) {
            this.actualValue = args.shift();
        }

        return this.acceptsValueAbsence || ("actualValue" in this);
    },

    shouldNotHaveValuePromise: function () {
        var errorPromise = busterPromise.create();
        errorPromise.reject(this.signature + " does not have a value.");
        return errorPromise;
    },

    missingValueErrorPromise: function () {
        var errorPromise = busterPromise.create();
        errorPromise.reject("No value specified for " + this.signature);
        return errorPromise;
    },

    isDoubleDashOption: function (option) {
        return (/^\-\-./).test(option);
    },

    isSingleDashOption: function (option) {
        return (/^\-./).test(option);
    },

    isOption: function (option) {
        return this.isDoubleDashOption(option) || this.isSingleDashOption(option);
    },

    intersects: function (otherOption) {
        if (this.arrayIntersects(this.singleDashOptions, otherOption.singleDashOptions)) {
            return true;
        }

        if (this.arrayIntersects(this.doubleDashOptions, otherOption.doubleDashOptions)) {
            return true;
        }
    },

    arrayIntersects: function (a, b) {
        for (var i = 0, ii = a.length; i < ii; i++)
            for (var j = 0, jj = b.length; j < jj; j++)
                if (a[i] == b[j]) return true;
    },

    hasOption: function (option) {
        // A hack.
        // Creating something that looks like an option, so we can exploit
        // intersects(). Gotta slice "option" too, since it's -p and this
        // module stores "p" not "-p" in singleDashOptions etc.
        return this.intersects({singleDashOptions: [option.slice(1)], doubleDashOptions: [option.slice(2)]});
    },
};<|endoftext|>


<commit_before>/*global angular*/
(function () {
    'use strict';
    angular.module('core').directive('slidePart', ['ScriptInjector',
        function (ScriptInjector) {
            return {
                link: function postLink(scope, element, attrs) {
                    if (scope.referenceSlide
                            && scope.referenceSlide.fireSetTemplateElementEvent
                            && element.attr("type") !== "script") {
                        scope.$emit("setTemplateElement", attrs.member, {
                            type: attrs.type || 'text',
                            label: attrs.label || attrs.member
                        });
                    }

                    var slide;
                    var templatePath;
                    var oldContent;
                    var update = function (content) {
                        if (oldContent==content) {
                            return;
                        }
                        oldContent = content;
                        if (element.attr("type") === "script" && element.attr("url") && !scope.isEdit) {
                            scope.$emit("getTemplatePath", function (templatePath) {
                                ScriptInjector.inject(templatePath + element.attr("url"), element.attr("tag") || element.attr("url"));
                            });
                        }

                        if (element[0].tagName === "IMG") {
                            element[0].src = content || 'modules/slideshows/css/img/default.jpg';
                            return;
                        }
                        if (content) {
                            var text = content;
                            if (element.attr("encoded") === "true") {
                                eval("text='" + content + "'");
                            }
                            element.html(text);
                        }
                    };
                    scope.$on("updateSlideContentPart", function (event, content, member, slidePartId) {
                        if (slidePartId!=scope.$id || member!=attrs.member) {
                            return;
                        }
                        update(content);
                    });
                    var loadSlideContent = function () {
                        scope.$emit("getSlideContentPart", attrs.member, function (content) {
                            update(content);
                        });
                    };

                    loadSlideContent();
                }
            };
        }]);
}());
<commit_msg>Some code styling issues fixed.<commit_after>/*global angular*/
(function () {
    'use strict';
    angular.module('core').directive('slidePart', ['ScriptInjector',
        function (ScriptInjector) {
            return {
                link: function postLink(scope, element, attrs) {
                    if (scope.referenceSlide
                            && scope.referenceSlide.fireSetTemplateElementEvent
                            && element.attr("type") !== "script") {
                        scope.$emit("setTemplateElement", attrs.member, {
                            type: attrs.type || 'text',
                            label: attrs.label || attrs.member
                        });
                    }

                    var slide;
                    var templatePath;
                    var oldContent;
                    var update = function (content) {
                        if (oldContent === content) {
                            return;
                        }
                        oldContent = content;
                        if (element.attr("type") === "script" && element.attr("url") && !scope.isEdit) {
                            scope.$emit("getTemplatePath", function (templatePath) {
                                ScriptInjector.inject(templatePath + element.attr("url"), element.attr("tag") || element.attr("url"));
                            });
                        }

                        if (element[0].tagName === "IMG") {
                            element[0].src = content || 'modules/slideshows/css/img/default.jpg';
                            return;
                        }
                        if (content) {
                            var text = content;
                            if (element.attr("encoded") === "true") {
                                eval("text='" + content + "'");
                            }
                            element.html(text);
                        }
                    };
                    scope.$on("updateSlideContentPart", function (event, content, member, slidePartId) {
                        if (slidePartId !== scope.$id || member !== attrs.member) {
                            return;
                        }
                        update(content);
                    });
                    var loadSlideContent = function () {
                        scope.$emit("getSlideContentPart", attrs.member, function (content) {
                            update(content);
                        });
                    };

                    loadSlideContent();
                }
            };
        }]);
}());
<|endoftext|>


<commit_before>/*
 * Chrome-Last.fm-Scrobbler Reddit Playlister Connector
 *
 * based on Pandora.com Jordan Perr --- http://jperr.com --- jordan[at]jperr[dot]com
 *
 * with VERY slight modifications by lcz
 * http://github.com/thricedotted || http://last.fm/user/thricedotted
 *
 */

/**
 * Trim whitespaces from both endings of the string
 */
function trim(str) {
   return str.replace(/^\s+|\s+$/g, '');
}

/**
 * Find first occurence of possible separator in given string
 * and return separator's position and size in chars or null
 */
function findSeparator(str) {
   // care - minus vs hyphen
   var separators = [' - ', ' – ', '-', '–', ':'];

   for (i in separators) {
      var sep = separators[i];
      var index = str.indexOf(sep);
      if (index > -1)
         return { index: index, length: sep.length };
   }

   return null;
}

/**
 * Parse given string into artist and track, assume common order Art - Ttl
 * @return {artist, track}
 */
function parseInfo(artistTitle) {
   var artist = '';
   var track = '';

   var separator = findSeparator(artistTitle);
   if (separator == null)
      return { artist: '', track: '' };

   artist = artistTitle.substr(0, separator.index);
   track = artistTitle.substr(separator.index + separator.length);

   return cleanArtistTrack(artist, track);
}

/**
 * Clean non-informative garbage from title
 */
function cleanArtistTrack(artist, track) {

   // Do some cleanup
   artist = artist.replace(/^\s+|\s+$/g,'');
   track = track.replace(/^\s+|\s+$/g,'');

   // Strip crap
   track = track.replace(/\s*\*+\s?\S+\s?\*+$/, ''); // **NEW**
   track = track.replace(/\s*\[[^\]]+\]$/, ''); // [whatever]
   track = track.replace(/\s*\([^\)]*version\)$/i, ''); // (whatever version)
   track = track.replace(/\s*\.(avi|wmv|mpg|mpeg|flv)$/i, ''); // video extensions
   track = track.replace(/\s*(of+icial\s*)?(music\s*)?video/i, ''); // (official)? (music)? video
   track = track.replace(/\s*\(\s*of+icial\s*\)/i, ''); // (official)
   track = track.replace(/\s*\(\s*[0-9]{4}\s*\)/i, ''); // (1999)
   track = track.replace(/\s+\(\s*(HD|HQ)\s*\)$/, ''); // HD (HQ)
   track = track.replace(/\s+(HD|HQ)\s*$/, ''); // HD (HQ)
   track = track.replace(/\s*video\s*clip/i, ''); // video clip
   track = track.replace(/\s+\(?live\)?$/i, ''); // live
   track = track.replace(/\(\s*\)/, ''); // Leftovers after e.g. (official video)
   track = track.replace(/^(|.*\s)"(.*)"(\s.*|)$/, '$2'); // Artist - The new "Track title" featuring someone
   track = track.replace(/^(|.*\s)'(.*)'(\s.*|)$/, '$2'); // 'Track title'
   track = track.replace(/^[\/\s,:;~-]+/, ''); // trim starting white chars and dash
   track = track.replace(/[\/\s,:;~-]+$/, ''); // trim trailing white chars and dash

   return {artist: artist, track: track};
}

/********* Configuration: ***********/

// changes to the DOM in this container will trigger an update.
LFM_WATCHED_CONTAINER = "div#portablecontrols";

// function that returns title of current song
function LFM_TRACK_TITLE() {
	var info = parseInfo($("div#portablecontrols h3").html());
    return info.track;
}

// function that returns artist of current song
function LFM_TRACK_ARTIST() {
	var info = parseInfo($("div#portablecontrols h3").html());
    return info.artist;
}

// function that returns duration of current song in seconds
// called at begining of song
function LFM_TRACK_DURATION() {
	durationArr = $("span.elapsed").html().split(" - ")[1].split(":");
	return parseInt(durationArr[0])*60 + parseInt(durationArr[1]);
}

/********* Connector: ***********/

var LFM_lastTrack = "";
var LFM_isWaiting = 0;

function LFM_updateNowPlaying(){
	// Acquire data from page
	title = LFM_TRACK_TITLE();
	artist = LFM_TRACK_ARTIST();
	duration = LFM_TRACK_DURATION();
	newTrack = title + " " + artist;
	// Update scrobbler if necessary
	if (newTrack != "" && newTrack != LFM_lastTrack){
		if (duration == 0) {
			// Nasty workaround for delayed duration visiblity with skipped tracks.
			setTimeout(LFM_updateNowPlaying, 5000);
			return 0;
		}

		LFM_lastTrack = newTrack;
		chrome.runtime.sendMessage({type: 'validate', artist: artist, track: title}, function(response) {
			if (response != false) {
				chrome.runtime.sendMessage({type: 'nowPlaying', artist: artist, track: title, duration: duration});
			} else { // on failure send nowPlaying 'unknown song'
				chrome.runtime.sendMessage({type: 'nowPlaying', duration: duration});
			}
		});
	}
	LFM_isWaiting = 0;
}

// The script is automatically injected on domready, no need for $()
$(LFM_WATCHED_CONTAINER).live('DOMSubtreeModified', function(e) {
    //console.log("Live watcher called");
    if ($(LFM_WATCHED_CONTAINER).length > 0) {
        if(LFM_isWaiting == 0){
            LFM_isWaiting = 1;
            setTimeout(LFM_updateNowPlaying, 10000);
        }
        return;
    }
});

$(window).unload(function() {
    chrome.runtime.sendMessage({type: 'reset'});
    return true;
});

/**
 * Listen for requests from scrobbler.js
 */
chrome.runtime.onMessage.addListener(
    function(request, sender, sendResponse) {
        switch(request.type) {

            // background calls this to see if the script is already injected
            case 'ping':
                sendResponse(true);
                break;
        }
    }
);
<commit_msg>Delete old Reddit Playlister connector<commit_after><|endoftext|>


<commit_before>"use strict";
var ourJoi = module.exports = { };

var Joi = require("joi");

ourJoi._joiBase = function(resourceName) {
  var relationType = Joi.object().keys({
    id: Joi.string().required(),
    type: Joi.any().required().valid(resourceName),
    meta: Joi.object().optional()
  });
  return relationType;
};
Joi.one = function(resource) {
  var obj = Joi.alternatives().try(
    Joi.any().valid(null), // null
    ourJoi._joiBase(resource)
  );
  obj._settings = {
    __one: resource
  };
  return obj;
};
Joi.many = function(resource) {
  var obj = Joi.array().items(ourJoi._joiBase(resource));
  obj._settings = {
    __many: resource
  };
  return obj;
};
Joi.belongsToOne = function(config) {
  var obj = Joi.alternatives().try(
    Joi.any().valid(null), // null
    ourJoi._joiBase(config.resource)
  );
  obj._settings = {
    __one: config.resource,
    __as: config.as
  };
  return obj;
};
Joi.belongsToMany = function(config) {
  var obj = Joi.array().items(ourJoi._joiBase(config.resource));
  obj._settings = {
    __many: config.resource,
    __as: config.as
  };
  return obj;
};
ourJoi.Joi = Joi;
<commit_msg>Error fast when creating invalid foreign relation<commit_after>"use strict";
var ourJoi = module.exports = { };

var Joi = require("joi");

ourJoi._joiBase = function(resourceName) {
  var relationType = Joi.object().keys({
    id: Joi.string().required(),
    type: Joi.any().required().valid(resourceName),
    meta: Joi.object().optional()
  });
  return relationType;
};
Joi.one = function(resource) {
  var obj = Joi.alternatives().try(
    Joi.any().valid(null), // null
    ourJoi._joiBase(resource)
  );
  obj._settings = {
    __one: resource
  };
  return obj;
};
Joi.many = function(resource) {
  var obj = Joi.array().items(ourJoi._joiBase(resource));
  obj._settings = {
    __many: resource
  };
  return obj;
};
Joi._validateForeignRelation = function(config) {
  if (!config.as) throw new Error("Missing 'as' property when defining a foreign relation");
  if (!config.resource) throw new Error("Missing 'resource' property when defining a foreign relation");
};
Joi.belongsToOne = function(config) {
  Joi._validateForeignRelation(config);
  var obj = Joi.alternatives().try(
    Joi.any().valid(null), // null
    ourJoi._joiBase(config.resource)
  );
  obj._settings = {
    __one: config.resource,
    __as: config.as
  };
  return obj;
};
Joi.belongsToMany = function(config) {
  Joi._validateForeignRelation(config);
  var obj = Joi.array().items(ourJoi._joiBase(config.resource));
  obj._settings = {
    __many: config.resource,
    __as: config.as
  };
  return obj;
};
ourJoi.Joi = Joi;
<|endoftext|>


<commit_before>if (typeof _AljsApp === 'undefined') { throw new Error("Please include ember.aljs-init.js in your compiled Ember Application"); }

_AljsApp.AljsLookupInputComponent = Ember.TextField.extend({
    attributeBindings: ['aria-expanded', 'aria-autocomplete', 'aria-activedescendant', 'role']
});

_AljsApp.AljsLookupComponent = Ember.Component.extend({
    layoutName: 'components/aljs-lookup',
    classNames: 'slds-lookup',
    classNameBindings: ['slds-has-selection'],
    attributeBindings: ['data-select', 'data-scope', 'data-typeahead', 'objectPluralLabel', 'objectLabel', 'items',
                        'emptySearchTermQuery', 'filledSearchTermQuery', 'initSelection', 'objectIconUrl', 'isObjectIconCustom', 'objectIconClass'],
    'slds-has-selection' : function() {
        return !Ember.isEmpty(this.get('selectedResult')) || !Ember.isEmpty(this.get('selectedResults'));
    }.property('selectedResult', 'selectedResults'),
    init: function() {
        this._super();

        var isSingle = this.get('data-select') === 'single';
        var initSelection = this.get('initSelection');

        if (initSelection) {
            this.set(isSingle ? 'selectedResult' : 'selectedResults', initSelection);
        } else {
            this.setProperties({
                selectedResult: null,
                selectedResults: []
            });
        }
    },
    didInsertElement: function() {

    },
    isExpanded: function() {
        return !Ember.isEmpty(this.get('searchResults')) ? 'true' : 'false';
    }.property('searchResults'),
    isSingle: function() {
        return this.get('data-select') === 'single';
    }.property('data-select'),
    focusIn: function(e) {
        if (e.target.nodeName.toLowerCase() === 'input') {
            var searchTerm = this.get('searchTerm');

            if (Ember.isEmpty(searchTerm)) {
                this.getDefaultResults();
            } else {
                this.getSearchTermResults(searchTerm);
            }
        }
    },
    focusOut: function(e) {
        var $relatedTarget = $(e.relatedTarget);

        if (Ember.isEmpty(this.$().find($relatedTarget))) {
            this.set('searchResults', null);
        }
    },
    keyUp: function(e) {
        var actionKeys = [9, 13, 16, 27, 40, 38];
        var $focusedA = this.$().find('a:focus');

        if (actionKeys.indexOf(e.keyCode) === -1) {
            var searchTerm = this.get('searchTerm');

            if (Ember.isEmpty(searchTerm)) {
                this.getDefaultResults();
            } else {
                this.getSearchTermResults(searchTerm);
            }
        }

        if (e.keyCode === 27) {
            this.set('searchResults', null);
            this.$().find('input').blur();
        }

        if (e.keyCode === 40) {
            // DOWN
            if ($focusedA.length > 0) {
                this.$().find('a:focus').parent().next().find('a').focus();
            } else {
                this.$().find('.slds-lookup__list').find('a:first').focus();
            }
        }

        if (e.keyCode === 38) {
            // UP
            if ($focusedA.length > 0) {
                this.$().find('a:focus').parent().prev().find('a').focus();
            } else {
                this.$().find('.slds-lookup__list').find('a:last').focus();
            }
        }

    },
    showUse: function() {
        var searchTerm = this.get('searchTerm');
        return !Ember.isEmpty(searchTerm) && searchTerm.length > 1;
    }.property('searchTerm'),
    showSearchResult: function(result) {
        // Check if the search result has been selected and don't show otherwise.

        return this.get('isSingle') || Ember.isEmpty(this.get('selectedResults').findBy('id', result.id));
    },
    getDefaultResults: function() {
        var self = this;

        var items = this.get('items');

        if (!Ember.isEmpty(items)) {
            this.set('searchResults', items.filter(function(item) {
                return self.showSearchResult(item);
            }));
        } else if (!Ember.isNone(this.get('emptySearchTermQuery'))) {
            var callback = function(searchResults) {
                self.set('searchResults', searchResults.filter(function(searchResult) {
                    return self.showSearchResult(searchResult);
                }));
            };

            this.get('emptySearchTermQuery')(callback);
        }
    },
    getSearchTermResults: function(searchTerm) {
        var self = this;
        var selectedResult = this.get('selectedResult');
        var selectedResults = this.get('selectedResults');
        var items = this.get('items');

        if (!Ember.isEmpty(items)) {
            this.set('searchResults', items.filter(function(item) {
                return item.label.match(searchTerm) !== null
                        && self.showSearchResult(item);
            }));
        } else if (!Ember.isNone(this.get('filledSearchTermQuery'))) {
            var callback = function(searchResults) {
                self.set('searchResults', searchResults.filter(function(result) {
                    return self.showSearchResult(result);
                }));
            };

            this.get('filledSearchTermQuery')(callback);
        }
    },
    searchResultsChanged: function() {
        if (!Ember.isEmpty(this.get('searchResults'))) {
            Ember.run.scheduleOnce('afterRender', this, function() {
                var self = this;
                console.log(this.$().find('li.slds-lookup__item'));
                this.$().find('a[role="option"]').on('focus', function(e) {
                    self.set('focusedSearchResult', $(e.target).attr('id'));
                });
            });
        }
    }.observes('searchResults'),
    actions: {
        clickResult: function(result) {
            if (this.get('isSingle')) {
                this.set('selectedResult', result);
            } else {
                this.get('selectedResults').addObject(result);
            }

            this.set('searchResults', null);
        },
        clickRemoveSelection: function(selectedResult) {
            if (this.get('isSingle')) {
                this.set('selectedResult', null);

                Ember.run.scheduleOnce('afterRender', this, function() {
                    this.$().find('input').focus();
                });
            } else {
                this.get('selectedResults').removeObject(selectedResult);
            }
        }
    }
});<commit_msg>Ember Lookups - allowing binding to the controller. Properly calling filled search term method.<commit_after>if (typeof _AljsApp === 'undefined') { throw new Error("Please include ember.aljs-init.js in your compiled Ember Application"); }

_AljsApp.AljsLookupInputComponent = Ember.TextField.extend({
    attributeBindings: ['aria-expanded', 'aria-autocomplete', 'aria-activedescendant', 'role']
});

_AljsApp.AljsLookupComponent = Ember.Component.extend({
    layoutName: 'components/aljs-lookup',
    classNames: 'slds-lookup',
    classNameBindings: ['slds-has-selection'],
    attributeBindings: ['data-select', 'data-scope', 'data-typeahead', 'objectPluralLabel', 'objectLabel', 'items', 'searchTerm', 'ctrl',
                        'emptySearchTermQuery', 'filledSearchTermQuery', 'initSelection', 'objectIconUrl', 'isObjectIconCustom', 'objectIconClass'],
    'slds-has-selection' : function() {
        return !Ember.isEmpty(this.get('selectedResult')) || !Ember.isEmpty(this.get('selectedResults'));
    }.property('selectedResult', 'selectedResults'),
    init: function() {
        this._super();

        var isSingle = this.get('data-select') === 'single';
        var initSelection = this.get('initSelection');

        if (initSelection) {
            this.set(isSingle ? 'selectedResult' : 'selectedResults', initSelection);
        } else {
            this.setProperties({
                selectedResult: null,
                selectedResults: []
            });
        }
    },
    didInsertElement: function() {

    },
    isExpanded: function() {
        return !Ember.isEmpty(this.get('searchResults')) ? 'true' : 'false';
    }.property('searchResults'),
    isSingle: function() {
        return this.get('data-select') === 'single';
    }.property('data-select'),
    focusIn: function(e) {
        if (e.target.nodeName.toLowerCase() === 'input') {
            var searchTerm = this.get('searchTerm');

            if (Ember.isEmpty(searchTerm)) {
                this.getDefaultResults();
            } else {
                this.getSearchTermResults(searchTerm);
            }
        }
    },
    focusOut: function(e) {
        var $relatedTarget = $(e.relatedTarget);

        if (Ember.isEmpty(this.$().find($relatedTarget))) {
            this.set('searchResults', null);
        }
    },
    keyUp: function(e) {
        var actionKeys = [9, 13, 16, 27, 40, 38];
        var $focusedA = this.$().find('a:focus');

        if (actionKeys.indexOf(e.keyCode) === -1) {
            var searchTerm = this.get('searchTerm');

            if (Ember.isEmpty(searchTerm)) {
                this.getDefaultResults();
            } else {
                this.getSearchTermResults(searchTerm);
            }
        }

        if (e.keyCode === 27) {
            this.set('searchResults', null);
            this.$().find('input').blur();
        }

        if (e.keyCode === 40) {
            // DOWN
            if ($focusedA.length > 0) {
                this.$().find('a:focus').parent().next().find('a').focus();
            } else {
                this.$().find('.slds-lookup__list').find('a:first').focus();
            }
        }

        if (e.keyCode === 38) {
            // UP
            if ($focusedA.length > 0) {
                this.$().find('a:focus').parent().prev().find('a').focus();
            } else {
                this.$().find('.slds-lookup__list').find('a:last').focus();
            }
        }

    },
    showUse: function() {
        var searchTerm = this.get('searchTerm');
        return !Ember.isEmpty(searchTerm) && searchTerm.length > 1;
    }.property('searchTerm'),
    showSearchResult: function(result) {
        // Check if the search result has been selected and don't show otherwise.

        return this.get('isSingle') || Ember.isEmpty(this.get('selectedResults').findBy('id', result.id));
    },
    getDefaultResults: function() {
        var self = this;

        var items = this.get('items');

        if (!Ember.isEmpty(items)) {
            this.set('searchResults', items.filter(function(item) {
                return self.showSearchResult(item);
            }));
        } else if (!Ember.isNone(this.get('emptySearchTermQuery'))) {
            var callback = function(searchResults) {
                self.set('searchResults', searchResults.filter(function(searchResult) {
                    return self.showSearchResult(searchResult);
                }));
            };

            this.get('emptySearchTermQuery').call(this, callback);
        }
    },
    getSearchTermResults: function(searchTerm) {
        var self = this;
        var selectedResult = this.get('selectedResult');
        var selectedResults = this.get('selectedResults');
        var items = this.get('items');

        if (!Ember.isEmpty(items)) {
            this.set('searchResults', items.filter(function(item) {
                return item.label.match(searchTerm) !== null
                        && self.showSearchResult(item);
            }));
        } else if (!Ember.isNone(this.get('filledSearchTermQuery'))) {
            var callback = function(searchResults) {
                self.set('searchResults', searchResults.filter(function(result) {
                    return self.showSearchResult(result);
                }));
            };

            this.get('filledSearchTermQuery').call(this, searchTerm, callback);
        }
    },
    searchResultsChanged: function() {
        if (!Ember.isEmpty(this.get('searchResults'))) {
            Ember.run.scheduleOnce('afterRender', this, function() {
                var self = this;
                console.log(this.$().find('li.slds-lookup__item'));
                this.$().find('a[role="option"]').on('focus', function(e) {
                    self.set('focusedSearchResult', $(e.target).attr('id'));
                });
            });
        }
    }.observes('searchResults'),
    actions: {
        clickResult: function(result) {
            if (this.get('isSingle')) {
                this.set('selectedResult', result);
            } else {
                this.get('selectedResults').addObject(result);
            }

            this.set('searchResults', null);
        },
        clickRemoveSelection: function(selectedResult) {
            if (this.get('isSingle')) {
                this.set('selectedResult', null);

                Ember.run.scheduleOnce('afterRender', this, function() {
                    this.$().find('input').focus();
                });
            } else {
                this.get('selectedResults').removeObject(selectedResult);
            }
        }
    }
});<|endoftext|>


<commit_before>export { testCorrectness };

import * as Pool from "../util/Pool.js";
import * as Recyclable from "../util/Recyclable.js";
import * as RandTools from "../util/RandTools.js";
import * as TensorTools from "../util/TensorTools.js";
import * as FloatValue from "../Unpacker/FloatValue.js";

/**
 * Test ScaleTranslate.
 */
class Base {

  constructor() {
    this.asserter_Equal = TensorTools.Asserter_Equal.Pool.get_or_create_by();
  }

  assert_PropertyProperty_Value( strThisPropertyName, strThisPropertyPropertyName, rhsValue ) {
    let thisValue = this[ strThisPropertyName ][ strThisPropertyPropertyName ];

//!!! (2021/12/31 Remarked)
//     if ( thisValue != rhsValue )
//       throw Error( `jsPerf_FloatValue_ScaleTranslate.testCorrectness(): `
//       + `this.${strThisPropertyName}.${strThisPropertyPropertyName} ( ${thisValue} ) should be ( ${rhsValue} ).` );

    this.asserter_Equal.assert_Number_Number( thisValue, rhsValue,
      `jsPerf_FloatValue_ScaleTranslate.testCorrectness(): this.`, // prefixMsg,
      `${strThisPropertyName}.${strThisPropertyPropertyName}`, // lhsNumberName,
      ``, // rhsNumberName,
      ``  // postfixMsg
    );
  }

  /**  */
  disposeResources() {
    this.asserter_Equal.disposeResources_and_recycleToPool();
    this.asserter_Equal = null;
  }

}

/**
 * Test ScaleTranslate.
 */
class Case extends Base {

  constructor() {
    super();

    { // Test ScaleTranslate.setBy_undoScaleTranslate().
      let scale = RandTools.getRandomIntInclusive( -10, +10 );
      if ( 0 == scale ) {
        scale = 0.05;  // Force to non-zero. (Note: undoScaleTranslate does not work for zero scale.)
      }

      let aScaleTranslate = new FloatValue.ScaleTranslate( scale, RandTools.getRandomIntInclusive( -10, +10 ) ); // Random scale-translate.

      let undoScaleTranslate = new FloatValue.ScaleTranslate();
      undoScaleTranslate.set_byUndo_ScaleTranslate( aScaleTranslate );

      if ( Number.isNaN( undoScaleTranslate.scale ) || Number.isNaN( undoScaleTranslate.translate ) ) {
        debugger;
      }

      let originalValue = RandTools.getRandomIntInclusive( -10, +10 );
      let changedValue = ( originalValue * aScaleTranslate.scale ) + aScaleTranslate.translate;
      this.undoTest = {
        undoChangedValue: ( changedValue * undoScaleTranslate.scale ) + undoScaleTranslate.translate
      };

      this.assert_PropertyProperty_Value( "undoTest", "undoChangedValue", originalValue );
    }

    { // Test ScaleArray.set_one_byUndo_N().
      let arrayLength = RandTools.getRandomIntInclusive( 1, 5 );

      let aScaleArray = new FloatValue.ScaleArray( arrayLength ); // Set up random scales.
      {
        for ( let i = 0; i < arrayLength; ++i ) {
          let scale = RandTools.getRandomIntInclusive( -10, +10 );
          if ( 0 == scale ) {
            scale = 0.05;  // Force to non-zero. (Note: undoScaleTranslate does not work for zero scale.)
          }

          aScaleArray.set_one_byN( i, scale );
        }
      }

      let undoScaleArray = new FloatValue.ScaleArray( arrayLength );

      { // Test .set_all_byUndo_ScaleArray() and .set_one_byUndo_N()

        // Test .set_all_byUndo_ScaleArray()
        undoScaleArray.set_all_byUndo_ScaleArray( aScaleArray );

        // Test .set_one_byUndo_N()
        let indexRand = RandTools.getRandomIntInclusive( 0, ( arrayLength - 1 ) );
        undoScaleArray.set_one_byUndo_N( indexRand, aScaleArray.scales[ indexRand ] );

        // Verify
        this.undoTest = {
          undoChangedValue: undefined
        };

        for ( let i = 0; i < arrayLength; ++i ) {

          if ( Number.isNaN( undoScaleArray.scales[ i ] ) ) {
            debugger;
          }

          let originalValue = RandTools.getRandomIntInclusive( -10, +10 );
          let changedValue = ( originalValue * aScaleArray.scales[ i ] );
          this.undoTest.undoChangedValue = ( changedValue * undoScaleArray.scales[ i ] );

          this.assert_PropertyProperty_Value( "undoTest", "undoChangedValue", originalValue );
        }
      }
    }

    this.disposeResources();
  }

}

/**
 * Test ScaleTranslateArray.
 */
class Cases extends Base {

  constructor() {
    super();

    this.disposeResources();
  }

}

function testCorrectness() {
  let casesArray = [
    new Cases( [
      new Case(),
    ] ),
  ];

//!!! ...unfinished... (2021/12/31)

//   let casesArray = [
//     new Cases( [
//       new Case( [  1,  2 ], [  3,  4 ],  5, [  4,  6 ], [  3,  8 ], [   5,  10 ] ),
//       new Case( [ -1,  2 ], [  3,  4 ],  5, [  2,  6 ], [ -4,  8 ], [  -5,  10 ] ),
//       new Case( [  1, -2 ], [  3,  4 ],  5, [  1,  5 ], [ -8,  4 ], [ -10,   5 ] ),
//       new Case( [ -1, -2 ], [  3,  4 ],  5, [  1,  3 ], [ -8, -3 ], [ -10,  -5 ] ),
//     ] ),

//     new Cases( [
//       new Case( [  1,  2 ], [ -3,  4 ], -5, [ -2,  6 ], [ -6,  8 ], [ -10,  -5 ] ),
//       new Case( [ -1,  2 ], [ -3,  4 ], -5, [ -4,  6 ], [ -6,  8 ], [ -10,   5 ] ),
//       new Case( [  1, -2 ], [ -3,  4 ], -5, [ -5,  5 ], [ -8,  6 ], [  -5,  10 ] ),
//       new Case( [ -1, -2 ], [ -3,  4 ], -5, [ -5,  3 ], [ -8,  6 ], [   5,  10 ] ),
//     ] ),

//     new Cases( [
//       new Case( [  1,  2 ], [  3, -4 ],  5, [ -3,  5 ], [ -8,  6 ], [   5,  10 ] ),
//       new Case( [ -1,  2 ], [  3, -4 ],  5, [ -5,  5 ], [ -8,  6 ], [  -5,  10 ] ),
//       new Case( [  1, -2 ], [  3, -4 ],  5, [ -6,  4 ], [ -6,  8 ], [ -10,   5 ] ),
//       new Case( [ -1, -2 ], [  3, -4 ],  5, [ -6,  2 ], [ -6,  8 ], [ -10,  -5 ] ),
//     ] ),

//     new Cases( [
//       new Case( [  1,  2 ], [ -3, -4 ], -5, [ -3, -1 ], [ -8, -3 ], [ -10,  -5 ] ),
//       new Case( [ -1,  2 ], [ -3, -4 ], -5, [ -5, -1 ], [ -8,  4 ], [ -10,   5 ] ),
//       new Case( [  1, -2 ], [ -3, -4 ], -5, [ -6, -2 ], [ -4,  8 ], [  -5,  10 ] ),
//       new Case( [ -1, -2 ], [ -3, -4 ], -5, [ -6, -4 ], [  3,  8 ], [   5,  10 ] ),
//     ] ),
//   ];

}
<commit_msg>Update jsPerf_FloatValue_ScaleTranslate.js<commit_after>export { testCorrectness };

import * as Pool from "../util/Pool.js";
import * as Recyclable from "../util/Recyclable.js";
import * as RandTools from "../util/RandTools.js";
import * as TensorTools from "../util/TensorTools.js";
import * as FloatValue from "../Unpacker/FloatValue.js";

/**
 * Test ScaleTranslate.
 */
class Base {

  constructor() {
    this.asserter_Equal = TensorTools.Asserter_Equal.Pool.get_or_create_by();
  }

  assert_PropertyProperty_Value( strThisPropertyName, strThisPropertyPropertyName, rhsValue ) {
    let thisValue = this[ strThisPropertyName ][ strThisPropertyPropertyName ];

//!!! (2021/12/31 Remarked)
//     if ( thisValue != rhsValue )
//       throw Error( `jsPerf_FloatValue_ScaleTranslate.testCorrectness(): `
//       + `this.${strThisPropertyName}.${strThisPropertyPropertyName} ( ${thisValue} ) should be ( ${rhsValue} ).` );

    this.asserter_Equal.assert_Number_Number( thisValue, rhsValue,
      `jsPerf_FloatValue_ScaleTranslate.testCorrectness(): this.`, // prefixMsg,
      `${strThisPropertyName}.${strThisPropertyPropertyName}`, // lhsNumberName,
      ``, // rhsNumberName,
      ``  // postfixMsg
    );
  }

  /**  */
  disposeResources() {
    this.asserter_Equal.disposeResources_and_recycleToPool();
    this.asserter_Equal = null;
  }

}

/**
 * Test ScaleTranslate.
 */
class Case extends Base {

  constructor() {
    super();

    { // Test ScaleTranslate.setBy_undoScaleTranslate().
      let scale = RandTools.getRandomIntInclusive( -10, +10 );
      if ( 0 == scale ) {
        scale = 0.05;  // Force to non-zero. (Note: undoScaleTranslate does not work for zero scale.)
      }

      let aScaleTranslate = new FloatValue.ScaleTranslate( scale, RandTools.getRandomIntInclusive( -10, +10 ) ); // Random scale-translate.

      let undoScaleTranslate = new FloatValue.ScaleTranslate();
      undoScaleTranslate.set_byUndo_ScaleTranslate( aScaleTranslate );

      if ( Number.isNaN( undoScaleTranslate.scale ) || Number.isNaN( undoScaleTranslate.translate ) ) {
        debugger;
      }

      let originalValue = RandTools.getRandomIntInclusive( -10, +10 );
      let changedValue = ( originalValue * aScaleTranslate.scale ) + aScaleTranslate.translate;
      this.undoTest = {
        undoChangedValue: ( changedValue * undoScaleTranslate.scale ) + undoScaleTranslate.translate
      };

      this.assert_PropertyProperty_Value( "undoTest", "undoChangedValue", originalValue );
    }

    { // Test ScaleArray.set_one_byUndo_N().
      let arrayLength = RandTools.getRandomIntInclusive( 1, 5 );

      let aScaleArray = FloatValue.ScaleArray.Pool.get_or_create_by( arrayLength ); // Set up random scales.
      {
        for ( let i = 0; i < arrayLength; ++i ) {
          let scale = RandTools.getRandomIntInclusive( -10, +10 );
          if ( 0 == scale ) {
            scale = 0.05;  // Force to non-zero. (Note: undoScaleTranslate does not work for zero scale.)
          }

          aScaleArray.set_one_byN( i, scale );
        }
      }

      let undoScaleArray = FloatValue.ScaleArray.Pool.get_or_create_by( arrayLength );

      { // Test .set_all_byUndo_ScaleArray() and .set_one_byUndo_N()

        // Test .set_all_byUndo_ScaleArray()
        undoScaleArray.set_all_byUndo_ScaleArray( aScaleArray );

        // Test .set_one_byUndo_N()
        let indexRand = RandTools.getRandomIntInclusive( 0, ( arrayLength - 1 ) );
        undoScaleArray.set_one_byUndo_N( indexRand, aScaleArray.scales[ indexRand ] );

        // Verify
        this.undoTest = {
          undoChangedValue: undefined
        };

        for ( let i = 0; i < arrayLength; ++i ) {

          if ( Number.isNaN( undoScaleArray.scales[ i ] ) ) {
            debugger;
          }

          let originalValue = RandTools.getRandomIntInclusive( -10, +10 );
          let changedValue = ( originalValue * aScaleArray.scales[ i ] );
          this.undoTest.undoChangedValue = ( changedValue * undoScaleArray.scales[ i ] );

          this.assert_PropertyProperty_Value( "undoTest", "undoChangedValue", originalValue );
        }
      }

      undoScaleArray.disposeResources_and_recycleToPool();
      undoScaleArray = null;

      aScaleArray.disposeResources_and_recycleToPool();
      aScaleArray = null;
    }

    this.disposeResources();
  }

  /** @override */
  disposeResources() {
//     ??this.asserter_Equal.disposeResources_and_recycleToPool();
//     this.asserter_Equal = null;

    super.disposeResources();
  }

}

/**
 * Test ScaleTranslateArray.
 */
class Cases extends Base {

  constructor() {
    super();

    this.disposeResources();
  }

}

function testCorrectness() {
  let casesArray = [
    new Cases( [
      new Case(),
    ] ),
  ];

//!!! ...unfinished... (2021/12/31)

//   let casesArray = [
//     new Cases( [
//       new Case( [  1,  2 ], [  3,  4 ],  5, [  4,  6 ], [  3,  8 ], [   5,  10 ] ),
//       new Case( [ -1,  2 ], [  3,  4 ],  5, [  2,  6 ], [ -4,  8 ], [  -5,  10 ] ),
//       new Case( [  1, -2 ], [  3,  4 ],  5, [  1,  5 ], [ -8,  4 ], [ -10,   5 ] ),
//       new Case( [ -1, -2 ], [  3,  4 ],  5, [  1,  3 ], [ -8, -3 ], [ -10,  -5 ] ),
//     ] ),

//     new Cases( [
//       new Case( [  1,  2 ], [ -3,  4 ], -5, [ -2,  6 ], [ -6,  8 ], [ -10,  -5 ] ),
//       new Case( [ -1,  2 ], [ -3,  4 ], -5, [ -4,  6 ], [ -6,  8 ], [ -10,   5 ] ),
//       new Case( [  1, -2 ], [ -3,  4 ], -5, [ -5,  5 ], [ -8,  6 ], [  -5,  10 ] ),
//       new Case( [ -1, -2 ], [ -3,  4 ], -5, [ -5,  3 ], [ -8,  6 ], [   5,  10 ] ),
//     ] ),

//     new Cases( [
//       new Case( [  1,  2 ], [  3, -4 ],  5, [ -3,  5 ], [ -8,  6 ], [   5,  10 ] ),
//       new Case( [ -1,  2 ], [  3, -4 ],  5, [ -5,  5 ], [ -8,  6 ], [  -5,  10 ] ),
//       new Case( [  1, -2 ], [  3, -4 ],  5, [ -6,  4 ], [ -6,  8 ], [ -10,   5 ] ),
//       new Case( [ -1, -2 ], [  3, -4 ],  5, [ -6,  2 ], [ -6,  8 ], [ -10,  -5 ] ),
//     ] ),

//     new Cases( [
//       new Case( [  1,  2 ], [ -3, -4 ], -5, [ -3, -1 ], [ -8, -3 ], [ -10,  -5 ] ),
//       new Case( [ -1,  2 ], [ -3, -4 ], -5, [ -5, -1 ], [ -8,  4 ], [ -10,   5 ] ),
//       new Case( [  1, -2 ], [ -3, -4 ], -5, [ -6, -2 ], [ -4,  8 ], [  -5,  10 ] ),
//       new Case( [ -1, -2 ], [ -3, -4 ], -5, [ -6, -4 ], [  3,  8 ], [   5,  10 ] ),
//     ] ),
//   ];

}
<|endoftext|>


<commit_before>!function($, wysi) {
	"use strict"

	var templates = {
		"font-styles": "
" +
							"" +
								" Normal text " +
							"" +
						    "
" +
						      	"
Normal text
" +
					            "
Heading 1
" +
					            "
Heading 2
" +
						    "
" +
						"
",
		"emphasis":     "
" +
							"
"
							    + "Bold"
							    + "Italic"
							    + "Underline"
							+ "
"
						+ "
",
		"lists": 	"
"
						+ "
"
					    	+ ""
						    + ""
						    + ""
						    + ""
						+ "
"
					+ "
",

		"link": 	"
"

						+ "
"
							+ "
"
							+ "×"
							  + "
Insert Link
"
							+ "
"
							+ "
"
							  + "<input value='http://' class='bootstrap-wysihtml5-insert-link-url input-xlarge'>"
							+ "
"
							+ "
"
							  + "Cancel"
							  + "Insert link"
							+ "
"
						+ "
"

				    	+ ""

					+ "
",

			"image": "
"

						+ "
"
							+ "
"
							+ "×"
							  + "
Insert Image
"
							+ "
"
							+ "
"
							  + "<input value='http://' class='bootstrap-wysihtml5-insert-image-url input-xlarge'>"
							+ "
"
							+ "
"
							  + "Cancel"
							  + "Insert image"
							+ "
"
						+ "
"

						+ ""

					+ "
",

		"html":
						"
"
							+ "
"
								+ ""
							+ "
"
						+ "
"
	};

	var defaultOptions = {
		"font-styles": true,
		"emphasis": true,
		"lists": true,
		"html": false,
		"link": true,
		"image": true,
		events: {},
		parserRules: {
			tags: {
				"b":  {},
				"i":  {},
				"br": {},
				"ol": {},
				"ul": {},
				"li": {},
				"h1": {},
				"h2": {},
				"u": 1,
				"img": {
					"check_attributes": {
			            "width": "numbers",
			            "alt": "alt",
			            "src": "url",
			            "height": "numbers"
			        }
				},
				"a":  {
					set_attributes: {
						target: "_blank",
						rel:    "nofollow"
					},
					check_attributes: {
						href:   "url" // important to avoid XSS
					}
				}
			}
		}
	};

	var Wysihtml5 = function(el, options) {
		this.el = el;
		this.toolbar = this.createToolbar(el, options || defaultOptions);
		this.editor =  this.createEditor(options);

		window.editor = this.editor;

  		$('iframe.wysihtml5-sandbox').each(function(i, el){
			$(el.contentWindow).off('focus.wysihtml5').on({
			  'focus.wysihtml5' : function(){
			     $('li.dropdown').removeClass('open');
			   }
			});
		});
	};

	Wysihtml5.prototype = {
		constructor: Wysihtml5,

		createEditor: function(options) {
			var parserRules = defaultOptions.parserRules;

			if(options && options.parserRules) {
				parserRules = options.parserRules;
			}

			var editor = new wysi.Editor(this.el.attr('id'), {
	    		toolbar: this.toolbar.attr('id'),
				parserRules: parserRules
	  		});

	  		if(options && options.events) {
				for(var eventName in options.events) {
					editor.on(eventName, options.events[eventName]);
				}
			}

	  		return editor;
		},

		createToolbar: function(el, options) {
			var self = this;
			var toolbar = $("
", {
				'id' : el.attr('id') + "-wysihtml5-toolbar",
				'class' : "wysihtml5-toolbar",
				'style': "display:none"
			});

			for(var key in defaultOptions) {
				var value = false;

				if(options[key] != undefined) {
					if(options[key] == true) {
						value = true;
					}
				} else {
					value = defaultOptions[key];
				}

				if(value == true) {
					toolbar.append(templates[key]);

					if(key == "html") {
						this.initHtml(toolbar);
					}

					if(key == "link") {
						this.initInsertLink(toolbar);
					}

					if(key == "image") {
						this.initInsertImage(toolbar);
					}
				}
			}

			var self = this;

			toolbar.find("a[data-wysihtml5-command='formatBlock']").click(function(e) {
				var el = $(e.srcElement);
				self.toolbar.find('.current-font').text(el.html())
			});

			this.el.before(toolbar);

			return toolbar;
		},

		initHtml: function(toolbar) {
			var changeViewSelector = "a[data-wysihtml5-action='change_view']";
			toolbar.find(changeViewSelector).click(function(e) {
				toolbar.find('a.btn').not(changeViewSelector).toggleClass('disabled');
			});
		},

		initInsertImage: function(toolbar) {
			var self = this;
			var insertImageModal = toolbar.find('.bootstrap-wysihtml5-insert-image-modal');
			var urlInput = insertImageModal.find('.bootstrap-wysihtml5-insert-image-url');
			var insertButton = insertImageModal.find('a.btn-primary');
			var initialValue = urlInput.val();

			var insertImage = function() {
				var url = urlInput.val();
				urlInput.val(initialValue);
				self.editor.composer.commands.exec("insertImage", url);
			};

			urlInput.keypress(function(e) {
				if(e.which == 13) {
					insertImage();
					insertImageModal.modal('hide');
				}
			});

			insertButton.click(insertImage);

			insertImageModal.on('shown', function() {
				urlInput.focus();
			});

			insertImageModal.on('hide', function() {
				self.editor.currentView.element.focus();
			});

			toolbar.find('a[data-wysihtml5-command=insertImage]').click(function() {
				insertImageModal.modal('show');
				return false;
			});
		},

		initInsertLink: function(toolbar) {
			var self = this;
			var insertLinkModal = toolbar.find('.bootstrap-wysihtml5-insert-link-modal');
			var urlInput = insertLinkModal.find('.bootstrap-wysihtml5-insert-link-url');
			var insertButton = insertLinkModal.find('a.btn-primary');
			var initialValue = urlInput.val();

			var insertLink = function() {
				var url = urlInput.val();
				urlInput.val(initialValue);
				self.editor.composer.commands.exec("createLink", {
					href: url,
					target: "_blank",
					rel: "nofollow"
				});
			};
			var pressedEnter = false;

			urlInput.keypress(function(e) {
				if(e.which == 13) {
					insertLink();
					insertLinkModal.modal('hide');
				}
			});

			insertButton.click(insertLink);

			insertLinkModal.on('shown', function() {
				urlInput.focus();
			});

			insertLinkModal.on('hide', function() {
				self.editor.currentView.element.focus();
			});

			toolbar.find('a[data-wysihtml5-command=createLink]').click(function() {
				insertLinkModal.modal('show');
				return false;
			});
		}
	};

	$.fn.wysihtml5 = function (options) {
		return this.each(function () {
			var $this = $(this);
	      	$this.data('wysihtml5', new Wysihtml5($this, options));
	    })
  	};

  	$.fn.wysihtml5.Constructor = Wysihtml5;

}(window.jQuery, window.wysihtml5);
<commit_msg>Use × instead of the character itself<commit_after>!function($, wysi) {
	"use strict"

	var templates = {
		"font-styles": "
" +
							"" +
								" Normal text " +
							"" +
						    "
" +
						      	"
Normal text
" +
					            "
Heading 1
" +
					            "
Heading 2
" +
						    "
" +
						"
",
		"emphasis":     "
" +
							"
"
							    + "Bold"
							    + "Italic"
							    + "Underline"
							+ "
"
						+ "
",
		"lists": 	"
"
						+ "
"
					    	+ ""
						    + ""
						    + ""
						    + ""
						+ "
"
					+ "
",

		"link": 	"
"

						+ "
"
							+ "
"
							+ "×"
							  + "
Insert Link
"
							+ "
"
							+ "
"
							  + "<input value='http://' class='bootstrap-wysihtml5-insert-link-url input-xlarge'>"
							+ "
"
							+ "
"
							  + "Cancel"
							  + "Insert link"
							+ "
"
						+ "
"

				    	+ ""

					+ "
",

			"image": "
"

						+ "
"
							+ "
"
							+ "×"
							  + "
Insert Image
"
							+ "
"
							+ "
"
							  + "<input value='http://' class='bootstrap-wysihtml5-insert-image-url input-xlarge'>"
							+ "
"
							+ "
"
							  + "Cancel"
							  + "Insert image"
							+ "
"
						+ "
"

						+ ""

					+ "
",

		"html":
						"
"
							+ "
"
								+ ""
							+ "
"
						+ "
"
	};

	var defaultOptions = {
		"font-styles": true,
		"emphasis": true,
		"lists": true,
		"html": false,
		"link": true,
		"image": true,
		events: {},
		parserRules: {
			tags: {
				"b":  {},
				"i":  {},
				"br": {},
				"ol": {},
				"ul": {},
				"li": {},
				"h1": {},
				"h2": {},
				"u": 1,
				"img": {
					"check_attributes": {
			            "width": "numbers",
			            "alt": "alt",
			            "src": "url",
			            "height": "numbers"
			        }
				},
				"a":  {
					set_attributes: {
						target: "_blank",
						rel:    "nofollow"
					},
					check_attributes: {
						href:   "url" // important to avoid XSS
					}
				}
			}
		}
	};

	var Wysihtml5 = function(el, options) {
		this.el = el;
		this.toolbar = this.createToolbar(el, options || defaultOptions);
		this.editor =  this.createEditor(options);

		window.editor = this.editor;

  		$('iframe.wysihtml5-sandbox').each(function(i, el){
			$(el.contentWindow).off('focus.wysihtml5').on({
			  'focus.wysihtml5' : function(){
			     $('li.dropdown').removeClass('open');
			   }
			});
		});
	};

	Wysihtml5.prototype = {
		constructor: Wysihtml5,

		createEditor: function(options) {
			var parserRules = defaultOptions.parserRules;

			if(options && options.parserRules) {
				parserRules = options.parserRules;
			}

			var editor = new wysi.Editor(this.el.attr('id'), {
	    		toolbar: this.toolbar.attr('id'),
				parserRules: parserRules
	  		});

	  		if(options && options.events) {
				for(var eventName in options.events) {
					editor.on(eventName, options.events[eventName]);
				}
			}

	  		return editor;
		},

		createToolbar: function(el, options) {
			var self = this;
			var toolbar = $("
", {
				'id' : el.attr('id') + "-wysihtml5-toolbar",
				'class' : "wysihtml5-toolbar",
				'style': "display:none"
			});

			for(var key in defaultOptions) {
				var value = false;

				if(options[key] != undefined) {
					if(options[key] == true) {
						value = true;
					}
				} else {
					value = defaultOptions[key];
				}

				if(value == true) {
					toolbar.append(templates[key]);

					if(key == "html") {
						this.initHtml(toolbar);
					}

					if(key == "link") {
						this.initInsertLink(toolbar);
					}

					if(key == "image") {
						this.initInsertImage(toolbar);
					}
				}
			}

			var self = this;

			toolbar.find("a[data-wysihtml5-command='formatBlock']").click(function(e) {
				var el = $(e.srcElement);
				self.toolbar.find('.current-font').text(el.html())
			});

			this.el.before(toolbar);

			return toolbar;
		},

		initHtml: function(toolbar) {
			var changeViewSelector = "a[data-wysihtml5-action='change_view']";
			toolbar.find(changeViewSelector).click(function(e) {
				toolbar.find('a.btn').not(changeViewSelector).toggleClass('disabled');
			});
		},

		initInsertImage: function(toolbar) {
			var self = this;
			var insertImageModal = toolbar.find('.bootstrap-wysihtml5-insert-image-modal');
			var urlInput = insertImageModal.find('.bootstrap-wysihtml5-insert-image-url');
			var insertButton = insertImageModal.find('a.btn-primary');
			var initialValue = urlInput.val();

			var insertImage = function() {
				var url = urlInput.val();
				urlInput.val(initialValue);
				self.editor.composer.commands.exec("insertImage", url);
			};

			urlInput.keypress(function(e) {
				if(e.which == 13) {
					insertImage();
					insertImageModal.modal('hide');
				}
			});

			insertButton.click(insertImage);

			insertImageModal.on('shown', function() {
				urlInput.focus();
			});

			insertImageModal.on('hide', function() {
				self.editor.currentView.element.focus();
			});

			toolbar.find('a[data-wysihtml5-command=insertImage]').click(function() {
				insertImageModal.modal('show');
				return false;
			});
		},

		initInsertLink: function(toolbar) {
			var self = this;
			var insertLinkModal = toolbar.find('.bootstrap-wysihtml5-insert-link-modal');
			var urlInput = insertLinkModal.find('.bootstrap-wysihtml5-insert-link-url');
			var insertButton = insertLinkModal.find('a.btn-primary');
			var initialValue = urlInput.val();

			var insertLink = function() {
				var url = urlInput.val();
				urlInput.val(initialValue);
				self.editor.composer.commands.exec("createLink", {
					href: url,
					target: "_blank",
					rel: "nofollow"
				});
			};
			var pressedEnter = false;

			urlInput.keypress(function(e) {
				if(e.which == 13) {
					insertLink();
					insertLinkModal.modal('hide');
				}
			});

			insertButton.click(insertLink);

			insertLinkModal.on('shown', function() {
				urlInput.focus();
			});

			insertLinkModal.on('hide', function() {
				self.editor.currentView.element.focus();
			});

			toolbar.find('a[data-wysihtml5-command=createLink]').click(function() {
				insertLinkModal.modal('show');
				return false;
			});
		}
	};

	$.fn.wysihtml5 = function (options) {
		return this.each(function () {
			var $this = $(this);
	      	$this.data('wysihtml5', new Wysihtml5($this, options));
	    })
  	};

  	$.fn.wysihtml5.Constructor = Wysihtml5;

}(window.jQuery, window.wysihtml5);
<|endoftext|>


<commit_before>import * as utility from './utility.js'

/**
 * Convert HTML table to data table with desired configurations.
 * @param {string} table: A HTML formatted table.
 * @returns {void}.
 */
function convertToDataTable (table) {
  const hideColumns =
    $('#vizMethod').val() === '3DScatter' ? [2, 3, 4] : [2, 3]

  table.DataTable({
    // Do not paging.
    paging: false,
    // Set the Scroll Height.
    scrollY: 300,
    // If not enough height, shrink the table height.
    scrollCollapse: true,
    // Specify where the button is.
    dom: `<'row'<'col-sm-12 text-right'f>>
          <'row'<'col-sm-12 'tr>>
          <'row k-means-result-download-button'<'col-sm-12'B>>`,
    // Specify all the download buttons that are displayed on the page.
    buttons: ['copyHtml5', 'excelHtml5', 'csvHtml5', 'pdfHtml5'],

    columnDefs: [
      { // Truncate long documentation names.
        targets: 1,
        render: $.fn.dataTable.render.ellipsis(15, true)
      },
      { // Hide columns which hold the coordinates.
        targets: hideColumns,
        visible: false,
        searchable: false
      }
    ]
  })
}

/**
 * Display the K-Means clustering result as a table on web though an Ajax call.
 * @returns {void}.
 */
function generateKMeansResult () {
  $('#status-analyze').css({'visibility': 'visible'})
  // Convert form into an object map string to string
  const form = utility.jsonifyForm()

  // Send the ajax request
  utility.sendAjaxRequest('/KMeansResult', form)
    .done(
      function (response) {
        // Insert the table result.
        const tableDiv = $('#KMeans-table')
        tableDiv.html(response['table'])
        // Change the HTML table to a data table.
        convertToDataTable(tableDiv.children())
        // Insert the plot result.
        $('#KMeans-plot').html(response['plot'])
      })
    .fail(
      function (jqXHR, textStatus, errorThrown) {
        // If fail hide the loading icon.
        $('#status-analyze').css({'visibility': 'hidden'})
        console.log(`textStatus: ${textStatus}`)
        console.log(`errorThrown: ${errorThrown}`)
        utility.runModal('Error encountered while generating the K-Means result.')
      })
    .always(
      function () {
        // Always hide the loading icon.
        $('#status-analyze').css({'visibility': 'hidden'})
      })
}

function toKMeanGraph () {
  $('html, body').animate({
    scrollTop: $('#get-k-means-result').offset().top
  }, 1000)
}

$(function () {
  // Hide the K-Means result div when first get to the page.
  $('#KMeans-result').css({'display': 'none'})

  // The event handler when generate K-Means result is clicked.
  $('#get-k-means-result').click(function () {
    // Catch the possible error during submission.
    const error = utility.submissionError(2)

    if (error === null) {
      // If there is no error, get the result.
      $('#KMeans-result').css({'display': 'block'})
      generateKMeansResult()
      toKMeanGraph()
    } else {
      utility.runModal(error)
    }
  })
})
<commit_msg>jsdoc.<commit_after>import * as utility from './utility.js'

/**
 * Convert HTML table to data table with desired configurations.
 * @param {string} table: A HTML formatted table.
 * @returns {void}.
 */
function convertToDataTable (table) {
  const hideColumns =
    $('#vizMethod').val() === '3DScatter' ? [2, 3, 4] : [2, 3]

  table.DataTable({
    // Do not paging.
    paging: false,
    // Set the Scroll Height.
    scrollY: 300,
    // If not enough height, shrink the table height.
    scrollCollapse: true,
    // Specify where the button is.
    dom: `<'row'<'col-sm-12 text-right'f>>
          <'row'<'col-sm-12 'tr>>
          <'row k-means-result-download-button'<'col-sm-12'B>>`,
    // Specify all the download buttons that are displayed on the page.
    buttons: ['copyHtml5', 'excelHtml5', 'csvHtml5', 'pdfHtml5'],

    columnDefs: [
      { // Truncate long documentation names.
        targets: 1,
        render: $.fn.dataTable.render.ellipsis(15, true)
      },
      { // Hide columns which hold the coordinates.
        targets: hideColumns,
        visible: false,
        searchable: false
      }
    ]
  })
}

/**
 * Display the K-Means clustering result as a table on web though an Ajax call.
 * @returns {void}.
 */
function generateKMeansResult () {
  $('#status-analyze').css({'visibility': 'visible'})
  // Convert form into an object map string to string
  const form = utility.jsonifyForm()

  // Send the ajax request
  utility.sendAjaxRequest('/KMeansResult', form)
    .done(
      function (response) {
        // Insert the table result.
        const tableDiv = $('#KMeans-table')
        tableDiv.html(response['table'])
        // Change the HTML table to a data table.
        convertToDataTable(tableDiv.children())
        // Insert the plot result.
        $('#KMeans-plot').html(response['plot'])
      })
    .fail(
      function (jqXHR, textStatus, errorThrown) {
        // If fail hide the loading icon.
        $('#status-analyze').css({'visibility': 'hidden'})
        console.log(`textStatus: ${textStatus}`)
        console.log(`errorThrown: ${errorThrown}`)
        utility.runModal('Error encountered while generating the K-Means result.')
      })
    .always(
      function () {
        // Always hide the loading icon.
        $('#status-analyze').css({'visibility': 'hidden'})
      })
}

/**
 * Scrolls to the graph once its generated.
 * @returns {void}
 */
function toKMeanGraph () {
  $('html, body').animate({
    scrollTop: $('#get-k-means-result').offset().top
  }, 1000)
}

$(function () {
  // Hide the K-Means result div when first get to the page.
  $('#KMeans-result').css({'display': 'none'})

  // The event handler when generate K-Means result is clicked.
  $('#get-k-means-result').click(function () {
    // Catch the possible error during submission.
    const error = utility.submissionError(2)

    if (error === null) {
      // If there is no error, get the result.
      $('#KMeans-result').css({'display': 'block'})
      generateKMeansResult()
      toKMeanGraph()
    } else {
      utility.runModal(error)
    }
  })
})
<|endoftext|>


<commit_before>define(function() { return Backbone.Model.extend({

    socket: null,

    token: null,

    defaults: {
        url: 'memory.basti.dev',
        port: '1337'
    },

    initialize: function() {
        var connect = this.get('url') + ':' + this.get('port');

        this.socket = io.connect(connect);
    },

    emit: function(ev, data) {
        data['token'] = this.token;
        this.socket.emit(ev, data);
    },

    on: function(ev, cb)  {
        this.socket.on(ev, cb);
    },

    setToken: function(token) {
        this.token = token;
    }

});
});
<commit_msg>changed socket target host for the workshop to work<commit_after>define(function() { return Backbone.Model.extend({

    socket: null,

    token: null,

    defaults: {
        url: 'workshop.local',
        port: '1337'
    },

    initialize: function() {
        var connect = this.get('url') + ':' + this.get('port');

        this.socket = io.connect(connect);
    },

    emit: function(ev, data) {
        data['token'] = this.token;
        this.socket.emit(ev, data);
    },

    on: function(ev, cb)  {
        this.socket.on(ev, cb);
    },

    setToken: function(token) {
        this.token = token;
    }

});
});
<|endoftext|>


<commit_before>'use strict';

const crypto = require('crypto');
const debug = require('debug')('miio.packet');

class Packet {
	constructor() {
		this.header = Buffer.alloc(2 + 2 + 4 + 4 + 4 + 16);
		this.header[0] = 0x21;
		this.header[1] = 0x31;

		for(let i=4; i<32; i++) {
			this.header[i] = 0xff;
		}

		this._serverHandshake = 0;
	}

	handshake() {
		this.data = null;
	}

	handleHandshakeReply() {
		if(typeof this._token === 'undefined') {
			const token = this.checksum;
			if(token.toString('hex').match(/^[fF0]+$/)) {
				// Device did not return its token so we set our token to null
				this._token = null;
			} else {
				this.token = this.checksum;
			}
		}
		this._serverHandshake = Date.now();
	}

	get needsHandshake() {
		// Hopefully temporary, handshake before every command
		return ! this._token || (Date.now() - this._serverHandshake) > 60000;
	}

	get raw() {
		if(this.data) {
			// Send a command to the device
			if(! this._token) {
				throw new Error('Token is required to send commands');
			}

			for(let i=4; i<8; i++) {
				this.header[i] = 0x00;
			}

			// Update the stamp to match server
			if(this._serverStampTime) {
				const secondsPassed = Math.floor(Date.now() - this._serverStampTime) / 1000;
				this.header.writeUInt32BE(this._serverStamp + secondsPassed, 12)
			}

			// Encrypt the data
			let cipher = crypto.createCipheriv('aes-128-cbc', this._tokenKey, this._tokenIV);
			let encrypted = Buffer.concat([
				cipher.update(this.data),
				cipher.final()
			]);

			// Set the length
			this.header.writeUInt16BE(32 + encrypted.length, 2);

			// Calculate the checksum
			let digest = crypto.createHash('md5')
				.update(this.header.slice(0, 16))
				.update(this._token)
				.update(encrypted)
				.digest();
			digest.copy(this.header, 16);

			debug('->', this.header);
			return Buffer.concat([ this.header, encrypted ]);
		} else {
			// Handshake
			this.header.writeUInt16BE(32, 2);

			for(let i=4; i<32; i++) {
				this.header[i] = 0xff;
			}

			debug('->', this.header);
			return this.header;
		}
	}

	set raw(msg) {
		msg.copy(this.header, 0, 0, 32);
		debug('<-', this.header);

		const stamp = this.stamp;
		if(stamp > 0) {
			// If the device return a stamp, store it
			this._serverStamp = this.stamp;
		}

		const encrypted = msg.slice(32);

		if(encrypted.length > 0) {
			if(! this._token) {
				debug('<- No token set, unable to handle packet');
				this.data = null;
				return;
			}

			const digest = crypto.createHash('md5')
				.update(this.header.slice(0, 16))
				.update(this._token)
				.update(encrypted)
				.digest();

			const checksum = this.checksum;
			if(! checksum.equals(digest)) {
				debug('<- Invalid packet, checksum was', checksum, 'should be', digest);
				this.data = null;
			} else {
				let decipher = crypto.createDecipheriv('aes-128-cbc', this._tokenKey, this._tokenIV);
				this.data = Buffer.concat([
					decipher.update(encrypted),
					decipher.final()
				]);
			}
		} else {
			this.data = null;
		}
	}

	get token() {
		return this._token;
	}

	set token(t) {
		this._token = Buffer.from(t);
		this._tokenKey = crypto.createHash('md5').update(t).digest();
		this._tokenIV = crypto.createHash('md5').update(this._tokenKey).update(t).digest();
	}

	get checksum() {
		return this.header.slice(16);
	}

	get deviceId() {
		return this.header.readUInt32BE(8);
	}

	get stamp() {
		return this.header.readUInt32BE(12);
	}
}

module.exports = Packet;
<commit_msg>Reducing the number of device handshakes needed<commit_after>'use strict';

const crypto = require('crypto');
const debug = require('debug')('miio.packet');

class Packet {
	constructor() {
		this.header = Buffer.alloc(2 + 2 + 4 + 4 + 4 + 16);
		this.header[0] = 0x21;
		this.header[1] = 0x31;

		for(let i=4; i<32; i++) {
			this.header[i] = 0xff;
		}

		this._serverStampTime = 0;
	}

	handshake() {
		this.data = null;
	}

	handleHandshakeReply() {
		if(typeof this._token === 'undefined') {
			const token = this.checksum;
			if(token.toString('hex').match(/^[fF0]+$/)) {
				// Device did not return its token so we set our token to null
				this._token = null;
			} else {
				this.token = this.checksum;
			}
		}
	}

	get needsHandshake() {
		/*
		 * Handshake if we:
		 * 1) do not have a token
		 * 2) it has been longer then 120 seconds since last received message
		 */
		return ! this._token || (Date.now() - this._serverStampTime) > 120000;
	}

	get raw() {
		if(this.data) {
			// Send a command to the device
			if(! this._token) {
				throw new Error('Token is required to send commands');
			}

			for(let i=4; i<8; i++) {
				this.header[i] = 0x00;
			}

			// Update the stamp to match server
			if(this._serverStampTime) {
				const secondsPassed = Math.floor(Date.now() - this._serverStampTime) / 1000;
				this.header.writeUInt32BE(this._serverStamp + secondsPassed, 12)
			}

			// Encrypt the data
			let cipher = crypto.createCipheriv('aes-128-cbc', this._tokenKey, this._tokenIV);
			let encrypted = Buffer.concat([
				cipher.update(this.data),
				cipher.final()
			]);

			// Set the length
			this.header.writeUInt16BE(32 + encrypted.length, 2);

			// Calculate the checksum
			let digest = crypto.createHash('md5')
				.update(this.header.slice(0, 16))
				.update(this._token)
				.update(encrypted)
				.digest();
			digest.copy(this.header, 16);

			debug('->', this.header);
			return Buffer.concat([ this.header, encrypted ]);
		} else {
			// Handshake
			this.header.writeUInt16BE(32, 2);

			for(let i=4; i<32; i++) {
				this.header[i] = 0xff;
			}

			debug('->', this.header);
			return this.header;
		}
	}

	set raw(msg) {
		msg.copy(this.header, 0, 0, 32);
		debug('<-', this.header);

		const stamp = this.stamp;
		if(stamp > 0) {
			// If the device returned a stamp, store it
			this._serverStamp = this.stamp;
			this._serverStampTime = Date.now();
		}

		const encrypted = msg.slice(32);

		if(encrypted.length > 0) {
			if(! this._token) {
				debug('<- No token set, unable to handle packet');
				this.data = null;
				return;
			}

			const digest = crypto.createHash('md5')
				.update(this.header.slice(0, 16))
				.update(this._token)
				.update(encrypted)
				.digest();

			const checksum = this.checksum;
			if(! checksum.equals(digest)) {
				debug('<- Invalid packet, checksum was', checksum, 'should be', digest);
				this.data = null;
			} else {
				let decipher = crypto.createDecipheriv('aes-128-cbc', this._tokenKey, this._tokenIV);
				this.data = Buffer.concat([
					decipher.update(encrypted),
					decipher.final()
				]);
			}
		} else {
			this.data = null;
		}
	}

	get token() {
		return this._token;
	}

	set token(t) {
		this._token = Buffer.from(t);
		this._tokenKey = crypto.createHash('md5').update(t).digest();
		this._tokenIV = crypto.createHash('md5').update(this._tokenKey).update(t).digest();
	}

	get checksum() {
		return this.header.slice(16);
	}

	get deviceId() {
		return this.header.readUInt32BE(8);
	}

	get stamp() {
		return this.header.readUInt32BE(12);
	}
}

module.exports = Packet;
<|endoftext|>