<commit_before>/**
 *
 *  Iterator.js
 *
 *  copyright 2013, Kevin Lindsey
 *
 */

var classUtils = require('kld-class-utils');

/**
 *  Iterator
 *
 *  @param {Object} items...
 *  @returns {Iterator}
 */
function Iterator() {
    var items = Array.prototype.slice.call(arguments, 0);

    this.items = (items.length == 1 && Array.isArray(items[0])) ? items[0] : items;
    this.reset();
}

/**
 *  isIterator
 *
 *  @param {Object} item
 *  @returns {Boolean}
 */
Iterator.isIterator = function(item) {
    return classUtils.isMethod(item, "hasNext") && classUtils.isMethod(item, "next") && classUtils.isMethod(item, "reset");
};

/**
 *  hasNext
 *
 *  @returns {Boolean}
 */
Iterator.prototype.hasNext = function() {
    var result = false;

    if (this.index < this.items.length) {
        var item = this.items[this.index];

        if (Iterator.isIterator(item)) {
            result = item.hasNext();
        }
        else {
            result = true;
        }
    }

    return result;
};

/**
 *  next
 *
 *  @returns {Object | null}
 */
Iterator.prototype.next = function() {
    if (this.hasNext()) {
        var item = this.items[this.index];
        var result;

        if (Iterator.isIterator(item)) {
            result = item.next();

            if (item.hasNext() === false) {
                this.index++;
            }
        }
        else {
            result = item;
            this.index++;
        }

        return result;
    }
    else {
        return null;
    }
};

/**
 *  reset
 */
Iterator.prototype.reset = function() {
    this.items.forEach(function(item) {
        if (Iterator.isIterator(item)) {
            item.reset();
        }
    });
    this.index = 0;
};

// array-like routines

/**
 *  forEach
 *
 *  @param {Method(Object, Integer, Iterator) | Function(Object, Integer, Iterator)} callback
 *  @param {Object} scope
 */
Iterator.prototype.forEach = function(callback, scope) {
    var index = 0;

    while (this.hasNext()) {
        callback.call(scope, this.next(), index++, this);
    }
};

/**
 *  every
 *
 *  @param {Method(Object, Integer, Iterator)->Boolean | Function(Object, Integer, Iterator)->Boolean} callback
 *  @param {Object} scope
 *  @returns {Boolean}
 */
Iterator.prototype.every = function(callback, scope) {
    var index = 0;
    var result = true;

    while (this.hasNext()) {
        if (callback.call(scope, this.next(), index++, this) === false) {
            result = false;
            break;
        }
    }

    return result;
};

/**
 *  some
 *
 *  @param {Method(Object, Integer, Iterator)->Boolean | Function(Object, Integer, Iterator)->Boolean} callback
 *  @param {Object} scope
 *  @returns {Boolean}
 */
Iterator.prototype.some = function(callback, scope) {
    var index = 0;
    var result = false;

    while (this.hasNext()) {
        if (callback.call(scope, this.next(), index++, this)) {
            result = true;
            break;
        }
    }

    return result;
};

/**
 *  filter
 *
 *  @param {Method(Object, Integer, Iterator)->Boolean | Function(Object, Integer, Iterator)->Boolean} callback
 *  @param {Object} scope
 *  @returns {Array}
 */
Iterator.prototype.filter = function(callback, scope) {
    var index = 0;
    var result = [];

    while (this.hasNext()) {
        var value = this.next();

        if (callback.call(scope, value, index++, this)) {
            result.push(value);
        }
    }

    return result;
};

/**
 *  map
 *
 *  @param {Method(Object, Integer, Iterator)->Object | Function(Object, Integer, Iterator)->Object} callback
 *  @param {Object} scope
 *  @returns {Array}
 */
Iterator.prototype.map = function(callback, scope) {
    var index = 0;
    var result = [];

    while (this.hasNext()) {
        result.push(callback.call(scope, this.next(), index++, this));
    }

    return result;
};

/**
 *  reduce
 *
 *  @param {Method(Object, Integer, Iterator)->Object | Function(Object, Integer, Iterator)->Object} callback
 *  @param {Object} scope
 *  @returns {Array}
 */
Iterator.prototype.reduce = function(callback, value) {
    var index = 0;

    while (this.hasNext()) {
        if (value === undefined && index == 0) {
            value = this.next();
        }
        else {
            value = callback(value, this.next(), index, this);
        }

        index++;
    }

    return value;
};

// TODO: Hmmm, is reduceRight possible? Collect all values, then
// Array.reduceRight? Doesn't seem like a good idea.

// other miscellaneous convenience functions

/**
 *  skip
 *
 *  @param {Number} count
 */
Iterator.prototype.skip = function(count) {
    while (count > 0 && this.hasNext()) {
        this.next();
        count--;
    }
};

/**
 *  take
 *
 *  @param {Number} count
 *  @returns {Array}
 */
Iterator.prototype.take = function(count) {
    var result = [];

    while (count > 0 && this.hasNext()) {
        result.push(this.next());
        count--;
    }

    return result;
};

/**
 *  takeAll
 *
 *  @returns {Array}
 */
Iterator.prototype.takeAll = function() {
    var result = [];

    while (this.hasNext()) {
        result.push(this.next());
    }

    return result;
};

module.exports = Iterator;
<commit_msg>Return "this" with skip so it can be chained<commit_after>/**
 *
 *  Iterator.js
 *
 *  copyright 2013, Kevin Lindsey
 *
 */

var classUtils = require('kld-class-utils');

/**
 *  Iterator
 *
 *  @param {Object} items...
 *  @returns {Iterator}
 */
function Iterator() {
    var items = Array.prototype.slice.call(arguments, 0);

    this.items = (items.length == 1 && Array.isArray(items[0])) ? items[0] : items;
    this.reset();
}

/**
 *  isIterator
 *
 *  @param {Object} item
 *  @returns {Boolean}
 */
Iterator.isIterator = function(item) {
    return classUtils.isMethod(item, "hasNext") && classUtils.isMethod(item, "next") && classUtils.isMethod(item, "reset");
};

/**
 *  hasNext
 *
 *  @returns {Boolean}
 */
Iterator.prototype.hasNext = function() {
    var result = false;

    if (this.index < this.items.length) {
        var item = this.items[this.index];

        if (Iterator.isIterator(item)) {
            result = item.hasNext();
        }
        else {
            result = true;
        }
    }

    return result;
};

/**
 *  next
 *
 *  @returns {Object | null}
 */
Iterator.prototype.next = function() {
    if (this.hasNext()) {
        var item = this.items[this.index];
        var result;

        if (Iterator.isIterator(item)) {
            result = item.next();

            if (item.hasNext() === false) {
                this.index++;
            }
        }
        else {
            result = item;
            this.index++;
        }

        return result;
    }
    else {
        return null;
    }
};

/**
 *  reset
 */
Iterator.prototype.reset = function() {
    this.items.forEach(function(item) {
        if (Iterator.isIterator(item)) {
            item.reset();
        }
    });
    this.index = 0;
};

// array-like routines

/**
 *  forEach
 *
 *  @param {Method(Object, Integer, Iterator) | Function(Object, Integer, Iterator)} callback
 *  @param {Object} scope
 */
Iterator.prototype.forEach = function(callback, scope) {
    var index = 0;

    while (this.hasNext()) {
        callback.call(scope, this.next(), index++, this);
    }
};

/**
 *  every
 *
 *  @param {Method(Object, Integer, Iterator)->Boolean | Function(Object, Integer, Iterator)->Boolean} callback
 *  @param {Object} scope
 *  @returns {Boolean}
 */
Iterator.prototype.every = function(callback, scope) {
    var index = 0;
    var result = true;

    while (this.hasNext()) {
        if (callback.call(scope, this.next(), index++, this) === false) {
            result = false;
            break;
        }
    }

    return result;
};

/**
 *  some
 *
 *  @param {Method(Object, Integer, Iterator)->Boolean | Function(Object, Integer, Iterator)->Boolean} callback
 *  @param {Object} scope
 *  @returns {Boolean}
 */
Iterator.prototype.some = function(callback, scope) {
    var index = 0;
    var result = false;

    while (this.hasNext()) {
        if (callback.call(scope, this.next(), index++, this)) {
            result = true;
            break;
        }
    }

    return result;
};

/**
 *  filter
 *
 *  @param {Method(Object, Integer, Iterator)->Boolean | Function(Object, Integer, Iterator)->Boolean} callback
 *  @param {Object} scope
 *  @returns {Array}
 */
Iterator.prototype.filter = function(callback, scope) {
    var index = 0;
    var result = [];

    while (this.hasNext()) {
        var value = this.next();

        if (callback.call(scope, value, index++, this)) {
            result.push(value);
        }
    }

    return result;
};

/**
 *  map
 *
 *  @param {Method(Object, Integer, Iterator)->Object | Function(Object, Integer, Iterator)->Object} callback
 *  @param {Object} scope
 *  @returns {Array}
 */
Iterator.prototype.map = function(callback, scope) {
    var index = 0;
    var result = [];

    while (this.hasNext()) {
        result.push(callback.call(scope, this.next(), index++, this));
    }

    return result;
};

/**
 *  reduce
 *
 *  @param {Method(Object, Integer, Iterator)->Object | Function(Object, Integer, Iterator)->Object} callback
 *  @param {Object} scope
 *  @returns {Array}
 */
Iterator.prototype.reduce = function(callback, value) {
    var index = 0;

    while (this.hasNext()) {
        if (value === undefined && index == 0) {
            value = this.next();
        }
        else {
            value = callback(value, this.next(), index, this);
        }

        index++;
    }

    return value;
};

// TODO: Hmmm, is reduceRight possible? Collect all values, then
// Array.reduceRight? Doesn't seem like a good idea.

// other miscellaneous convenience functions

/**
 *  skip
 *
 *  @param {Number} count
 @  @returns {Iterator}
 */
Iterator.prototype.skip = function(count) {
    while (count > 0 && this.hasNext()) {
        this.next();
        count--;
    }

    return this;
};

/**
 *  take
 *
 *  @param {Number} count
 *  @returns {Array}
 */
Iterator.prototype.take = function(count) {
    var result = [];

    while (count > 0 && this.hasNext()) {
        result.push(this.next());
        count--;
    }

    return result;
};

/**
 *  takeAll
 *
 *  @returns {Array}
 */
Iterator.prototype.takeAll = function() {
    var result = [];

    while (this.hasNext()) {
        result.push(this.next());
    }

    return result;
};

module.exports = Iterator;
<|endoftext|>


<commit_before>var childProcess = require('child_process'),
    Stream = require('stream').Stream,
    util = require('util'),
    which = require('which'),
    memoizeAsync = require('memoizeasync'),
    getJpegTranBinaryPath = memoizeAsync(function (cb) {
        which('jpegtran', function (err, jpegTranBinaryPath) {
            if (err) {
                jpegTranBinaryPath = require('jpegtran-bin').path;
            }
            if (jpegTranBinaryPath) {
                cb(null, jpegTranBinaryPath);
            } else {
                cb(new Error('No jpegtran binary in PATH and jpegtran-bin does not provide a pre-built binary for your architecture'));
            }
        });
    });

function JpegTran(jpegTranArgs) {
    Stream.call(this);

    this.writable = this.readable = true;

    getJpegTranBinaryPath(function (err, jpegTranBinaryPath) {
        if (err) {
            return this.emit('error', err);
        }
        this.jpegTranProcess = childProcess.spawn(jpegTranBinaryPath, jpegTranArgs);

        this.hasEnded = false;
        this.seenDataOnStdout = false;

        this.jpegTranProcess.on('exit', function (exitCode) {
            if (exitCode > 0 && !this.hasEnded) {
                return this.emit('error', new Error('The jpegtran process exited with a non-zero exit code: ' + exitCode));
                this.hasEnded = true;
            }
        }.bind(this));

        this.jpegTranProcess.stdout.on('data', function (chunk) {
            this.seenDataOnStdout = true;
            this.emit('data', chunk);
        }.bind(this)).on('end', function () {
            if (!this.hasEnded) {
                if (this.seenDataOnStdout) {
                    this.emit('end');
                } else {
                    this.emit('error', new Error('JpegTran: The stdout stream ended without emitting any data'));
                }
                this.hasEnded = true;
            }
        }.bind(this));

        if (this.isPaused) {
            this.jpegTranProcess.stdout.pause();
        }

        if (this.bufferedChunks) {
            this.bufferedChunks.forEach(function (chunk) {
                if (chunk === null) {
                    this.jpegTranProcess.stdin.end();
                } else {
                    this.jpegTranProcess.stdin.write(chunk);
                }
            }, this);
            this.bufferedChunks = null;
        }
    }.bind(this));
}

util.inherits(JpegTran, Stream);

JpegTran.prototype.write = function (chunk) {
    if (this.jpegTranProcess) {
        this.jpegTranProcess.stdin.write(chunk);
    } else {
        (this.bufferedChunks = this.bufferedChunks || []).push(chunk);
    }
};

JpegTran.prototype.end = function (chunk) {
    if (chunk) {
        this.write(chunk);
    }
    if (this.jpegTranProcess) {
        this.jpegTranProcess.stdin.end();
    } else {
        this.bufferedChunks.push(null);
    }
};

JpegTran.prototype.pause = function () {
    if (this.jpegTranProcess) {
        this.jpegTranProcess.stdout.pause();
    }
    this.isPaused = true;
};

JpegTran.prototype.resume = function () {
    if (this.jpegTranProcess) {
        this.jpegTranProcess.stdout.resume();
    }
    this.isPaused = false;
};

module.exports = JpegTran;
<commit_msg>Expose getBinaryPath on the constructor.<commit_after>var childProcess = require('child_process'),
    Stream = require('stream').Stream,
    util = require('util'),
    which = require('which'),
    memoizeAsync = require('memoizeasync');

function JpegTran(jpegTranArgs) {
    Stream.call(this);

    this.writable = this.readable = true;

    JpegTran.getBinaryPath(function (err, jpegTranBinaryPath) {
        if (err) {
            return this.emit('error', err);
        }
        this.jpegTranProcess = childProcess.spawn(jpegTranBinaryPath, jpegTranArgs);

        this.hasEnded = false;
        this.seenDataOnStdout = false;

        this.jpegTranProcess.on('exit', function (exitCode) {
            if (exitCode > 0 && !this.hasEnded) {
                return this.emit('error', new Error('The jpegtran process exited with a non-zero exit code: ' + exitCode));
                this.hasEnded = true;
            }
        }.bind(this));

        this.jpegTranProcess.stdout.on('data', function (chunk) {
            this.seenDataOnStdout = true;
            this.emit('data', chunk);
        }.bind(this)).on('end', function () {
            if (!this.hasEnded) {
                if (this.seenDataOnStdout) {
                    this.emit('end');
                } else {
                    this.emit('error', new Error('JpegTran: The stdout stream ended without emitting any data'));
                }
                this.hasEnded = true;
            }
        }.bind(this));

        if (this.isPaused) {
            this.jpegTranProcess.stdout.pause();
        }

        if (this.bufferedChunks) {
            this.bufferedChunks.forEach(function (chunk) {
                if (chunk === null) {
                    this.jpegTranProcess.stdin.end();
                } else {
                    this.jpegTranProcess.stdin.write(chunk);
                }
            }, this);
            this.bufferedChunks = null;
        }
    }.bind(this));
}

util.inherits(JpegTran, Stream);

JpegTran.getBinaryPath = memoizeAsync(function (cb) {
    which('jpegtran', function (err, jpegTranBinaryPath) {
        if (err) {
            jpegTranBinaryPath = require('jpegtran-bin').path;
        }
        if (jpegTranBinaryPath) {
            cb(null, jpegTranBinaryPath);
        } else {
            cb(new Error('No jpegtran binary in PATH and jpegtran-bin does not provide a pre-built binary for your architecture'));
        }
    });
});

JpegTran.prototype.write = function (chunk) {
    if (this.jpegTranProcess) {
        this.jpegTranProcess.stdin.write(chunk);
    } else {
        (this.bufferedChunks = this.bufferedChunks || []).push(chunk);
    }
};

JpegTran.prototype.end = function (chunk) {
    if (chunk) {
        this.write(chunk);
    }
    if (this.jpegTranProcess) {
        this.jpegTranProcess.stdin.end();
    } else {
        this.bufferedChunks.push(null);
    }
};

JpegTran.prototype.pause = function () {
    if (this.jpegTranProcess) {
        this.jpegTranProcess.stdout.pause();
    }
    this.isPaused = true;
};

JpegTran.prototype.resume = function () {
    if (this.jpegTranProcess) {
        this.jpegTranProcess.stdout.resume();
    }
    this.isPaused = false;
};

module.exports = JpegTran;
<|endoftext|>


<commit_before>/*
[Check the tests](../test/Observer.html "Observer")
________________________________________________________________________________________________
*/
define(function () { 'use strict';

    // The constructor can be used both to create new subjects and to turn arbitrary objects into observables
    function Subject ( targetObject ) {
        if (typeof targetObject === 'object') {
            targetObject.publish = Subject.prototype.publish
            targetObject.run = Subject.prototype.run
            targetObject.on = Subject.prototype.on
            targetObject.off = Subject.prototype.off
        }
        (targetObject || this).__base__ = new Topic()
    }

    Subject.prototype = {

        // _Method_ __publish__ `boolean` If any callback returns false we immediately exit otherwise we simply return true to indicate
        // that all callbacks were fired without interference
        //
        //   +   __String__ `topic` the event type
        //   +   __...?__ `data` any data you want passed to the callbacks
        publish : function ( topic, data ) {
            var topicNode = this.__base__[topic],
                listeners, len

            if ( !topicNode ) {

                topicNode = this.__base__
                topic = topic.split('.')
                len = 0

                while ( len < topic.length ) {
                    if ( topicNode.hasOwnProperty(topic[len]) ) {
                        topicNode = topicNode[topic[len++]]
                    } else {
                        break
                    }
                }
            }

            do {

                listeners = topicNode._listeners,
                len = listeners.length - 1

                // [Performance test](http://jsperf.com/while-vs-if "loop setup cost")
                if ( len !== -1 ) {
                    // ...and trigger each subscription, from highest to lowest priority
                    do {
                        // Returning false from a handler will prevent any further subscriptions from being notified
                        if ( (topic = listeners[len]).callback.call(topic.context, data) === false ) {
                            return false
                        }
                    } while ( len-- )
                }

            } while ( topicNode = topicNode._parent )

            return true
        },

        // _Method_ __run__ A quicker version of publish designed to trigger top level topics as quickly as possible
        //
        //   +   __String__ `topic` the event type
        //   +   __...?__ `data` any data you want passed to the callbacks
        run : function ( topic, data ) {
            var len
            // By getting the sub reference immediatly we don't need to worry about subscriptions
            // changing since both subscribe and unsubscribe copy the listener array rather than augment it
            if (topic = this.__base__[topic]) {
                topic = topic._listeners
                len = topic.length - 1
                if (len !== -1) {
                    do {
                        if (topic[len].trigger(data) === false) {
                            return false
                        }
                    } while ( len-- )
                }
                return true
            }
            // If no topic exists `undefined` is returned
        },

        //  _Method_ __on__ `listenerObject`
        //
        //  +   _optional_ __string__ `topics` a ' ' seperate list of topics In the format `lvl1.lvl2.lvl3.etc`
        //  +   _optional_ __object__ `context`
        //  +   __function__ `callback` the function to handle events. Should take one argument, `data`
        //  +   _optional_ __number__ `priority` 1 will trigger before 2 etc
        on : function ( topics, context, callback, priority ) {
            switch (arguments.length) {
            case 3:
                if (typeof callback === 'number') {
                    priority = callback
                    callback = context
                    context = window
                } else {
                    priority = 0
                }
                break
            case 2:
                callback = context
                context = window
                priority = 0
                break
            case 1:
                callback = topics
                topics = ''
                context = window
                priority = 0
                break
            case 0:
                throw 'Insufficient arguments'
                break
            }
            if (typeof callback !== 'function') {
                throw 'Incorrect argument format'
            }
            if (typeof priority !== 'number') {
                throw 'Incorrect argument format'
            }
            // No error checking for correct context type since the user may want `this` to equal something weird

            var listenerData = new Subscription(context, callback, priority)

            if ( topics === '' ) {
                // Install to top level
                this.__base__.insertListener(listenerData)
            } else {
                // Multiple subscriptions can be set at the same time, in fact it is recommended as they end up sharing memory this way
                // No need to throw error for incorrect topic since accessing `split` on a non-string will throw an error anyway
                topics.split(' ').forEach(function (topic) {
                    var topicObject = this.__base__

                    var directions = topic.split('.')
                    // find the correct topic to insert the subscription on
                    for ( var location = 0, destination = directions.length; location < destination; location++ ) {
                        // Create a new topic if one does not already exist
                        if ( topicObject.hasOwnProperty([directions[location]]) ) {
                            topicObject = topicObject[directions[location]]
                        } else {
                            topicObject = topicObject.createSubTopic(directions[location], directions.slice(0, location + 1).join('.'))
                        }
                    }

                    topicObject.insertListener(listenerData)

                }, this)
            }

            // since the object which ultimately gets subscribed is returned you can catch it in a variable and use that later to unsubscribe in a more specific fashion than
            // would otherwise be if unsubscribing by callback, which removes all matching callbacks on the given topic. Returning the subscribed objects is also a plus
            // for plug-in developers who can augment a subscriptions behavior after the fact
            return listenerData
        },

        //  _Method_ __off__
        //
        //  +   __String__ `topic` the event type
        //  +   _optional_ __function|string__ `callback`
        //    + If you do not pass a callback then all sunscriptions will be rmove from that topic
        //    + If you pass a string then all subscriptions with a callback name matching that string will be remove
        //    + If you pass a function then all subscriptions with that function will be removed
        off : function ( topics, callback ) {
            if (typeof topics !== 'string') {
                throw 'Need tp provide a topic'
            }

            topics.split(' ').forEach(function seek (topic) {
                var direction = car(topic)

                if (!direction) {
                    this.removeListener(callback)
                } else {
                    // Stop if we are heading down a non-existant path
                    if (!this.hasOwnProperty(direction)) {
                        return
                    }
                    seek.call(this[direction], cdr(topic))
                }

            }, this.__base__)
        },
        // Incase the user doesn't know where the pub/sub instance came from
        constructor : Subject
    }

    function Subscription (context, callback, priority) {
        this.context = context
        this.callback = callback
        this.priority = priority
    }

    // All new subscriptions are returned to the user from the subscribe function. Therefore, the subscription prototype is a good place to add smarts
    Subscription.prototype = {
        trigger : function (data) {
            // [Call is faster than apply](http://jsperf.com/apply-vs-call-vs-invoke/9 "jsperf apply vs call vs invoke")
            return this.callback.call(this.context, data)
        }
        // TODO: add an unsubscribe method
    }

    function Topic ( parentNode ) {
        this._listeners = []
        this._parent = parentNode
    }

    Topic.prototype = {

        createSubTopic : function ( subName, fullAddress ) {
            this[subName] = new Topic(this)
            var topicObject = this
            while ( topicObject._parent ) {
                topicObject = topicObject._parent
            }
            // Create top level mapping
            return topicObject[fullAddress] = this[subName]
        },

        insertListener : function ( subscriptionData ) {
            var listeners = this._listeners.slice(),
                len = listeners.length,
                priority = subscriptionData.priority,
                i = 0,
                added = false

            for ( ; i < len; i++ ) {
                if ( listeners[i].priority >= priority ) {
                    listeners.splice( i , 0, subscriptionData )
                    added = true
                    break
                }
            }

            if ( !added ) {
                listeners.push(subscriptionData)
            }

            // Because the topic now references a new object any publication processes will not be affected
            this._listeners = listeners
        },

        removeListener : function ( callback ) {
            var check

            switch (typeof callback) {
            case 'function':
                check = function (listenerData) {
                    return listenerData.callback !== callback
                }
                break;
            case 'string':
                check = function (listenerData) {
                    return listenerData.callback.name !== callback
                }
                break
            case 'object':
                check = function (listenerData) {
                    return listenerData !== callback
                }
                break
            default:
                // if the user didn't pass a callback, all listeners will be removed
                check = function () {
                    return false
                }
            }

            this._listeners = this._listeners.filter(check)
        },

        invokeListeners : function ( data ) {
            var listeners = this._listeners,
                len = listeners.length - 1
            // [Performance test](http://jsperf.com/while-vs-if "loop setup cost")
            if (len !== -1) {
                // ...and trigger each subscription, from highest to lowest priority
                do {
                    // Returning false from a handler will prevent any further subscriptions from being notified
                    if (listeners[len].trigger(data) === false) {
                        return false
                    }
                } while (len--)
            }

            // If we made it this far it means no subscriptions canceled propagation. So we return true to let the user know
            return true
        }
    }

    function car ( topic ) {
        var i = topic.indexOf('.')
        if (i === -1) {
            return topic
        } else {
            return topic.substr(0, i)
        }
    }

    function cdr ( topic ) {
        var i = topic.indexOf('.')
        if (i === -1) {
            return ''
        } else {
            return topic.substr(i + 1)
        }
    }

    // Create aliases
    Subject.prototype.unsubscribe = Subject.prototype.off
    Subject.prototype.subscribe = Subject.prototype.on

    // Make supporting constructors available on Subject so as to allow extension developers to subclass them
    Subject.Subscription = Subscription

    return Subject
})<commit_msg>clarified predicates<commit_after>/*
[Check the tests](../test/Observer.html "Observer")
________________________________________________________________________________________________
*/
define(function () { 'use strict';

    // The constructor can be used both to create new subjects and to turn arbitrary objects into observables
    function Subject ( targetObject ) {
        if (typeof targetObject === 'object') {
            targetObject.publish = Subject.prototype.publish
            targetObject.run = Subject.prototype.run
            targetObject.on = Subject.prototype.on
            targetObject.off = Subject.prototype.off
        }
        (targetObject || this).__base__ = new Topic()
    }

    Subject.prototype = {

        // _Method_ __publish__ `boolean` If any callback returns false we immediately exit otherwise we simply return true to indicate
        // that all callbacks were fired without interference
        //
        //   +   __String__ `topic` the event type
        //   +   __...?__ `data` any data you want passed to the callbacks
        publish : function ( topic, data ) {
            var topicNode = this.__base__[topic],
                listeners, len

            if ( !topicNode ) {

                topicNode = this.__base__
                topic = topic.split('.')
                len = 0

                while ( len < topic.length ) {
                    if ( topicNode.hasOwnProperty(topic[len]) ) {
                        topicNode = topicNode[topic[len++]]
                    } else {
                        break
                    }
                }
            }

            do {

                listeners = topicNode._listeners,
                len = listeners.length - 1

                // [Performance test](http://jsperf.com/while-vs-if "loop setup cost")
                if ( len >= 0 ) {
                    // ...and trigger each subscription, from highest to lowest priority
                    do {
                        // Returning false from a handler will prevent any further subscriptions from being notified
                        if ( (topic = listeners[len]).callback.call(topic.context, data) === false ) {
                            return false
                        }
                    } while ( len-- )
                }

            } while ( (topicNode = topicNode._parent) !== undefined )

            return true
        },

        // _Method_ __run__ A quicker version of publish designed to trigger top level topics as quickly as possible
        //
        //   +   __String__ `topic` the event type
        //   +   __...?__ `data` any data you want passed to the callbacks
        run : function ( topic, data ) {
            var len
            // By getting the sub reference immediatly we don't need to worry about subscriptions
            // changing since both subscribe and unsubscribe copy the listener array rather than augment it
            if (topic = this.__base__[topic]) {
                topic = topic._listeners
                len = topic.length - 1
                if (len !== -1) {
                    do {
                        if (topic[len].trigger(data) === false) {
                            return false
                        }
                    } while ( len-- )
                }
                return true
            }
            // If no topic exists `undefined` is returned
        },

        //  _Method_ __on__ `listenerObject`
        //
        //  +   _optional_ __string__ `topics` a ' ' seperate list of topics In the format `lvl1.lvl2.lvl3.etc`
        //  +   _optional_ __object__ `context`
        //  +   __function__ `callback` the function to handle events. Should take one argument, `data`
        //  +   _optional_ __number__ `priority` 1 will trigger before 2 etc
        on : function ( topics, context, callback, priority ) {
            switch (arguments.length) {
            case 3:
                if (typeof callback === 'number') {
                    priority = callback
                    callback = context
                    context = window
                } else {
                    priority = 0
                }
                break
            case 2:
                callback = context
                context = window
                priority = 0
                break
            case 1:
                callback = topics
                topics = ''
                context = window
                priority = 0
                break
            case 0:
                throw 'Insufficient arguments'
                break
            }
            if (typeof callback !== 'function') {
                throw 'Incorrect argument format'
            }
            if (typeof priority !== 'number') {
                throw 'Incorrect argument format'
            }
            // No error checking for correct context type since the user may want `this` to equal something weird

            var listenerData = new Subscription(context, callback, priority)

            if ( topics === '' ) {
                // Install to top level
                this.__base__.insertListener(listenerData)
            } else {
                // Multiple subscriptions can be set at the same time, in fact it is recommended as they end up sharing memory this way
                // No need to throw error for incorrect topic since accessing `split` on a non-string will throw an error anyway
                topics.split(' ').forEach(function (topic) {
                    var topicObject = this.__base__

                    var directions = topic.split('.')
                    // find the correct topic to insert the subscription on
                    for ( var location = 0, destination = directions.length; location < destination; location++ ) {
                        // Create a new topic if one does not already exist
                        if ( topicObject.hasOwnProperty([directions[location]]) ) {
                            topicObject = topicObject[directions[location]]
                        } else {
                            topicObject = topicObject.createSubTopic(directions[location], directions.slice(0, location + 1).join('.'))
                        }
                    }

                    topicObject.insertListener(listenerData)

                }, this)
            }

            // since the object which ultimately gets subscribed is returned you can catch it in a variable and use that later to unsubscribe in a more specific fashion than
            // would otherwise be if unsubscribing by callback, which removes all matching callbacks on the given topic. Returning the subscribed objects is also a plus
            // for plug-in developers who can augment a subscriptions behavior after the fact
            return listenerData
        },

        //  _Method_ __off__
        //
        //  +   __String__ `topic` the event type
        //  +   _optional_ __function|string__ `callback`
        //    + If you do not pass a callback then all sunscriptions will be rmove from that topic
        //    + If you pass a string then all subscriptions with a callback name matching that string will be remove
        //    + If you pass a function then all subscriptions with that function will be removed
        off : function ( topics, callback ) {
            if (typeof topics !== 'string') {
                throw 'Need tp provide a topic'
            }

            topics.split(' ').forEach(function seek (topic) {
                var direction = car(topic)

                if (!direction) {
                    this.removeListener(callback)
                } else {
                    // Stop if we are heading down a non-existant path
                    if (!this.hasOwnProperty(direction)) {
                        return
                    }
                    seek.call(this[direction], cdr(topic))
                }

            }, this.__base__)
        },
        // Incase the user doesn't know where the pub/sub instance came from
        constructor : Subject
    }

    function Subscription (context, callback, priority) {
        this.context = context
        this.callback = callback
        this.priority = priority
    }

    // All new subscriptions are returned to the user from the subscribe function. Therefore, the subscription prototype is a good place to add smarts
    Subscription.prototype = {
        trigger : function (data) {
            // [Call is faster than apply](http://jsperf.com/apply-vs-call-vs-invoke/9 "jsperf apply vs call vs invoke")
            return this.callback.call(this.context, data)
        }
        // TODO: add an unsubscribe method
    }

    function Topic ( parentNode ) {
        this._listeners = []
        this._parent = parentNode
    }

    Topic.prototype = {

        createSubTopic : function ( subName, fullAddress ) {
            this[subName] = new Topic(this)
            var topicObject = this
            while ( topicObject._parent ) {
                topicObject = topicObject._parent
            }
            // Create top level mapping
            return topicObject[fullAddress] = this[subName]
        },

        insertListener : function ( subscriptionData ) {
            var listeners = this._listeners.slice(),
                len = listeners.length,
                priority = subscriptionData.priority,
                i = 0,
                added = false

            for ( ; i < len; i++ ) {
                if ( listeners[i].priority >= priority ) {
                    listeners.splice( i , 0, subscriptionData )
                    added = true
                    break
                }
            }

            if ( !added ) {
                listeners.push(subscriptionData)
            }

            // Because the topic now references a new object any publication processes will not be affected
            this._listeners = listeners
        },

        removeListener : function ( callback ) {
            var check

            switch (typeof callback) {
            case 'function':
                check = function (listenerData) {
                    return listenerData.callback !== callback
                }
                break;
            case 'string':
                check = function (listenerData) {
                    return listenerData.callback.name !== callback
                }
                break
            case 'object':
                check = function (listenerData) {
                    return listenerData !== callback
                }
                break
            default:
                // if the user didn't pass a callback, all listeners will be removed
                check = function () {
                    return false
                }
            }

            this._listeners = this._listeners.filter(check)
        },

        invokeListeners : function ( data ) {
            var listeners = this._listeners,
                len = listeners.length - 1
            // [Performance test](http://jsperf.com/while-vs-if "loop setup cost")
            if (len !== -1) {
                // ...and trigger each subscription, from highest to lowest priority
                do {
                    // Returning false from a handler will prevent any further subscriptions from being notified
                    if (listeners[len].trigger(data) === false) {
                        return false
                    }
                } while (len--)
            }

            // If we made it this far it means no subscriptions canceled propagation. So we return true to let the user know
            return true
        }
    }

    function car ( topic ) {
        var i = topic.indexOf('.')
        if (i === -1) {
            return topic
        } else {
            return topic.substr(0, i)
        }
    }

    function cdr ( topic ) {
        var i = topic.indexOf('.')
        if (i === -1) {
            return ''
        } else {
            return topic.substr(i + 1)
        }
    }

    // Create aliases
    Subject.prototype.unsubscribe = Subject.prototype.off
    Subject.prototype.subscribe = Subject.prototype.on

    // Make supporting constructors available on Subject so as to allow extension developers to subclass them
    Subject.Subscription = Subscription

    return Subject
})<|endoftext|>


<commit_before>/*globals define*/
/*jshint node:true, browser:true*/

/**
 * This plugin is used to generate/update the metamodel given a schema generated
 * from keras.
 */

define([
    'plugin/PluginConfig',
    'keras/Constants',
    'text!./metadata.json',
    'text!./schema.json',
    'plugin/PluginBase',
    'common/util/guid',
    'underscore',
    'q'
], function (
    PluginConfig,
    Constants,
    pluginMetadata,
    schemaText,
    PluginBase,
    generateGuid,
    _,
    Q
) {
    'use strict';

    pluginMetadata = JSON.parse(pluginMetadata);
    const SCHEMAS = JSON.parse(schemaText).filter(schema => !schema.abstract);

    const DEFAULT_META_TAB = 'META';

    /**
     * Initializes a new instance of UpdateMeta.
     * @class
     * @augments {PluginBase}
     * @classdesc This class represents the plugin UpdateMeta.
     * @constructor
     */
    var UpdateMeta = function () {
        // Call base class' constructor.
        PluginBase.call(this);
        this.pluginMetadata = pluginMetadata;
    };

    /**
     * Metadata associated with the plugin. Contains id, name, version, description, icon, configStructue etc.
     * This is also available at the instance at this.pluginMetadata.
     * @type {object}
     */
    UpdateMeta.metadata = pluginMetadata;

    // Prototypical inheritance from PluginBase.
    UpdateMeta.prototype = Object.create(PluginBase.prototype);
    UpdateMeta.prototype.constructor = UpdateMeta;

    /**
     * Main function for the plugin to execute. This will perform the execution.
     * Notes:
     * - Always log with the provided logger.[error,warning,info,debug].
     * - Do NOT put any user interaction logic UI, etc. inside this method.
     * - callback always has to be called even if error happened.
     *
     * @param {function(string, plugin.PluginResult)} callback - the result callback
     */
    UpdateMeta.prototype.main = function (callback) {
        this.metaSheets = {};
        this.sheetCounts = {};
        this.nodes = {};
        // TODO: Generate the metamodel from the schema

        return this.prepareMetaModel()
            .then(() => this.createCategories(SCHEMAS))
            .then(() => this.updateNodes())
            .then(() => this.save('UpdateMeta updated metamodel.'))
            .then(() => {
                this.result.setSuccess(true);
                callback(null, this.result);
            })
            .catch(err => {
                // Result success is false at invocation.
                callback(err, this.result);
            });

    };

    UpdateMeta.prototype.prepareMetaModel = function () {
        // Create the base class, if needed
        this.metaSheets.META = this.createMetaSheetTab('META');
        if (!this.META.Language) {
            let node = this.core.createNode({
                parent: this.rootNode,
                base: this.META.FCO
            });
            this.core.setAttribute(node, 'name', 'Language');
            this.META.Language = node;
        }

        if (!this.META.Architecture) {
            this.META.Architecture = this.createMetaNode('Architecture', this.META.FCO);
        }

        if (!this.META.Layer) {
            this.META.Layer = this.createMetaNode('Layer', this.META.FCO);
        }

        //var isNewLayer = {},
            //newLayers = schemas.map(layer => layer.name),
            //oldLayers,
            //oldNames;

        //newLayers = newLayers.concat(this.getCategories());  // add the category nodes
        //newLayers.forEach(name => isNewLayer[name] = true);

        //// Set the newLayer nodes 'base' to 'Layer' so we don't accidentally
        //// delete them
        //newLayers
            //.map(name => this.META[name])
            //.filter(layer => !!layer)
            //.forEach(layer => this.core.setBase(layer, this.META.Layer));

        //oldLayers = Object.keys(this.META)
                //.filter(name => name !== 'Layer')
                //.map(name => this.META[name])
                //.filter(node => this.isMetaTypeOf(node, this.META.Layer))
                //.filter(node => !isNewLayer[this.core.getAttribute(node, 'name')]);

        //oldNames = oldLayers.map(l => this.core.getAttribute(l, 'name'));
        //// Get the old layer names
        //this.logger.debug(`Removing layers: ${oldNames.join(', ')}`);
        //oldLayers.forEach(layer => this.core.deleteNode(layer));
        return Q();
    };

    UpdateMeta.prototype.getCategories = function (schemas) {
        let content = {};

        schemas = schemas || SCHEMAS;
        schemas.forEach(layer => {
            var type = this.getBaseName(layer.file);
            if (!content[type]) {
                content[type] = [];
            }
            content[type].push(layer);
        });

        return Object.keys(content);
    };

    UpdateMeta.prototype.createCategories = function (schemas) {
        // Get the categories
        // Create the category nodes
        var categories = this.getCategories(schemas);
        categories
            .forEach(name => {
                let node = this.META[name];
                this.metaSheets[name] = this.createMetaSheetTab(name);
                if (!node) {
                    this.logger.debug(`Creating category ${name}`);
                    node = this.createMetaNode(name, this.META.Layer, name);
                }

                // Create a tab for each
                this.nodes[name] = node;
            });

        // Make them abstract
        categories
            .forEach(name => this.core.setRegistry(this.META[name], 'isAbstract', true));

    };

    UpdateMeta.prototype.getBaseName = function (filename) {
        var type = filename.split('/').pop().replace(/\.py$/, '');
        return type[0].toUpperCase() + type.substring(1);
    };

    UpdateMeta.prototype.getBaseFor = function (layer) {
        var type = this.getBaseName(layer.file);
        if (!this.META[type]) type = 'Layer';
        return this.META[type];
    };

    UpdateMeta.prototype.updateNodes = function () {
        return Q.all(SCHEMAS.map(schema => this.updateNode(schema)));
    };

    UpdateMeta.prototype.updateNode = function (layer) {
        // Create the actual nodes
        this.logger.debug(`adding "${layer.name}" layer`);
        let node = this.createMetaLayer(layer);

        // Make the node non-abstract
        if (node) {
            this.core.setRegistry(node, 'isAbstract', false);
            this.nodes[layer.name] = node;
        }
    };

    UpdateMeta.prototype.removeFromMeta = function (nodeId) {
        var sheets = this.core.getRegistry(this.rootNode, Constants.REGISTRY.META_SHEETS),
            sheet;

        // Remove from meta
        this.core.delMember(this.rootNode, Constants.META_ASPECT_SET_NAME, nodeId);

        // Remove from the given meta sheet
        sheet = sheets.find(sheet => {
            var paths = this.core.getMemberPaths(this.rootNode, sheet.SetID);
            return paths.indexOf(nodeId) > -1;
        });

        if (sheet) {
            this.core.delMember(this.rootNode, sheet.SetID, nodeId);
        }
    };

    UpdateMeta.prototype.createMetaSheetTab = function (name) {
        var sheets = this.core.getRegistry(this.rootNode, Constants.REGISTRY.META_SHEETS),
            id = Constants.META_ASPECT_SHEET_NAME_PREFIX + generateGuid(),
            sheet,
            desc = {
                SetID: id,
                order: sheets.length,
                title: name
            };

        sheet = sheets.find(sheet => sheet.title === name);
        if (!sheet) {
            sheet = desc;
            this.logger.debug(`creating meta sheet "${name}"`);
            this.core.createSet(this.rootNode, sheet.SetID);
            sheets.push(sheet);
            this.core.setRegistry(this.rootNode, Constants.REGISTRY.META_SHEETS, sheets);
        }
        return sheet.SetID;
    };

    // Some helper methods w/ attribute handling
    UpdateMeta.prototype.createMetaLayer = function (layer) {
        // create a meta node for the given layer
        var category = this.getBaseName(layer.file);
        var node = this.createMetaNode(layer.name, this.getBaseFor(layer), category);

        // TODO: configure the given node
        if (layer.arguments[0] && layer.arguments[0].name === 'self') {
            layer.arguments.shift();
        }
        var argNames = layer.arguments.map(arg => arg.name);

        layer.arguments.forEach(arg => {
            this.addAttribute(arg.name, node, {type: arg.type}, arg.default);
        });

        // Add ctor args
        this.core.setAttributeMeta(node, Constants.ATTR.CTOR_ARGS, {type: 'string'});
        this.core.setAttribute(node, Constants.ATTR.CTOR_ARGS, argNames.join(','));

        this.logger.debug(`added attributes to ${layer.name}`);

        // Remove attributes not in the given list
        //var currentAttrs = this.core.getValidAttributeNames(node),
            //defVal,
            //rmAttrs,
            //simpleAttrs,
            //rmPtrs;

        //simpleAttrs = argNames;
        //rmAttrs = _.difference(currentAttrs, simpleAttrs)  // old attribute names
            //.filter(attr => attr !== 'name');

        //rmAttrs.forEach(attr => {
            //this.core.delAttributeMeta(node, attr);
            //if (this.core.getOwnAttribute(node, attr) !== undefined) {
                //this.core.delAttribute(node, attr);
            //}
        //});
        this.logger.debug(`removed old attributes for ${layer.name}`);

        return node;
    };

    UpdateMeta.prototype.createMetaNode = function (name, base, tabName) {
        var node = this.META[name],
            nodeId = node && this.core.getPath(node),
            setters = {},
            defaults = {},
            types = {},
            position,
            tabId;

        tabName = tabName || DEFAULT_META_TAB;
        tabId = this.metaSheets[tabName];
        position = this.getPositionFor(name, tabName);

        if (!tabId) {
            var err = `No meta sheet for ${tabName}`;
            this.logger.error(err);
            throw err;
        }

        if (!node) {
            // Create a node
            node = this.core.createNode({
                parent: this.META.Language,
                base: base
            });
            this.core.setAttribute(node, 'name', name);
            this.META[name] = node;

            nodeId = this.core.getPath(node);
        } else {
            // Remove from meta
            this.removeFromMeta(nodeId);
            this.core.setBase(node, base);
        }

        // Add it to the meta sheet
        this.core.addMember(this.rootNode, Constants.META_ASPECT_SET_NAME, node);
        this.core.addMember(this.rootNode, tabId, node);

        this.core.setMemberRegistry(
            this.rootNode,
            Constants.META_ASPECT_SET_NAME,
            nodeId,
            Constants.REGISTRY.POSITION,
            position
        );
        this.core.setMemberRegistry(
            this.rootNode,
            tabId,
            nodeId,
            Constants.REGISTRY.POSITION,
            position
        );

        this.logger.debug(`added ${name} to the meta`);

        return node;
    };

    UpdateMeta.prototype.getPositionFor = function(name, tabName) {
        var index = this.sheetCounts[tabName] || 0,
            position,
            dx = 140,
            dy = 100,
            MAX_WIDTH = 1200,
            x;

        this.sheetCounts[tabName] = index + 1;
        if (index === 0) {
            position = {
                x: MAX_WIDTH/2,
                y: 50
            };
        } else {
            x = dx*index;
            position = {
                x: x%MAX_WIDTH,
                y: Math.floor(x/MAX_WIDTH+1)*dy + 50
            };
        }
        return position;
    };

    UpdateMeta.prototype.addLayerAttribute = function (name, node) {
        // No default value support for now...
        // Create a pointer of the given type on the node
        this.core.setPointerMetaTarget(node, name, this.META.Architecture, 1, 1);
        this.core.setPointerMetaLimits(node, name, 1, 1);
    };

    UpdateMeta.prototype.addAttribute = function (name, node, schema, defVal) {
        schema.type = schema.type || 'string';
        if (schema.type === 'list') {  // FIXME: add support for lists
            schema.type = 'string';
        }

        if (schema.min !== undefined) {
            schema.min = +schema.min;
        }

        if (schema.max !== undefined) {
            // Set the min, max
            schema.max = +schema.max;
        }
        // Add the enum for booleans so we use python style True/False
        if (schema.type === 'boolean') {
            schema.enum = ['True', 'False'];
            schema.type = 'string';
        }

        // Create the attribute and set the schema
        this.core.setAttributeMeta(node, name, schema);

        if (defVal) {
            this.core.setAttribute(node, name, defVal);
        }
    };

    // Helpers for testing
    UpdateMeta.prototype.getSchemas = function () {
        return SCHEMAS;
    };

    return UpdateMeta;
});
<commit_msg>Filtered out name arguments (as it already exists)<commit_after>/*globals define*/
/*jshint node:true, browser:true*/

/**
 * This plugin is used to generate/update the metamodel given a schema generated
 * from keras.
 */

define([
    'plugin/PluginConfig',
    'keras/Constants',
    'text!./metadata.json',
    'text!./schema.json',
    'plugin/PluginBase',
    'common/util/guid',
    'underscore',
    'q'
], function (
    PluginConfig,
    Constants,
    pluginMetadata,
    schemaText,
    PluginBase,
    generateGuid,
    _,
    Q
) {
    'use strict';

    pluginMetadata = JSON.parse(pluginMetadata);
    const SCHEMAS = JSON.parse(schemaText).filter(schema => !schema.abstract);

    const DEFAULT_META_TAB = 'META';

    /**
     * Initializes a new instance of UpdateMeta.
     * @class
     * @augments {PluginBase}
     * @classdesc This class represents the plugin UpdateMeta.
     * @constructor
     */
    var UpdateMeta = function () {
        // Call base class' constructor.
        PluginBase.call(this);
        this.pluginMetadata = pluginMetadata;
    };

    /**
     * Metadata associated with the plugin. Contains id, name, version, description, icon, configStructue etc.
     * This is also available at the instance at this.pluginMetadata.
     * @type {object}
     */
    UpdateMeta.metadata = pluginMetadata;

    // Prototypical inheritance from PluginBase.
    UpdateMeta.prototype = Object.create(PluginBase.prototype);
    UpdateMeta.prototype.constructor = UpdateMeta;

    /**
     * Main function for the plugin to execute. This will perform the execution.
     * Notes:
     * - Always log with the provided logger.[error,warning,info,debug].
     * - Do NOT put any user interaction logic UI, etc. inside this method.
     * - callback always has to be called even if error happened.
     *
     * @param {function(string, plugin.PluginResult)} callback - the result callback
     */
    UpdateMeta.prototype.main = function (callback) {
        this.metaSheets = {};
        this.sheetCounts = {};
        this.nodes = {};
        // TODO: Generate the metamodel from the schema

        return this.prepareMetaModel()
            .then(() => this.createCategories(SCHEMAS))
            .then(() => this.updateNodes())
            .then(() => this.save('UpdateMeta updated metamodel.'))
            .then(() => {
                this.result.setSuccess(true);
                callback(null, this.result);
            })
            .catch(err => {
                // Result success is false at invocation.
                callback(err, this.result);
            });

    };

    UpdateMeta.prototype.prepareMetaModel = function () {
        // Create the base class, if needed
        this.metaSheets.META = this.createMetaSheetTab('META');
        if (!this.META.Language) {
            let node = this.core.createNode({
                parent: this.rootNode,
                base: this.META.FCO
            });
            this.core.setAttribute(node, 'name', 'Language');
            this.META.Language = node;
        }

        if (!this.META.Architecture) {
            this.META.Architecture = this.createMetaNode('Architecture', this.META.FCO);
        }

        if (!this.META.Layer) {
            this.META.Layer = this.createMetaNode('Layer', this.META.FCO);
        }

        //var isNewLayer = {},
            //newLayers = schemas.map(layer => layer.name),
            //oldLayers,
            //oldNames;

        //newLayers = newLayers.concat(this.getCategories());  // add the category nodes
        //newLayers.forEach(name => isNewLayer[name] = true);

        //// Set the newLayer nodes 'base' to 'Layer' so we don't accidentally
        //// delete them
        //newLayers
            //.map(name => this.META[name])
            //.filter(layer => !!layer)
            //.forEach(layer => this.core.setBase(layer, this.META.Layer));

        //oldLayers = Object.keys(this.META)
                //.filter(name => name !== 'Layer')
                //.map(name => this.META[name])
                //.filter(node => this.isMetaTypeOf(node, this.META.Layer))
                //.filter(node => !isNewLayer[this.core.getAttribute(node, 'name')]);

        //oldNames = oldLayers.map(l => this.core.getAttribute(l, 'name'));
        //// Get the old layer names
        //this.logger.debug(`Removing layers: ${oldNames.join(', ')}`);
        //oldLayers.forEach(layer => this.core.deleteNode(layer));
        return Q();
    };

    UpdateMeta.prototype.getCategories = function (schemas) {
        let content = {};

        schemas = schemas || SCHEMAS;
        schemas.forEach(layer => {
            var type = this.getBaseName(layer.file);
            if (!content[type]) {
                content[type] = [];
            }
            content[type].push(layer);
        });

        return Object.keys(content);
    };

    UpdateMeta.prototype.createCategories = function (schemas) {
        // Get the categories
        // Create the category nodes
        var categories = this.getCategories(schemas);
        categories
            .forEach(name => {
                let node = this.META[name];
                this.metaSheets[name] = this.createMetaSheetTab(name);
                if (!node) {
                    this.logger.debug(`Creating category ${name}`);
                    node = this.createMetaNode(name, this.META.Layer, name);
                }

                // Create a tab for each
                this.nodes[name] = node;
            });

        // Make them abstract
        categories
            .forEach(name => this.core.setRegistry(this.META[name], 'isAbstract', true));

    };

    UpdateMeta.prototype.getBaseName = function (filename) {
        var type = filename.split('/').pop().replace(/\.py$/, '');
        return type[0].toUpperCase() + type.substring(1);
    };

    UpdateMeta.prototype.getBaseFor = function (layer) {
        var type = this.getBaseName(layer.file);
        if (!this.META[type]) type = 'Layer';
        return this.META[type];
    };

    UpdateMeta.prototype.updateNodes = function () {
        return Q.all(SCHEMAS.map(schema => this.updateNode(schema)));
    };

    UpdateMeta.prototype.updateNode = function (layer) {
        // Create the actual nodes
        this.logger.debug(`adding "${layer.name}" layer`);
        let node = this.createMetaLayer(layer);

        // Make the node non-abstract
        if (node) {
            this.core.setRegistry(node, 'isAbstract', false);
            this.nodes[layer.name] = node;
        }
    };

    UpdateMeta.prototype.removeFromMeta = function (nodeId) {
        var sheets = this.core.getRegistry(this.rootNode, Constants.REGISTRY.META_SHEETS),
            sheet;

        // Remove from meta
        this.core.delMember(this.rootNode, Constants.META_ASPECT_SET_NAME, nodeId);

        // Remove from the given meta sheet
        sheet = sheets.find(sheet => {
            var paths = this.core.getMemberPaths(this.rootNode, sheet.SetID);
            return paths.indexOf(nodeId) > -1;
        });

        if (sheet) {
            this.core.delMember(this.rootNode, sheet.SetID, nodeId);
        }
    };

    UpdateMeta.prototype.createMetaSheetTab = function (name) {
        var sheets = this.core.getRegistry(this.rootNode, Constants.REGISTRY.META_SHEETS),
            id = Constants.META_ASPECT_SHEET_NAME_PREFIX + generateGuid(),
            sheet,
            desc = {
                SetID: id,
                order: sheets.length,
                title: name
            };

        sheet = sheets.find(sheet => sheet.title === name);
        if (!sheet) {
            sheet = desc;
            this.logger.debug(`creating meta sheet "${name}"`);
            this.core.createSet(this.rootNode, sheet.SetID);
            sheets.push(sheet);
            this.core.setRegistry(this.rootNode, Constants.REGISTRY.META_SHEETS, sheets);
        }
        return sheet.SetID;
    };

    // Some helper methods w/ attribute handling
    UpdateMeta.prototype.createMetaLayer = function (layer) {
        // create a meta node for the given layer
        var category = this.getBaseName(layer.file);
        var node = this.createMetaNode(layer.name, this.getBaseFor(layer), category);

        // Clean the arguments
        if (layer.arguments[0] && layer.arguments[0].name === 'self') {
            layer.arguments.shift();
        }
        layer.arguments = layer.arguments.filter(arg => arg.name !== 'name');

        var argNames = layer.arguments.map(arg => arg.name);

        layer.arguments.forEach(arg => {
            this.addAttribute(arg.name, node, {type: arg.type}, arg.default);
        });

        // Add ctor args
        this.core.setAttributeMeta(node, Constants.ATTR.CTOR_ARGS, {type: 'string'});
        this.core.setAttribute(node, Constants.ATTR.CTOR_ARGS, argNames.join(','));

        this.logger.debug(`added attributes to ${layer.name}`);

        // Remove attributes not in the given list
        //var currentAttrs = this.core.getValidAttributeNames(node),
            //defVal,
            //rmAttrs,
            //simpleAttrs,
            //rmPtrs;

        //simpleAttrs = argNames;
        //rmAttrs = _.difference(currentAttrs, simpleAttrs)  // old attribute names
            //.filter(attr => attr !== 'name');

        //rmAttrs.forEach(attr => {
            //this.core.delAttributeMeta(node, attr);
            //if (this.core.getOwnAttribute(node, attr) !== undefined) {
                //this.core.delAttribute(node, attr);
            //}
        //});
        this.logger.debug(`removed old attributes for ${layer.name}`);

        return node;
    };

    UpdateMeta.prototype.createMetaNode = function (name, base, tabName) {
        var node = this.META[name],
            nodeId = node && this.core.getPath(node),
            setters = {},
            defaults = {},
            types = {},
            position,
            tabId;

        tabName = tabName || DEFAULT_META_TAB;
        tabId = this.metaSheets[tabName];
        position = this.getPositionFor(name, tabName);

        if (!tabId) {
            var err = `No meta sheet for ${tabName}`;
            this.logger.error(err);
            throw err;
        }

        if (!node) {
            // Create a node
            node = this.core.createNode({
                parent: this.META.Language,
                base: base
            });
            this.core.setAttribute(node, 'name', name);
            this.META[name] = node;

            nodeId = this.core.getPath(node);
        } else {
            // Remove from meta
            this.removeFromMeta(nodeId);
            this.core.setBase(node, base);
        }

        // Add it to the meta sheet
        this.core.addMember(this.rootNode, Constants.META_ASPECT_SET_NAME, node);
        this.core.addMember(this.rootNode, tabId, node);

        this.core.setMemberRegistry(
            this.rootNode,
            Constants.META_ASPECT_SET_NAME,
            nodeId,
            Constants.REGISTRY.POSITION,
            position
        );
        this.core.setMemberRegistry(
            this.rootNode,
            tabId,
            nodeId,
            Constants.REGISTRY.POSITION,
            position
        );

        this.logger.debug(`added ${name} to the meta`);

        return node;
    };

    UpdateMeta.prototype.getPositionFor = function(name, tabName) {
        var index = this.sheetCounts[tabName] || 0,
            position,
            dx = 140,
            dy = 100,
            MAX_WIDTH = 1200,
            x;

        this.sheetCounts[tabName] = index + 1;
        if (index === 0) {
            position = {
                x: MAX_WIDTH/2,
                y: 50
            };
        } else {
            x = dx*index;
            position = {
                x: x%MAX_WIDTH,
                y: Math.floor(x/MAX_WIDTH+1)*dy + 50
            };
        }
        return position;
    };

    UpdateMeta.prototype.addLayerAttribute = function (name, node) {
        // No default value support for now...
        // Create a pointer of the given type on the node
        this.core.setPointerMetaTarget(node, name, this.META.Architecture, 1, 1);
        this.core.setPointerMetaLimits(node, name, 1, 1);
    };

    UpdateMeta.prototype.addAttribute = function (name, node, schema, defVal) {
        schema.type = schema.type || 'string';
        if (schema.type === 'list') {  // FIXME: add support for lists
            schema.type = 'string';
        }

        if (schema.min !== undefined) {
            schema.min = +schema.min;
        }

        if (schema.max !== undefined) {
            // Set the min, max
            schema.max = +schema.max;
        }
        // Add the enum for booleans so we use python style True/False
        if (schema.type === 'boolean') {
            schema.enum = ['True', 'False'];
            schema.type = 'string';
        }

        // Create the attribute and set the schema
        this.core.setAttributeMeta(node, name, schema);

        if (defVal) {
            this.core.setAttribute(node, name, defVal);
        }
    };

    // Helpers for testing
    UpdateMeta.prototype.getSchemas = function () {
        return SCHEMAS;
    };

    return UpdateMeta;
});
<|endoftext|>


<commit_before>var vow = require('vow'),
    fs = require('fs'),
    vfs = require('vow-fs'),
    path = require('path'),
    _ = require('lodash'),
    EventEmitter = require('events').EventEmitter,
    Sortr = require('./Sortr'),
    logr = require('./logr');

/**
 * @event Schedulr#posted
 * @type {Object}
 * @prop {String} fileName
 * @prop {String} postId
 */

/**
 * Folder scaner, job list maker and runner.
 * @param {Postr} postr
 * @constructor
 */
var Schedulr = function (postr) {
    var config = postr.getConfig(),
        folderPath = postr.getFolderPath(),
        doneFolderPath = folderPath + path.sep + 'done',
        failedFolderPath = folderPath + path.sep + 'failed',
        api = Object.create(EventEmitter.prototype),
        sortr = new Sortr(config.order);

    /**
     * Get postr instance
     * @returns {Postr}
     */
    api.getPostr = function () {
        return postr;
    };

    /**
     * Get files list
     * @returns {vow.Promise.<String[]>}
     */
    api.collectFiles = function () {
        return vfs.listDir(folderPath).then(function (list) {
            return vow.allResolved(list.reduce(function (promises, fileName) {
                promises[fileName] = vfs.stat(folderPath + path.sep + fileName);
                return promises;
            }, {})).then(function (statsInfo) {
                return Object.keys(_.pick(statsInfo, function (promise) {
                    return promise.isResolved() && promise.valueOf().isFile();
                }));
            });
        });
    };

    /**
     * Create service folders 'done' and 'failed'
     * @returns {vow.Promise}
     */
    api.ensureServiceFolders = function () {
        return vow.all([
            vfs.makeDir(doneFolderPath),
            vfs.makeDir(failedFolderPath)
        ]);
    };

    /**
     * Just run it already
     * @returns {vow.Promise}
     */
    api.run = function () {
        api.ensureServiceFolders()
            .then(function () {
                return api.collectFiles();
            })
            .then(function (list) {
                return runWithList(list);
            });
    };

    /**
     * Run schedulr for given list, list could be modified on the fly
     * @param {String[]} list
     * @returns {vow.Promise}
     */
    function runWithList(list) {
        var watcher, uploader;

        sortr.setList(list);

        watcher = require('chokidar').watch(folderPath, {
            ignored: function (filePath) {
                if (filePath === folderPath) {
                    return false;
                }
                return filePath.indexOf(doneFolderPath + path.sep) === 0 ||
                    filePath.indexOf(failedFolderPath + path.sep) === 0;
            },
            ignoreInitial: true,
            followSymlinks: false,
            depth: 1
        });

        watcher.on('add', function (filePath) {
            list.push(filePath.substr(folderPath.length + 1));
            sortr.update();
        });

        uploader = function (fileName) {
            if (fileName) {
                postr.post(fileName).always(function (promise) {
                    var value = promise.valueOf(),
                        targetFolder = promise.isFulfilled() ? doneFolderPath : failedFolderPath;

                    if (promise.isFulfilled()) {
                        api.emit('posted', {
                            fileName: fileName,
                            postId: value
                        });
                    } else {
                        api.emit('failed', {
                            fileName: fileName,
                            error: value
                        });
                    }
                    try {
                        fs.rename(folderPath + path.sep + fileName, targetFolder + path.sep + fileName);
                    } catch (err) {
                        logr.debug('failed to move %s', folderPath + path.sep + fileName);
                    }
                    setTimeout(uploader.bind(api, sortr.next()), config.interval * 1000);
                }).done();
            }
        };

        uploader(sortr.next());

        return vow.fulfill();
    };

    return api;
};

module.exports = Schedulr;
<commit_msg>Scheduler fix, debug extend<commit_after>var vow = require('vow'),
    fs = require('fs'),
    vfs = require('vow-fs'),
    path = require('path'),
    _ = require('lodash'),
    EventEmitter = require('events').EventEmitter,
    Sortr = require('./Sortr'),
    logr = require('./logr');

/**
 * @event Schedulr#posted
 * @type {Object}
 * @prop {String} fileName
 * @prop {String} postId
 */

/**
 * Folder scaner, job list maker and runner.
 * @param {Postr} postr
 * @constructor
 */
var Schedulr = function (postr) {
    var config = postr.getConfig(),
        folderPath = postr.getFolderPath(),
        doneFolderPath = folderPath + path.sep + 'done',
        failedFolderPath = folderPath + path.sep + 'failed',
        api = Object.create(EventEmitter.prototype),
        sortr = new Sortr(config.order);

    /**
     * Get postr instance
     * @returns {Postr}
     */
    api.getPostr = function () {
        return postr;
    };

    /**
     * Get files list
     * @returns {vow.Promise.<String[]>}
     */
    api.collectFiles = function () {
        return vfs.listDir(folderPath).then(function (list) {
            return vow.allResolved(list.reduce(function (promises, fileName) {
                promises[fileName] = vfs.stat(folderPath + path.sep + fileName);
                return promises;
            }, {})).then(function (statsInfo) {
                return Object.keys(_.pick(statsInfo, function (promise) {
                    return promise.isResolved() && promise.valueOf().isFile();
                }));
            });
        });
    };

    /**
     * Create service folders 'done' and 'failed'
     * @returns {vow.Promise}
     */
    api.ensureServiceFolders = function () {
        return vow.all([
            vfs.makeDir(doneFolderPath),
            vfs.makeDir(failedFolderPath)
        ]);
    };

    /**
     * Just run it already
     * @returns {vow.Promise}
     */
    api.run = function () {
        api.ensureServiceFolders()
            .then(function () {
                return api.collectFiles();
            })
            .then(function (list) {
                return runWithList(list);
            });
    };

    /**
     * Run schedulr for given list, list could be modified on the fly
     * @param {String[]} list
     * @returns {vow.Promise}
     */
    function runWithList(list) {
        var watcher, uploader;

        sortr.setList(list);
        if (list.length) {
            list.forEach(function (fileName) {
                logr.debug('queued on start: ' + folderPath + '/' + fileName);
            });
        } else {
            logr.debug('starting with empty list');
        }

        watcher = require('chokidar').watch(folderPath, {
            ignored: function (filePath) {
                if (filePath === folderPath) {
                    return false;
                }
                return filePath.indexOf(doneFolderPath + path.sep) === 0 ||
                    filePath.indexOf(failedFolderPath + path.sep) === 0;
            },
            ignoreInitial: true,
            followSymlinks: false,
            depth: 1
        });

        watcher.on('add', function (filePath) {
            logr.debug('add to queue: ' + filePath + ', ' + list.length + 1 + ' files currently');
            list.push(filePath.substr(folderPath.length + 1));
            sortr.update();
        });

        uploader = function (fileName) {
            if (fileName) {
                postr.post(fileName).always(function (promise) {
                    var value = promise.valueOf(),
                        targetFolder = promise.isFulfilled() ? doneFolderPath : failedFolderPath;

                    if (promise.isFulfilled()) {
                        api.emit('posted', {
                            fileName: fileName,
                            postId: value
                        });
                    } else {
                        api.emit('failed', {
                            fileName: fileName,
                            error: value
                        });
                    }
                    try {
                        fs.rename(folderPath + path.sep + fileName, targetFolder + path.sep + fileName);
                    } catch (err) {
                        logr.debug('failed to move %s', folderPath + path.sep + fileName);
                    }
                    setTimeout(uploader.bind(api, sortr.next()), config.interval * 1000);
                }).done();
            } else {
                // wait 1 minute if queue is empty for the moment
                setTimeout(uploader.bind(api, sortr.next()), 60 * 1000);
            }
        };

        uploader(sortr.next());

        return vow.fulfill();
    };

    return api;
};

module.exports = Schedulr;
<|endoftext|>


<commit_before>
'use strict';

var Class = require('cakes').Class;

var _     = require('./utils');
var Model = require('./model');

/**
 * ORM warpper
 * @return {Function} Constructor
 */
var ThinkORM = Class(function() {

    var _modelInstances = {};

    return {

        __construct: function(config) {
            this.config = config;

            var identifier = _.serialize(config);
            if (!_modelInstances[identifier]) {
                _modelInstances[identifier] = {};
            }
        },

        /**
         * Create a model which map to table.
         * @param  {String}  modelName Name of model
         * @param  {Object}  prop      Extend or override Model method
         * @param  {Boolean} force     Force to get a new instance
         * @return {Object}            A model instance
         */
        model: function(modelName, prop, force) {
            var identifier = _.serialize(this.config);
            var curInstances = _modelInstances[identifier];

            if (curInstances && curInstances[modelName]) {
                if (!force) {
                    return curInstances[modelName];
                }

                curInstances[modelName] = null;
            }

            if (!curInstances) {
                _modelInstances[identifier] = {};
            }

            var m = new (Class(prop, Model))(modelName, prop, this.config);
            _modelInstances[identifier][modelName] = m;

            return _modelInstances[identifier][modelName];
        }
    };
});

module.exports = ThinkORM;
<commit_msg>Fixed table prefix argument for `model` constructor.<commit_after>
'use strict';

var Class = require('cakes').Class;

var _     = require('./utils');
var Model = require('./model');

/**
 * ORM warpper
 * @return {Function} Constructor
 */
var ThinkORM = Class(function() {

    var _modelInstances = {};

    return {

        __construct: function(config) {
            this.config = config;

            var identifier = _.serialize(config);
            if (!_modelInstances[identifier]) {
                _modelInstances[identifier] = {};
            }
        },

        /**
         * Create a model which map to table.
         * @param  {String}  modelName Name of model
         * @param  {Object}  prop      Extend or override Model method
         * @param  {Boolean} force     Force to get a new instance
         * @return {Object}            A model instance
         */
        model: function(modelName, prop, force) {
            var identifier = _.serialize(this.config);
            var curInstances = _modelInstances[identifier];

            if (curInstances && curInstances[modelName]) {
                if (!force) {
                    return curInstances[modelName];
                }

                curInstances[modelName] = null;
            }

            if (!curInstances) {
                _modelInstances[identifier] = {};
            }

            // table prefix
            var prefix = this.config.prefix;
            delete this.config.prefix;

            var m = new (Class(prop, Model))(modelName, prefix, this.config);
            _modelInstances[identifier][modelName] = m;

            return _modelInstances[identifier][modelName];
        }
    };
});

module.exports = ThinkORM;
<|endoftext|>


<commit_before>var fs = require('fs');

function copyFile(source, folder, cb) {
    if (!fs.existsSync(folder + "")) {
        fs.mkdirSync(folder + "", 0766, function (err) {
            if (err) {
                done(err);
                response.send("ERROR! Can't make the directory! \n");    // echo the result back
            }
        });
    }
    var cbCalled = false;

    var rd = fs.createReadStream(source);
    rd.on("error", function (err) {
        done(err);
    });
    var wr = fs.createWriteStream(folder + "/" + source);
    wr.on("error", function (err) {
        done(err);
    });
    wr.on("close", function (ex) {
        done("complete");
    });
    rd.pipe(wr);

	fs.unlink(rd, done);

    function done(err) {
        if (!cbCalled) {
            cb(err);
            cbCalled = true;
        }
    }
}

var files = fs.readdirSync(__dirname);

var maxFile = 0;
var otherFiles = [];

for(var i = 0; i < files.length; i++){
	if(parseInt(files[i]) > maxFile){
		maxFile = parseInt(files[i]);
	}
	else{
		if("strip.js, .git, LICENSE".indexOf(files[i]) == -1){
			otherFiles.push(files[i]);
		}
	}
}

for(var i = 0; i < otherFiles.length; i++){
	copyFile(otherFiles[i], maxFile + 1, console.log);
}

<commit_msg>Remove moved files<commit_after>var fs = require('fs');

function copyFile(source, folder, cb, unlink) {
    if (!fs.existsSync(folder + "")) {
        fs.mkdirSync(folder + "", 0766, function (err) {
            if (err) {
                done(err);
                response.send("ERROR! Can't make the directory! \n");    // echo the result back
            }
        });
    }
    var cbCalled = false;

    var rd = fs.createReadStream(source);
    rd.on("error", function (err) {
        done(err);
    });
    var wr = fs.createWriteStream(folder + "/" + source);
    wr.on("error", function (err) {
        done(err);
    });
    wr.on("close", function (ex) {
        done("complete");
    });
    rd.pipe(wr);

	if(unlink){
		fs.unlink(source, done);
	}

    function done(err) {
        if (!cbCalled) {
            cb(err);
            cbCalled = true;
        }
    }
}

var files = fs.readdirSync(__dirname);

var maxFile = 0;
var otherFiles = [];

for(var i = 0; i < files.length; i++){
	if(parseInt(files[i]) > maxFile){
		maxFile = parseInt(files[i]);
	}
	else{
		if("strip.js, .git, LICENSE".indexOf(files[i]) == -1){
			otherFiles.push(files[i]);
		}
	}
}

for(var i = 0; i < otherFiles.length; i++){
	var unlink = otherFiles[i] != "styles.css";
	copyFile(otherFiles[i], maxFile + 1, console.log, unlink);
}

<|endoftext|>


<commit_before>/**
*
*	ANAGRAMS
*
*
*	DESCRIPTION:
*		- Finds anagrams in an array of strings.
*
*
*	NOTES:
*		[1]
*
*
*	TODO:
*		[1]
*
*
*	LICENSE:
*		MIT
*
*	Copyright (c) 2015. Athan Reines.
*
*
*	AUTHOR:
*		Athan Reines. kgryte@gmail.com. 2015.
*
*/

'use strict';

// FUNCTIONS //

/**
* FUNCTION: ascending( a, b )
*	Comparator function for sorting characters in ascending order.
*
* @private
* @param {String} a - character
* @param {String} b - character
* @returns {Number} comparison value
*/
function ascending( a, b ) {
	if ( a < b ) {
		return -1;
	}
	if ( a === b ) {
		return 0;
	}
	return 1;
} // end FUNCTION ascending()

/**
* FUNCTION: contains( arr, str )
*	Validates if an array contains a specified string.
*
* @private
* @param {String[]} arr - input array
* @param {String} str - input string
* @returns {Boolean} boolean indicating if the input array contains the specified string
*/
function contains( arr, str ) {
	var len = arr.length,
		i;
	for ( i = 0; i < len; i++ ) {
		if ( arr[ i ] === str ) {
			return true;
		}
	}
	return false;
} // end FUNCTION contains()

/**
* FUNCTION: anagrams( arr )
*	Finds anagrams in an string array.
*
* @param {String[]} arr - input string array
* @returns {Array[]|null} array of anagram lists or null
*/
function anagrams( arr ) {
	var hash,
		keys,
		list,
		out,
		str,
		key,
		len,
		i;

	// [1] Create a hash...
	len = arr.length;
	hash = {};

	// For each string...
	for ( i = 0; i < len; i++ ) {
		// [1.1] Split the string into separate characters:
		str = arr[ i ];
		key = str.split( '' );

		// [1.2] Sort the characters to create a hash key:
		key.sort( ascending ).join( '' );

		// [1.3] Look-up key in hash...
		if ( hash.hasOwnProperty( key ) ) {
			// Key already exists. Determine if we have already seen this string before...
			list = hash[ key ];
			if ( !contains( list, str ) ) {
				// Hurray! New anagram!
				list.push( str );
			}
		} else {
			// Initialize a new anagram list:
			hash[ key ] = [ str ];
		}
	}
	// [2] Create an output array containing anagram lists having a length greater than 1...
	keys = Object.keys( hash );
	len = keys.length;
	out = [];
	for ( i = 0; i < len; i++ ) {
		list = hash[ keys[ i ] ];
		if ( list.length > 1 ) {
			out.push( list );
		}
	}
	// [3] Return the anagram lists...
	return ( out.length ) ? out : null;
} // end FUNCTION anagrams()

// EXPORTS //

module.exports = anagrams;
<commit_msg>[UPDATE] change from array to object store.<commit_after>/**
*
*	ANAGRAMS
*
*
*	DESCRIPTION:
*		- Finds anagrams in an array of strings.
*
*
*	NOTES:
*		[1]
*
*
*	TODO:
*		[1]
*
*
*	LICENSE:
*		MIT
*
*	Copyright (c) 2015. Athan Reines.
*
*
*	AUTHOR:
*		Athan Reines. kgryte@gmail.com. 2015.
*
*/

'use strict';

// FUNCTIONS //

/**
* FUNCTION: ascending( a, b )
*	Comparator function for sorting characters in ascending order.
*
* @private
* @param {String} a - character
* @param {String} b - character
* @returns {Number} comparison value
*/
function ascending( a, b ) {
	if ( a < b ) {
		return -1;
	}
	if ( a === b ) {
		return 0;
	}
	return 1;
} // end FUNCTION ascending()

/**
* FUNCTION: anagrams( arr )
*	Finds anagrams in an string array.
*
* @param {String[]} arr - input string array
* @returns {Array[]|null} array of anagram lists or null
*/
function anagrams( arr ) {
	var hash,
		keys,
		grams,
		out,
		str,
		key,
		len,
		i;

	// [1] Create a hash...
	hash = {};

	// For each string...
	len = arr.length;
	for ( i = 0; i < len; i++ ) {
		// [1.1] Split the string into separate characters:
		str = arr[ i ];
		key = str.split( '' );

		// [1.2] Sort the characters to create a hash key:
		key.sort( ascending ).join( '' );

		// [1.3] Look-up key in hash...
		if ( hash.hasOwnProperty( key ) ) {
			// Key already exists. Determine if we have already seen this string before...
			grams = hash[ key ];
			if ( grams.hasOwnProperty( str ) ) {
				grams[ str ] += 1;
			} else {
				grams[ str ] = 1;
			}
		} else {
			// Initialize a new anagram store...
			grams = {};
			grams[ str ] = 1;
			hash[ key ] = grams;
		}
	}
	// [2] Create an output array containing anagram lists having a length greater than 1...
	keys = Object.keys( hash );
	len = keys.length;
	out = [];

	for ( i = 0; i < len; i++ ) {
		grams = Object.keys( hash[ keys[ i ] ] );
		if ( grams.length > 1 ) {
			out.push( grams );
		}
	}
	// [3] Return the anagram lists...
	return ( out.length ) ? out : null;
} // end FUNCTION anagrams()

// EXPORTS //

module.exports = anagrams;
<|endoftext|>


<commit_before>'use strict';

var async = require('async');

var debug = require('./util/debug')('analysis');
var createLogger = require('./util/logger');

var Factory = require('./workflow/factory');
var toposort = require('../lib/dag/toposort');
var validator = require('../lib/dag/validator');

var DatabaseService = require('./service/database');

function Analysis(rootNode) {
    this.rootNode = rootNode;
}

Analysis.prototype.id = function() {
    return this.rootNode.id();
};

Analysis.prototype.getQuery = function() {
    return this.rootNode.getQuery();
};

Analysis.prototype.getRoot = function() {
    return this.rootNode;
};

Analysis.prototype.getNodes = function() {
    function buildList(node, nodesList) {
        nodesList = nodesList || [node];

        node.getInputNodes().forEach(function(inputNode) {
            nodesList.push(inputNode);

            buildList(inputNode, nodesList);
        });

        return nodesList;
    }

    return buildList(this.rootNode);
};

Analysis.prototype.getSortedNodes = function() {
    return toposort(this.rootNode);
};

function AnalysisFactory(DatabaseServiceClass) {
    this.DatabaseServiceClass = DatabaseServiceClass;
}

AnalysisFactory.prototype.create = function(configuration, definition, callback) {
    debug('Using configuration=%j', configuration);
    var logger = createLogger(configuration.logger);
    var databaseService = new this.DatabaseServiceClass(
        configuration.user,
        configuration.db,
        configuration.batch,
        logger
    );

    async.waterfall(
        [
            function analysis$createWithFactory(done) {
                var factory = new Factory(configuration.user, databaseService);
                factory.create(JSON.parse(JSON.stringify(definition)), done);
            },
            function analysis$validate(rootNode, done) {
                logger = logger.child({ analysis: rootNode.id(), username: configuration.user }, true);

                databaseService.setLogger(logger);

                if (!validator.isValid(rootNode)) {
                    return done(new Error('Invalid analysis, cycle found'));
                }

                return done(null, new Analysis(rootNode));
            },
            function analysis$register(analysis, done) {
                analysis.getSortedNodes().map(function (node) {
                    logger.info({ node: node.id(), type: node.getType() }, 'analysis:node-type');
                });

                databaseService.registerAnalysisInCatalog(analysis, function(err) {
                    return done(err, analysis);
                });
            },
            function analysis$queueOperations(analysis, done) {
                databaseService.queueAnalysisOperations(analysis, function(err) {
                    return done(err, analysis);
                });
            },
            function analysis$track(analysis, done) {
                databaseService.trackAnalysis(analysis, function(err) {
                    return done(err, analysis);
                });
            }
        ],
        function analysis$finish(err, analysis) {
            if (err) {
                if (err && err.message && err.message.match(/permission denied/i)) {
                    err = new Error('Analysis requires authentication with API key: permission denied.');
                }

                logger.error({ error: err.message }, 'analysis:error');

                return callback(err);
            }

            return callback(null, analysis);
        }
    );
};

module.exports = AnalysisFactory;
var analysisFactory = new AnalysisFactory(DatabaseService);
module.exports.create = analysisFactory.create.bind(analysisFactory);
module.exports.reference = require('../reference');
module.exports.version = require('../package.json').version;
<commit_msg>Remove analysis logger<commit_after>'use strict';

var async = require('async');

var debug = require('./util/debug')('analysis');

var Factory = require('./workflow/factory');
var toposort = require('../lib/dag/toposort');
var validator = require('../lib/dag/validator');

var DatabaseService = require('./service/database');

function Analysis(rootNode) {
    this.rootNode = rootNode;
}

Analysis.prototype.id = function() {
    return this.rootNode.id();
};

Analysis.prototype.getQuery = function() {
    return this.rootNode.getQuery();
};

Analysis.prototype.getRoot = function() {
    return this.rootNode;
};

Analysis.prototype.getNodes = function() {
    function buildList(node, nodesList) {
        nodesList = nodesList || [node];

        node.getInputNodes().forEach(function(inputNode) {
            nodesList.push(inputNode);

            buildList(inputNode, nodesList);
        });

        return nodesList;
    }

    return buildList(this.rootNode);
};

Analysis.prototype.getSortedNodes = function() {
    return toposort(this.rootNode);
};

function AnalysisFactory(DatabaseServiceClass) {
    this.DatabaseServiceClass = DatabaseServiceClass;
}

AnalysisFactory.prototype.create = function(configuration, definition, callback) {
    debug('Using configuration=%j', configuration);
    var databaseService = new this.DatabaseServiceClass(
        configuration.user,
        configuration.db,
        configuration.batch,
        configuration.logger
    );

    async.waterfall(
        [
            function analysis$createWithFactory(done) {
                var factory = new Factory(configuration.user, databaseService);
                factory.create(JSON.parse(JSON.stringify(definition)), done);
            },
            function analysis$validate(rootNode, done) {
                if (!validator.isValid(rootNode)) {
                    return done(new Error('Invalid analysis, cycle found'));
                }

                return done(null, new Analysis(rootNode));
            },
            function analysis$register(analysis, done) {
                databaseService.registerAnalysisInCatalog(analysis, function(err) {
                    return done(err, analysis);
                });
            },
            function analysis$queueOperations(analysis, done) {
                databaseService.queueAnalysisOperations(analysis, function(err) {
                    return done(err, analysis);
                });
            },
            function analysis$track(analysis, done) {
                databaseService.trackAnalysis(analysis, function(err) {
                    return done(err, analysis);
                });
            }
        ],
        function analysis$finish(err, analysis) {
            if (err) {
                if (err && err.message && err.message.match(/permission denied/i)) {
                    err = new Error('Analysis requires authentication with API key: permission denied.');
                }

                return callback(err);
            }

            return callback(null, analysis);
        }
    );
};

module.exports = AnalysisFactory;
var analysisFactory = new AnalysisFactory(DatabaseService);
module.exports.create = analysisFactory.create.bind(analysisFactory);
module.exports.reference = require('../reference');
module.exports.version = require('../package.json').version;
<|endoftext|>