<commit_before>"use strict";

const Plugin = require('../plugin');
const uuid = require('node-uuid');
const crypto = require('crypto');
const Path = require('path');
const os = require('os');
const Data = require('../settings/data');
const clientSessions = require('client-sessions');

//const Data = require('defiant/plugin/settings/data');

// TODO: This entire thing is pre-alpha code quality (i.e. horrible).

//let sessionFile = Path.join(os.tmpDir(), 'sessions.json');

class Session extends Plugin {
  constructor(engine) {
    super(engine);
    this.sessions = {};
    // TODO: Come up with a way to track the age of this session.
    this.volatile = {};

    // Add session information to incoming HTTP requests.
    engine.pluginRegistry.get('Router').addHandler(new (require('./handler/sessionHandler'))());

    // The number of seconds that a session can last past its last use.
    this.sessionLifetime = 60 * 60 * 24 * 30;
  }

  async init() {
    let Settings = this.engine.pluginRegistry.get('Settings');

    // Declare the Session Reader Data item and set its defaults.
    Settings.cacheRegistry.set(new Data({
      id: `session/sessionReader.json`,
      filename: Path.join('session', `sessionReader.json`),
      storage: 'settings',
      storageCanChange: true,
      // TODO: Translate.
      description: 'Stores the settings for the Session Reader.',
      default: {
        cookieName: 'sessionId',
        secret: '',
        // Default duration one week.
        duration: 7 * 24 * 60 * 60 * 1000,
      },
    }));

    // Load the actual values.
    let sessionSettings = Settings.cacheRegistry.get(`session/sessionReader.json`);
    let sessionSettingsData = await sessionSettings.load();
    if (!sessionSettingsData.secret) {
      // Generate a random secret and save it for future use.
      sessionSettingsData.secret = uuid.v4();
      let e = await sessionSettings.save();
      if (e) {
        // TODO: Translate.
        console.error(`Could not write Session Reader settings file.`);
      }
    }

    // Initialize the Session Reader function.
    this.sessionReader = promisify(clientSessions(sessionSettingsData));
  }

  // Read a session file from disk.
  async readSessionFile(id) {
    return new Data({
      filename: Path.join(id.substring(0, 2), id + '.json'),
      storage: 'sessions',
      default: undefined
    }, this.engine.pluginRegistry.get('Settings')).load();
  }

  // Write a session file to disk.
  async writeSessionFile(session) {
    return new Data({
      filename: Path.join(session.id.substring(0, 2), session.id + '.json'),
      storage: 'sessions',
      data: session
    }, this.engine.pluginRegistry.get('Settings')).save();
  }

  // Delete a session file from disk.
  async deleteSessionFile(id) {
    return new Data({
      filename: Path.join(id.substring(0, 2), id + '.json'),
      storage: 'sessions',
    }, this.engine.pluginRegistry.get('Settings')).remove();
  }

  async generateSession(context) {
    let newID,
        currentTime = Math.round(new Date().getTime() / 1000);
    do {
      newID = uuid.v4();
    } while (this.sessions[newID]);

    let session = {
      id: newID,
      expire: currentTime + this.sessionLifetime
    };

    let buff = await new Promise((accept, reject) => {
      crypto.randomBytes(32, (ex, buf) => {accept(buf);});
    });
    session.formApiKey = buff.toString('base64');
    session.formApiKeyRaw = buff.toString('binary');
    this.sessions[session.id] = session;
    this.volatile[session.id] = {};
    context.request.sessionId = {
      id: session.id
    };
    context.session = session;
  }

  // Cron not implemented yet...
  cron() {
    let currentTime = Math.round(new Date().getTime() / 1000);
    let killTime = currentTime + this.sessionLifetime;
    for (let key in this.sessions) {
      if (this.sessions[key].expire < currentTime) {
        console.log('Delete: ' + key);
        delete this.sessions[key];
        delete this.volatile[key];
      }
    }
  }

  startCron() {
    if (this.cronID) {
      clearInterval(this.cronID);
    }
    this.cronID = setInterval(this.cron, Session.cronFrequency * 1000);
  };
}

// The number of seconds between cron runs.
Session.cronFrequency = 60;

module.exports = Session;
<commit_msg>Replace deprecated os.tmpDir() with os.tmpdir()<commit_after>"use strict";

const Plugin = require('../plugin');
const uuid = require('node-uuid');
const crypto = require('crypto');
const Path = require('path');
const os = require('os');
const Data = require('../settings/data');
const clientSessions = require('client-sessions');

//const Data = require('defiant/plugin/settings/data');

// TODO: This entire thing is pre-alpha code quality (i.e. horrible).

//let sessionFile = Path.join(os.tmpdir(), 'sessions.json');

class Session extends Plugin {
  constructor(engine) {
    super(engine);
    this.sessions = {};
    // TODO: Come up with a way to track the age of this session.
    this.volatile = {};

    // Add session information to incoming HTTP requests.
    engine.pluginRegistry.get('Router').addHandler(new (require('./handler/sessionHandler'))());

    // The number of seconds that a session can last past its last use.
    this.sessionLifetime = 60 * 60 * 24 * 30;
  }

  async init() {
    let Settings = this.engine.pluginRegistry.get('Settings');

    // Declare the Session Reader Data item and set its defaults.
    Settings.cacheRegistry.set(new Data({
      id: `session/sessionReader.json`,
      filename: Path.join('session', `sessionReader.json`),
      storage: 'settings',
      storageCanChange: true,
      // TODO: Translate.
      description: 'Stores the settings for the Session Reader.',
      default: {
        cookieName: 'sessionId',
        secret: '',
        // Default duration one week.
        duration: 7 * 24 * 60 * 60 * 1000,
      },
    }));

    // Load the actual values.
    let sessionSettings = Settings.cacheRegistry.get(`session/sessionReader.json`);
    let sessionSettingsData = await sessionSettings.load();
    if (!sessionSettingsData.secret) {
      // Generate a random secret and save it for future use.
      sessionSettingsData.secret = uuid.v4();
      let e = await sessionSettings.save();
      if (e) {
        // TODO: Translate.
        console.error(`Could not write Session Reader settings file.`);
      }
    }

    // Initialize the Session Reader function.
    this.sessionReader = promisify(clientSessions(sessionSettingsData));
  }

  // Read a session file from disk.
  async readSessionFile(id) {
    return new Data({
      filename: Path.join(id.substring(0, 2), id + '.json'),
      storage: 'sessions',
      default: undefined
    }, this.engine.pluginRegistry.get('Settings')).load();
  }

  // Write a session file to disk.
  async writeSessionFile(session) {
    return new Data({
      filename: Path.join(session.id.substring(0, 2), session.id + '.json'),
      storage: 'sessions',
      data: session
    }, this.engine.pluginRegistry.get('Settings')).save();
  }

  // Delete a session file from disk.
  async deleteSessionFile(id) {
    return new Data({
      filename: Path.join(id.substring(0, 2), id + '.json'),
      storage: 'sessions',
    }, this.engine.pluginRegistry.get('Settings')).remove();
  }

  async generateSession(context) {
    let newID,
        currentTime = Math.round(new Date().getTime() / 1000);
    do {
      newID = uuid.v4();
    } while (this.sessions[newID]);

    let session = {
      id: newID,
      expire: currentTime + this.sessionLifetime
    };

    let buff = await new Promise((accept, reject) => {
      crypto.randomBytes(32, (ex, buf) => {accept(buf);});
    });
    session.formApiKey = buff.toString('base64');
    session.formApiKeyRaw = buff.toString('binary');
    this.sessions[session.id] = session;
    this.volatile[session.id] = {};
    context.request.sessionId = {
      id: session.id
    };
    context.session = session;
  }

  // Cron not implemented yet...
  cron() {
    let currentTime = Math.round(new Date().getTime() / 1000);
    let killTime = currentTime + this.sessionLifetime;
    for (let key in this.sessions) {
      if (this.sessions[key].expire < currentTime) {
        console.log('Delete: ' + key);
        delete this.sessions[key];
        delete this.volatile[key];
      }
    }
  }

  startCron() {
    if (this.cronID) {
      clearInterval(this.cronID);
    }
    this.cronID = setInterval(this.cron, Session.cronFrequency * 1000);
  };
}

// The number of seconds between cron runs.
Session.cronFrequency = 60;

module.exports = Session;
<|endoftext|>


<commit_before>
function toArray(value)
{
    return Array.prototype.slice.call(value);
}

// Manually implement this so that Shadow DOM polyfill works.
// https://github.com/Polymer/ShadowDOM/issues/395
Element.prototype.remove = function()
{
    if (!this.parentNode)
        return;
    this.parentNode.removeChild(this);
}

// We only have String#contains if "Experimental JavaScript is enabled."
if (!String.prototype.contains) {
    String.prototype.contains = function(text)
    {
        return this.indexOf(text) != -1;
    }
}
<commit_msg>Don't override Element#remove if the browser supports it.<commit_after>
function toArray(value)
{
    return Array.prototype.slice.call(value);
}

// Manually implement this so that Shadow DOM polyfill works.
// https://github.com/Polymer/ShadowDOM/issues/395
if (!Element.prototype.remove) {
    Element.prototype.remove = function() {
        if (!this.parentNode)
            return;
        this.parentNode.removeChild(this);
    }
}

// We only have String#contains if "Experimental JavaScript is enabled."
if (!String.prototype.contains) {
    String.prototype.contains = function(text) {
        return this.indexOf(text) != -1;
    }
}
<|endoftext|>


<commit_before>var path = require('path'),
    fs = require('fs'),
    crypto = require('crypto'),
    EOL = require('os').EOL,
    vm = require('vm'),
    naming = require('bem-naming'),
    vow = require('vow'),
    enb = require('enb'),
    vfs = enb.asyncFS || require('enb/lib/fs/async-fs'),
    scanner = require('enb-bem-pseudo-levels/lib/level-scanner'),
    BEMJSON_JS_CODE_REGEX = /`{3,}(bemjson\s*[^`]*|js\s*\(?(\{|\[)[^`]*block ?:[^`]*(\]|\})\)?)\s*`{3,}/gi;

/**
 * Возвращает конфигуратор уровня-сетов из примеров БЭМ-блоков с помощью `MagicHelper`.
 * Умеет собирать примеры по md-файлам.
 *
 * @param {MagicHelper} helper
 * @returns {{configure: Function}}
 */
module.exports = function (helper) {
    return {
        /**
         * Настраивает сборку примеров по md-файлам, обрабатывая полученный `bemjson` с помощью `processInlineBemjson`
         * функции. На файловую систему пример запишется с именем равным хэш-сумме от исходного кода примера.
         *
         * Инлайновый пример в md-файле может выглядеть следующим образом:
         *
         * ```bemjson
         * ({
         *     block: 'button',
         *     text: 'Click me!'
         * })
         * ```
         *
         * или
         *
         * ```js
         * ({
         *     block: 'button',
         *     text: 'Click me!'
         * })
         * ```
         *
         * @param {Object}   options
         * @param {String}   options.destPath               Путь до нового уровня-сета относительный корня.
         * @param {Array}    options.levels                 Уровни в которых следует искать примеры.
         * @param {Function} [options.processInlineBemjson] Функция обработки инлайнового bemjson.
         */
        configure: function (options) {
            this._buildTargets(options);
            this._provideTargets(options);
        },

        _buildTargets: function (options) {
            var channel = helper.getEventChannel(),
                root = helper.getRootPath(),
                task = helper._projectConfig._tasks[helper._taskName];

            helper.prebuild(function (magic, logger) {
                var cache = task._makePlatform._cacheStorage;

                return getMdFiles(options.levels)
                    .then(function (filenames) {
                        return getExamplesFromMdFiles(root, options.destPath, filenames);
                    })
                    .then(function (examples) {
                        // Отсеиваем примеры, которые не нужно собирать
                        examples = examples.filter(function (example) {
                            return magic.isRequiredNode(example.path);
                        });

                        return vow.all(examples.map(function (example) {
                            var dirname = path.join(root, example.path),
                                target = path.join(example.path, example.name + '.bemjson.js'),
                                bemjsonFilename = path.join(root, target);

                            if (cache.get('inline-example', bemjsonFilename)) {
                                magic.registerNode(example.path);

                                return example;
                            }

                            var bemjson = buildBemjson(example, bemjsonFilename, options.processInlineBemjson, logger);

                            if (!bemjson) { return undefined; }

                            var examplesBlocksPath = path.join(example.examplesDir, 'all.blocks'),
                                examplesBlocksSymlink = path.join(dirname, 'blocks');

                            // Записываем bemjson на файловую систему
                            return vfs.makeDir(dirname)
                                .then(function () {
                                    return vow.all([
                                        vfs.exists(examplesBlocksPath),
                                        vfs.exists(examplesBlocksSymlink)
                                    ]).spread(function (blocksExists, symlinkExists) {
                                        if (blocksExists && !symlinkExists) {
                                            return vfs.symLink(examplesBlocksPath, examplesBlocksSymlink);
                                        }
                                    });
                                })
                                .then(function () {
                                    return vfs.write(bemjsonFilename, bemjson, 'utf-8');
                                })
                                .then(function () {
                                    cache.set('inline-example', bemjsonFilename, true);

                                    magic.registerNode(example.path);

                                    return example;
                                });
                        }));
                    })
                    .then(function (examples) {
                        channel.emit('inline-examples', options.destPath, examples.filter(function (example) {
                            return example;
                        }));
                    });
            });
        },

        _provideTargets: function (options) {
            helper.configure(function (config, nodes, targets) {
                var targetNodes = {};

                targets && targets.length && targets.forEach(function (target) {
                    targetNodes[path.dirname(target)] = true;
                });

                var setNodes = [].concat(nodes, Object.keys(targetNodes)).filter(function (node) {
                    return node.indexOf(options.destPath) === 0;
                });

                // Провайдим полученные `bemjson.js` файлы
                config.nodes(setNodes, function (nodeConfig) {
                    var node = path.basename(nodeConfig.getNodePath()),
                        symlinkTarget = node + '.bemjson.js.symlink',
                        symlinkFilename = nodeConfig.resolvePath(symlinkTarget);

                    // Проверяем инлайновый ли это пример
                    if (!fs.existsSync(symlinkFilename)) {
                        nodeConfig.addTech([require('enb/techs/file-provider'), { target: '?.bemjson.js' }]);
                        nodeConfig.addTarget('?.bemjson.js');
                    }
                });
            });
        }
    };
};

function buildBemjson(example, bemjsonFilename, callback, logger) {
    var meta = {
            filename: bemjsonFilename,
            notation: example.notation
        },
        bemjson;

    try {
        bemjson = vm.runInNewContext('(' + example.source + ')', {});
    } catch (err) {
        logger.logWarningAction('inline-bemjson', example.sourcePath, err.name + ': ' + err.message);

        return;
    }

    return '(' + JSON.stringify(callback(bemjson, meta)) + ')';
}

function getExamplesFromMdFiles(root, dstpath, filenames) {
    return vow.all(filenames.map(function (filename) {
            return getExamplesFromMdFile(root, dstpath, filename);
        }))
        .then(function (list) {
            return Array.prototype.concat.apply([], list);
        });
}

function getMdFiles(levels) {
    return scanner.scan(levels)
        .then(function (files) {
            var filenames = [];

            files.forEach(function (file) {
                var ext = path.extname(file.fullname);

                if (ext === '.md') {
                    filenames.push(file.fullname);
                }
            });

            return filenames;
        });
}

function getExamplesFromMdFile(root, dstpath, filename) {
    return vfs.read(filename, 'utf-8')
        .then(function (source) {
            var basename = path.basename(filename),
                blockname = basename.split('.')[0],
                notation = naming.parse(blockname),
                scope = path.join(dstpath, notation.block),
                examplesDir = path.resolve(filename, '..', blockname + '.examples'),
                examples = getExamplesFromSource(source);

            return examples.map(function (example) {
                example.sourcePath = path.relative(root, filename);
                example.path = path.join(scope, example.name);
                example.notation = notation;
                example.examplesDir = examplesDir;

                return example;
            });
        });
}

function getExamplesFromSource(source) {
    var matched = source.match(BEMJSON_JS_CODE_REGEX);

    return matched ? matched.map(function (str) {
        var code = str.split(/\r?\n/).slice(1);

        code.pop();
        code = code.join(EOL);

        var shasum = crypto.createHash('sha1'); shasum.update(code);
        var base64 = fixBase64(shasum.digest('base64'));

        return {
            name: base64,
            source: code
        };
    }) : [];
}

function fixBase64(base64) {
    return base64
        .replace(/\+/g, '-')
        .replace(/\//g, '_')
        .replace(/=/g, '')
        .replace(/^[+-]+/g, '');
}
<commit_msg>Symlinking common blocks fix<commit_after>var path = require('path'),
    fs = require('fs'),
    crypto = require('crypto'),
    EOL = require('os').EOL,
    vm = require('vm'),
    naming = require('bem-naming'),
    vow = require('vow'),
    enb = require('enb'),
    vfs = enb.asyncFS || require('enb/lib/fs/async-fs'),
    scanner = require('enb-bem-pseudo-levels/lib/level-scanner'),
    BEMJSON_JS_CODE_REGEX = /`{3,}(bemjson\s*[^`]*|js\s*\(?(\{|\[)[^`]*block ?:[^`]*(\]|\})\)?)\s*`{3,}/gi;

/**
 * Возвращает конфигуратор уровня-сетов из примеров БЭМ-блоков с помощью `MagicHelper`.
 * Умеет собирать примеры по md-файлам.
 *
 * @param {MagicHelper} helper
 * @returns {{configure: Function}}
 */
module.exports = function (helper) {
    return {
        /**
         * Настраивает сборку примеров по md-файлам, обрабатывая полученный `bemjson` с помощью `processInlineBemjson`
         * функции. На файловую систему пример запишется с именем равным хэш-сумме от исходного кода примера.
         *
         * Инлайновый пример в md-файле может выглядеть следующим образом:
         *
         * ```bemjson
         * ({
         *     block: 'button',
         *     text: 'Click me!'
         * })
         * ```
         *
         * или
         *
         * ```js
         * ({
         *     block: 'button',
         *     text: 'Click me!'
         * })
         * ```
         *
         * @param {Object}   options
         * @param {String}   options.destPath               Путь до нового уровня-сета относительный корня.
         * @param {Array}    options.levels                 Уровни в которых следует искать примеры.
         * @param {Function} [options.processInlineBemjson] Функция обработки инлайнового bemjson.
         */
        configure: function (options) {
            this._buildTargets(options);
            this._provideTargets(options);
        },

        _buildTargets: function (options) {
            var channel = helper.getEventChannel(),
                root = helper.getRootPath(),
                task = helper._projectConfig._tasks[helper._taskName];

            helper.prebuild(function (magic, logger) {
                var cache = task._makePlatform._cacheStorage;

                return getMdFiles(options.levels)
                    .then(function (filenames) {
                        return getExamplesFromMdFiles(root, options.destPath, filenames);
                    })
                    .then(function (examples) {
                        // Отсеиваем примеры, которые не нужно собирать
                        examples = examples.filter(function (example) {
                            return magic.isRequiredNode(example.path);
                        });

                        return vow.all(examples.map(function (example) {
                            var dirname = path.join(root, example.path),
                                target = path.join(example.path, example.name + '.bemjson.js'),
                                bemjsonFilename = path.join(root, target);

                            if (cache.get('inline-example', bemjsonFilename)) {
                                magic.registerNode(example.path);

                                return example;
                            }

                            var bemjson = buildBemjson(example, bemjsonFilename, options.processInlineBemjson, logger);

                            if (!bemjson) { return undefined; }

                            var examplesBlocksPath = path.join(example.examplesDir, 'blocks'),
                                examplesBlocksSymlink = path.join(dirname, example.blockName + '.blocks');

                            // Записываем bemjson на файловую систему
                            return vfs.makeDir(dirname)
                                .then(function () {
                                    return vow.all([
                                        vfs.exists(examplesBlocksPath),
                                        vfs.exists(examplesBlocksSymlink)
                                    ]).spread(function (blocksExists, symlinkExists) {
                                        if (blocksExists && !symlinkExists) {
                                            return vfs.symLink(examplesBlocksPath, examplesBlocksSymlink);
                                        }
                                    });
                                })
                                .then(function () {
                                    return vfs.write(bemjsonFilename, bemjson, 'utf-8');
                                })
                                .then(function () {
                                    cache.set('inline-example', bemjsonFilename, true);

                                    magic.registerNode(example.path);

                                    return example;
                                });
                        }));
                    })
                    .then(function (examples) {
                        channel.emit('inline-examples', options.destPath, examples.filter(function (example) {
                            return example;
                        }));
                    });
            });
        },

        _provideTargets: function (options) {
            helper.configure(function (config, nodes, targets) {
                var targetNodes = {};

                targets && targets.length && targets.forEach(function (target) {
                    targetNodes[path.dirname(target)] = true;
                });

                var setNodes = [].concat(nodes, Object.keys(targetNodes)).filter(function (node) {
                    return node.indexOf(options.destPath) === 0;
                });

                // Провайдим полученные `bemjson.js` файлы
                config.nodes(setNodes, function (nodeConfig) {
                    var node = path.basename(nodeConfig.getNodePath()),
                        symlinkTarget = node + '.bemjson.js.symlink',
                        symlinkFilename = nodeConfig.resolvePath(symlinkTarget);

                    // Проверяем инлайновый ли это пример
                    if (!fs.existsSync(symlinkFilename)) {
                        nodeConfig.addTech([require('enb/techs/file-provider'), { target: '?.bemjson.js' }]);
                        nodeConfig.addTarget('?.bemjson.js');
                    }
                });
            });
        }
    };
};

function buildBemjson(example, bemjsonFilename, callback, logger) {
    var meta = {
            filename: bemjsonFilename,
            notation: example.notation
        },
        bemjson;

    try {
        bemjson = vm.runInNewContext('(' + example.source + ')', {});
    } catch (err) {
        logger.logWarningAction('inline-bemjson', example.sourcePath, err.name + ': ' + err.message);

        return;
    }

    return '(' + JSON.stringify(callback(bemjson, meta)) + ')';
}

function getExamplesFromMdFiles(root, dstpath, filenames) {
    return vow.all(filenames.map(function (filename) {
            return getExamplesFromMdFile(root, dstpath, filename);
        }))
        .then(function (list) {
            return Array.prototype.concat.apply([], list);
        });
}

function getMdFiles(levels) {
    return scanner.scan(levels)
        .then(function (files) {
            var filenames = [];

            files.forEach(function (file) {
                var ext = path.extname(file.fullname);

                if (ext === '.md') {
                    filenames.push(file.fullname);
                }
            });

            return filenames;
        });
}

function getExamplesFromMdFile(root, dstpath, filename) {
    return vfs.read(filename, 'utf-8')
        .then(function (source) {
            var basename = path.basename(filename),
                blockname = basename.split('.')[0],
                notation = naming.parse(blockname),
                scope = path.join(dstpath, notation.block),
                examplesDir = path.resolve(filename, '..', blockname + '.examples'),
                examples = getExamplesFromSource(source);

            return examples.map(function (example) {
                example.sourcePath = path.relative(root, filename);
                example.path = path.join(scope, example.name);
                example.notation = notation;
                example.examplesDir = examplesDir;
                example.blockName = blockname;

                return example;
            });
        });
}

function getExamplesFromSource(source) {
    var matched = source.match(BEMJSON_JS_CODE_REGEX);

    return matched ? matched.map(function (str) {
        var code = str.split(/\r?\n/).slice(1);

        code.pop();
        code = code.join(EOL);

        var shasum = crypto.createHash('sha1'); shasum.update(code);
        var base64 = fixBase64(shasum.digest('base64'));

        return {
            name: base64,
            source: code
        };
    }) : [];
}

function fixBase64(base64) {
    return base64
        .replace(/\+/g, '-')
        .replace(/\//g, '_')
        .replace(/=/g, '')
        .replace(/^[+-]+/g, '');
}
<|endoftext|>


<commit_before>CouchDBModel = Ember.Mixin.create({
  rev: DS.attr('string')
});

DS.CouchDBAdapter = DS.Adapter.extend({
  typeAttribute: 'ember_type',

  _ajax: Ember.K,

  ajax: function(url, type, hash) {
    var db = this.get('db');
    return this._ajax('/%@/%@'.fmt(db, url), type, hash);
  },

  stringForType: function(type) {
    return type.toString();
  },

  addTypeProperty: function(json, type) {
    var typeAttribute = this.get('typeAttribute');
    json[typeAttribute] = this.stringForType(type);
  },

  find: function(store, type, id) {
    this.ajax(id, 'GET', {
      data: {},
      context: this,
      success: function(data) {
        store.load(type, data);
      }
    });
  },

  findMany: function(store, type, ids) {
    this.ajax('_all_docs?include_docs=true', 'POST', {
      data: { keys: ids },
      context: this,
      success: function(data) {
        store.loadMany(type, data.rows.getEach('doc'));
      }
    });
  },

  findQuery: function(store, type, query, modelArray) {
    var designDoc = this.get('designDoc');
    if (query.type === 'view') {
      this.ajax('_design/%@/_view/%@'.fmt(query.designDoc || designDoc, query.viewName), 'GET', {});
    }
  },

  findAll: Ember.K,

  createRecord: function(store, type, record) {
    var json = record.toJSON();
    this.addTypeProperty(json, type);
    delete json.rev;
    this.ajax('', 'POST', {
      data: json,
      context: this,
      success: function(data) {
        store.didCreateRecord(record, $.extend(json, data));
      }
    });
  },

  updateRecord: function(store, type, record) {
    var json = record.toJSON();
    this.addTypeProperty(json, type);
    json._id = json.id;
    json._rev = json.rev;
    delete json.id;
    delete json.rev;
    this.ajax(json._id, 'PUT', {
      data: json,
      context: this,
      success: function(data) {
        store.didUpdateRecord(record, $.extend(json, data));
      }
    });
  },

  deleteRecord: function(store, type, record) {
    this.ajax(record.get('id') + '?rev=' + record.get('rev'), 'DELETE', {
      context: this,
      success: function(data) {
        store.didDeleteRecord(record);
      }
    });
  }
});<commit_msg>Remove unused data property from hash when finding a record<commit_after>CouchDBModel = Ember.Mixin.create({
  rev: DS.attr('string')
});

DS.CouchDBAdapter = DS.Adapter.extend({
  typeAttribute: 'ember_type',

  _ajax: Ember.K,

  ajax: function(url, type, hash) {
    var db = this.get('db');
    return this._ajax('/%@/%@'.fmt(db, url), type, hash);
  },

  stringForType: function(type) {
    return type.toString();
  },

  addTypeProperty: function(json, type) {
    var typeAttribute = this.get('typeAttribute');
    json[typeAttribute] = this.stringForType(type);
  },

  find: function(store, type, id) {
    this.ajax(id, 'GET', {
      context: this,
      success: function(data) {
        store.load(type, data);
      }
    });
  },

  findMany: function(store, type, ids) {
    this.ajax('_all_docs?include_docs=true', 'POST', {
      data: { keys: ids },
      context: this,
      success: function(data) {
        store.loadMany(type, data.rows.getEach('doc'));
      }
    });
  },

  findQuery: function(store, type, query, modelArray) {
    var designDoc = this.get('designDoc');
    if (query.type === 'view') {
      this.ajax('_design/%@/_view/%@'.fmt(query.designDoc || designDoc, query.viewName), 'GET', {});
    }
  },

  findAll: Ember.K,

  createRecord: function(store, type, record) {
    var json = record.toJSON();
    this.addTypeProperty(json, type);
    delete json.rev;
    this.ajax('', 'POST', {
      data: json,
      context: this,
      success: function(data) {
        store.didCreateRecord(record, $.extend(json, data));
      }
    });
  },

  updateRecord: function(store, type, record) {
    var json = record.toJSON();
    this.addTypeProperty(json, type);
    json._id = json.id;
    json._rev = json.rev;
    delete json.id;
    delete json.rev;
    this.ajax(json._id, 'PUT', {
      data: json,
      context: this,
      success: function(data) {
        store.didUpdateRecord(record, $.extend(json, data));
      }
    });
  },

  deleteRecord: function(store, type, record) {
    this.ajax(record.get('id') + '?rev=' + record.get('rev'), 'DELETE', {
      context: this,
      success: function(data) {
        store.didDeleteRecord(record);
      }
    });
  }
});<|endoftext|>


<commit_before>import { mapBy, not } from '@ember/object/computed';
import Evented from '@ember/object/evented';
import ArrayProxy from '@ember/array/proxy';
import { set, get, computed } from '@ember/object';
import { isEmpty } from '@ember/utils';
import { makeArray, A } from '@ember/array';
import MapWithDefault from '@ember/map/with-default';
import { deprecate, warn } from '@ember/debug';

/**
@module ember-data
*/

/**
  Holds validation errors for a given record, organized by attribute names.

  Every `DS.Model` has an `errors` property that is an instance of
  `DS.Errors`. This can be used to display validation error
  messages returned from the server when a `record.save()` rejects.

  For Example, if you had a `User` model that looked like this:

  ```app/models/user.js
  import DS from 'ember-data';

  export default DS.Model.extend({
    username: DS.attr('string'),
    email: DS.attr('string')
  });
  ```
  And you attempted to save a record that did not validate on the backend:

  ```javascript
  let user = store.createRecord('user', {
    username: 'tomster',
    email: 'invalidEmail'
  });
  user.save();
  ```

  Your backend would be expected to return an error response that described
  the problem, so that error messages can be generated on the app.

  API responses will be translated into instances of `DS.Errors` differently,
  depending on the specific combination of adapter and serializer used. You
  may want to check the documentation or the source code of the libraries
  that you are using, to know how they expect errors to be communicated.

  Errors can be displayed to the user by accessing their property name
  to get an array of all the error objects for that property. Each
  error object is a JavaScript object with two keys:

  - `message` A string containing the error message from the backend
  - `attribute` The name of the property associated with this error message

  ```handlebars
  <label>Username: {{input value=username}} </label>
  {{#each model.errors.username as |error|}}
    <div class="error">
      {{error.message}}
    </div>
  {{/each}}

  <label>Email: {{input value=email}} </label>
  {{#each model.errors.email as |error|}}
    <div class="error">
      {{error.message}}
    </div>
  {{/each}}
  ```

  You can also access the special `messages` property on the error
  object to get an array of all the error strings.

  ```handlebars
  {{#each model.errors.messages as |message|}}
    <div class="error">
      {{message}}
    </div>
  {{/each}}
  ```

  @class Errors
  @namespace DS
  @extends Ember.Object
  @uses Ember.Enumerable
  @uses Ember.Evented
 */
export default ArrayProxy.extend(Evented, {
  /**
    Register with target handler

    @method registerHandlers
    @param {Object} target
    @param {Function} becameInvalid
    @param {Function} becameValid
    @deprecated
  */
  registerHandlers(target, becameInvalid, becameValid) {
    deprecate(
      `Record errors will no longer be evented.`, false, {
        id: 'ds.errors.registerHandlers',
        until: '3.0.0'
      });

    this._registerHandlers(target, becameInvalid, becameValid);
  },

  /**
    Register with target handler

    @method _registerHandlers
    @private
  */
  _registerHandlers(target, becameInvalid, becameValid) {
    this.on('becameInvalid', target, becameInvalid);
    this.on('becameValid', target, becameValid);
  },

  /**
    @property errorsByAttributeName
    @type {Ember.MapWithDefault}
    @private
  */
  errorsByAttributeName: computed(function() {
    return MapWithDefault.create({
      defaultValue() {
        return A();
      }
    });
  }),

  /**
    Returns errors for a given attribute

    ```javascript
    let user = store.createRecord('user', {
      username: 'tomster',
      email: 'invalidEmail'
    });
    user.save().catch(function(){
      user.get('errors').errorsFor('email'); // returns:
      // [{attribute: "email", message: "Doesn't look like a valid email."}]
    });
    ```

    @method errorsFor
    @param {String} attribute
    @return {Array}
  */
  errorsFor(attribute) {
    return get(this, 'errorsByAttributeName').get(attribute);
  },

  /**
    An array containing all of the error messages for this
    record. This is useful for displaying all errors to the user.

    ```handlebars
    {{#each model.errors.messages as |message|}}
      <div class="error">
        {{message}}
      </div>
    {{/each}}
    ```

    @property messages
    @type {Array}
  */
  messages: mapBy('content', 'message'),

  /**
    @property content
    @type {Array}
    @private
  */
  content: computed(function() {
    return A();
  }),

  /**
    @method unknownProperty
    @private
  */
  unknownProperty(attribute) {
    let errors = this.errorsFor(attribute);
    if (isEmpty(errors)) { return null; }
    return errors;
  },

  /**
    Total number of errors.

    @property length
    @type {Number}
    @readOnly
  */

  /**
    @property isEmpty
    @type {Boolean}
    @readOnly
  */
  isEmpty: not('length').readOnly(),

  /**
    Adds error messages to a given attribute and sends
    `becameInvalid` event to the record.

    Example:

    ```javascript
    if (!user.get('username') {
      user.get('errors').add('username', 'This field is required');
    }
    ```

    @method add
    @param {String} attribute
    @param {(Array|String)} messages
    @deprecated
  */
  add(attribute, messages) {
    warn(`Interacting with a record errors object will no longer change the record state.`, false, {
      id: 'ds.errors.add'
    });

    let wasEmpty = get(this, 'isEmpty');

    this._add(attribute, messages);

    if (wasEmpty && !get(this, 'isEmpty')) {
      this.trigger('becameInvalid');
    }
  },

  /**
    Adds error messages to a given attribute without sending event.

    @method _add
    @private
  */
  _add(attribute, messages) {
    messages = this._findOrCreateMessages(attribute, messages);
    this.addObjects(messages);
    get(this, 'errorsByAttributeName').get(attribute).addObjects(messages);

    this.notifyPropertyChange(attribute);
  },

  /**
    @method _findOrCreateMessages
    @private
  */
  _findOrCreateMessages(attribute, messages) {
    let errors = this.errorsFor(attribute);
    let messagesArray = makeArray(messages);
    let _messages = new Array(messagesArray.length);

    for (let i = 0; i < messagesArray.length; i++) {
      let message = messagesArray[i];
      let err = errors.findBy('message', message);
      if (err) {
        _messages[i] = err;
      } else {
        _messages[i] = {
          attribute: attribute,
          message: message
        };
      }
    }

    return _messages;
  },

  /**
    Removes all error messages from the given attribute and sends
    `becameValid` event to the record if there no more errors left.

    Example:

    ```app/models/user.js
    import DS from 'ember-data';

    export default DS.Model.extend({
      email: DS.attr('string'),
      twoFactorAuth: DS.attr('boolean'),
      phone: DS.attr('string')
    });
    ```

    ```app/routes/user/edit.js
    import Route from '@ember/routing/route';

    export default Route.extend({
      actions: {
        save: function(user) {
          if (!user.get('twoFactorAuth')) {
            user.get('errors').remove('phone');
          }
          user.save();
        }
      }
    });
    ```

    @method remove
    @param {String} attribute
    @deprecated
  */
  remove(attribute) {
    warn(`Interacting with a record errors object will no longer change the record state.`, false, {
      id: 'ds.errors.remove'
    });

    if (get(this, 'isEmpty')) { return; }

    this._remove(attribute);

    if (get(this, 'isEmpty')) {
      this.trigger('becameValid');
    }
  },

  /**
    Removes all error messages from the given attribute without sending event.

    @method _remove
    @private
  */
  _remove(attribute) {
    if (get(this, 'isEmpty')) { return; }

    let content = this.rejectBy('attribute', attribute);
    set(this, 'content', content);
    get(this, 'errorsByAttributeName').delete(attribute);

    this.notifyPropertyChange(attribute);
    this.notifyPropertyChange('length');
  },

  /**
    Removes all error messages and sends `becameValid` event
    to the record.

    Example:

    ```app/routes/user/edit.js
  import Route from '@ember/routing/route';

    export default Route.extend({
      actions: {
        retrySave: function(user) {
          user.get('errors').clear();
          user.save();
        }
      }
    });
    ```

    @method clear
    @deprecated
  */
  clear() {
    warn(`Interacting with a record errors object will no longer change the record state.`, false, {
      id: 'ds.errors.clear'
    });

    if (get(this, 'isEmpty')) { return; }

    this._clear();
    this.trigger('becameValid');
  },

  /**
    Removes all error messages.
    to the record.

    @method _clear
    @private
  */
  _clear() {
    if (get(this, 'isEmpty')) { return; }

    let errorsByAttributeName = get(this, 'errorsByAttributeName');
    let attributes = A();

    errorsByAttributeName.forEach(function(_, attribute) {
      attributes.push(attribute);
    });

    errorsByAttributeName.clear();
    attributes.forEach(function(attribute) {
      this.notifyPropertyChange(attribute);
    }, this);

    ArrayProxy.prototype.clear.call(this);
  },

  /**
    Checks if there is error messages for the given attribute.

    ```app/routes/user/edit.js
    import Route from '@ember/routing/route';

    export default Route.extend({
      actions: {
        save: function(user) {
          if (user.get('errors').has('email')) {
            return alert('Please update your email before attempting to save.');
          }
          user.save();
        }
      }
    });
    ```

    @method has
    @param {String} attribute
    @return {Boolean} true if there some errors on given attribute
  */
  has(attribute) {
    return !isEmpty(this.errorsFor(attribute));
  }
});
<commit_msg>[BUGFIX beta] Update DS.Errors#unknownProperty to return `undefined`.<commit_after>import { mapBy, not } from '@ember/object/computed';
import Evented from '@ember/object/evented';
import ArrayProxy from '@ember/array/proxy';
import { set, get, computed } from '@ember/object';
import { isEmpty } from '@ember/utils';
import { makeArray, A } from '@ember/array';
import MapWithDefault from '@ember/map/with-default';
import { deprecate, warn } from '@ember/debug';

/**
@module ember-data
*/

/**
  Holds validation errors for a given record, organized by attribute names.

  Every `DS.Model` has an `errors` property that is an instance of
  `DS.Errors`. This can be used to display validation error
  messages returned from the server when a `record.save()` rejects.

  For Example, if you had a `User` model that looked like this:

  ```app/models/user.js
  import DS from 'ember-data';

  export default DS.Model.extend({
    username: DS.attr('string'),
    email: DS.attr('string')
  });
  ```
  And you attempted to save a record that did not validate on the backend:

  ```javascript
  let user = store.createRecord('user', {
    username: 'tomster',
    email: 'invalidEmail'
  });
  user.save();
  ```

  Your backend would be expected to return an error response that described
  the problem, so that error messages can be generated on the app.

  API responses will be translated into instances of `DS.Errors` differently,
  depending on the specific combination of adapter and serializer used. You
  may want to check the documentation or the source code of the libraries
  that you are using, to know how they expect errors to be communicated.

  Errors can be displayed to the user by accessing their property name
  to get an array of all the error objects for that property. Each
  error object is a JavaScript object with two keys:

  - `message` A string containing the error message from the backend
  - `attribute` The name of the property associated with this error message

  ```handlebars
  <label>Username: {{input value=username}} </label>
  {{#each model.errors.username as |error|}}
    <div class="error">
      {{error.message}}
    </div>
  {{/each}}

  <label>Email: {{input value=email}} </label>
  {{#each model.errors.email as |error|}}
    <div class="error">
      {{error.message}}
    </div>
  {{/each}}
  ```

  You can also access the special `messages` property on the error
  object to get an array of all the error strings.

  ```handlebars
  {{#each model.errors.messages as |message|}}
    <div class="error">
      {{message}}
    </div>
  {{/each}}
  ```

  @class Errors
  @namespace DS
  @extends Ember.Object
  @uses Ember.Enumerable
  @uses Ember.Evented
 */
export default ArrayProxy.extend(Evented, {
  /**
    Register with target handler

    @method registerHandlers
    @param {Object} target
    @param {Function} becameInvalid
    @param {Function} becameValid
    @deprecated
  */
  registerHandlers(target, becameInvalid, becameValid) {
    deprecate(
      `Record errors will no longer be evented.`, false, {
        id: 'ds.errors.registerHandlers',
        until: '3.0.0'
      });

    this._registerHandlers(target, becameInvalid, becameValid);
  },

  /**
    Register with target handler

    @method _registerHandlers
    @private
  */
  _registerHandlers(target, becameInvalid, becameValid) {
    this.on('becameInvalid', target, becameInvalid);
    this.on('becameValid', target, becameValid);
  },

  /**
    @property errorsByAttributeName
    @type {Ember.MapWithDefault}
    @private
  */
  errorsByAttributeName: computed(function() {
    return MapWithDefault.create({
      defaultValue() {
        return A();
      }
    });
  }),

  /**
    Returns errors for a given attribute

    ```javascript
    let user = store.createRecord('user', {
      username: 'tomster',
      email: 'invalidEmail'
    });
    user.save().catch(function(){
      user.get('errors').errorsFor('email'); // returns:
      // [{attribute: "email", message: "Doesn't look like a valid email."}]
    });
    ```

    @method errorsFor
    @param {String} attribute
    @return {Array}
  */
  errorsFor(attribute) {
    return get(this, 'errorsByAttributeName').get(attribute);
  },

  /**
    An array containing all of the error messages for this
    record. This is useful for displaying all errors to the user.

    ```handlebars
    {{#each model.errors.messages as |message|}}
      <div class="error">
        {{message}}
      </div>
    {{/each}}
    ```

    @property messages
    @type {Array}
  */
  messages: mapBy('content', 'message'),

  /**
    @property content
    @type {Array}
    @private
  */
  content: computed(function() {
    return A();
  }),

  /**
    @method unknownProperty
    @private
  */
  unknownProperty(attribute) {
    let errors = this.errorsFor(attribute);
    if (isEmpty(errors)) { return undefined; }
    return errors;
  },

  /**
    Total number of errors.

    @property length
    @type {Number}
    @readOnly
  */

  /**
    @property isEmpty
    @type {Boolean}
    @readOnly
  */
  isEmpty: not('length').readOnly(),

  /**
    Adds error messages to a given attribute and sends
    `becameInvalid` event to the record.

    Example:

    ```javascript
    if (!user.get('username') {
      user.get('errors').add('username', 'This field is required');
    }
    ```

    @method add
    @param {String} attribute
    @param {(Array|String)} messages
    @deprecated
  */
  add(attribute, messages) {
    warn(`Interacting with a record errors object will no longer change the record state.`, false, {
      id: 'ds.errors.add'
    });

    let wasEmpty = get(this, 'isEmpty');

    this._add(attribute, messages);

    if (wasEmpty && !get(this, 'isEmpty')) {
      this.trigger('becameInvalid');
    }
  },

  /**
    Adds error messages to a given attribute without sending event.

    @method _add
    @private
  */
  _add(attribute, messages) {
    messages = this._findOrCreateMessages(attribute, messages);
    this.addObjects(messages);
    get(this, 'errorsByAttributeName').get(attribute).addObjects(messages);

    this.notifyPropertyChange(attribute);
  },

  /**
    @method _findOrCreateMessages
    @private
  */
  _findOrCreateMessages(attribute, messages) {
    let errors = this.errorsFor(attribute);
    let messagesArray = makeArray(messages);
    let _messages = new Array(messagesArray.length);

    for (let i = 0; i < messagesArray.length; i++) {
      let message = messagesArray[i];
      let err = errors.findBy('message', message);
      if (err) {
        _messages[i] = err;
      } else {
        _messages[i] = {
          attribute: attribute,
          message: message
        };
      }
    }

    return _messages;
  },

  /**
    Removes all error messages from the given attribute and sends
    `becameValid` event to the record if there no more errors left.

    Example:

    ```app/models/user.js
    import DS from 'ember-data';

    export default DS.Model.extend({
      email: DS.attr('string'),
      twoFactorAuth: DS.attr('boolean'),
      phone: DS.attr('string')
    });
    ```

    ```app/routes/user/edit.js
    import Route from '@ember/routing/route';

    export default Route.extend({
      actions: {
        save: function(user) {
          if (!user.get('twoFactorAuth')) {
            user.get('errors').remove('phone');
          }
          user.save();
        }
      }
    });
    ```

    @method remove
    @param {String} attribute
    @deprecated
  */
  remove(attribute) {
    warn(`Interacting with a record errors object will no longer change the record state.`, false, {
      id: 'ds.errors.remove'
    });

    if (get(this, 'isEmpty')) { return; }

    this._remove(attribute);

    if (get(this, 'isEmpty')) {
      this.trigger('becameValid');
    }
  },

  /**
    Removes all error messages from the given attribute without sending event.

    @method _remove
    @private
  */
  _remove(attribute) {
    if (get(this, 'isEmpty')) { return; }

    let content = this.rejectBy('attribute', attribute);
    set(this, 'content', content);
    get(this, 'errorsByAttributeName').delete(attribute);

    this.notifyPropertyChange(attribute);
    this.notifyPropertyChange('length');
  },

  /**
    Removes all error messages and sends `becameValid` event
    to the record.

    Example:

    ```app/routes/user/edit.js
  import Route from '@ember/routing/route';

    export default Route.extend({
      actions: {
        retrySave: function(user) {
          user.get('errors').clear();
          user.save();
        }
      }
    });
    ```

    @method clear
    @deprecated
  */
  clear() {
    warn(`Interacting with a record errors object will no longer change the record state.`, false, {
      id: 'ds.errors.clear'
    });

    if (get(this, 'isEmpty')) { return; }

    this._clear();
    this.trigger('becameValid');
  },

  /**
    Removes all error messages.
    to the record.

    @method _clear
    @private
  */
  _clear() {
    if (get(this, 'isEmpty')) { return; }

    let errorsByAttributeName = get(this, 'errorsByAttributeName');
    let attributes = A();

    errorsByAttributeName.forEach(function(_, attribute) {
      attributes.push(attribute);
    });

    errorsByAttributeName.clear();
    attributes.forEach(function(attribute) {
      this.notifyPropertyChange(attribute);
    }, this);

    ArrayProxy.prototype.clear.call(this);
  },

  /**
    Checks if there is error messages for the given attribute.

    ```app/routes/user/edit.js
    import Route from '@ember/routing/route';

    export default Route.extend({
      actions: {
        save: function(user) {
          if (user.get('errors').has('email')) {
            return alert('Please update your email before attempting to save.');
          }
          user.save();
        }
      }
    });
    ```

    @method has
    @param {String} attribute
    @return {Boolean} true if there some errors on given attribute
  */
  has(attribute) {
    return !isEmpty(this.errorsFor(attribute));
  }
});
<|endoftext|>


<commit_before>import Ember from 'ember';

var _addBrace = function(element, plusId, numberSpaces, hasPlus, braceColor, braceHighlight){
    var jsonLine = {
            elements: [],
            lines: []
        },
        jsonObj = {};

    for(var counter = 0; counter < numberSpaces; counter = counter +1){
        jsonLine.elements.push(_addBlank());
    }

    jsonObj.hasPlus = hasPlus;
    jsonObj.plusId = 'plus_' + plusId;
    jsonObj.isBlank = false;
    jsonObj.element = element;
    jsonObj.style = 'color:' + braceColor + '; background-color:' + braceHighlight;
    jsonObj.class = 'json-brace';

    jsonLine.elements.push(jsonObj);

    return jsonLine;
};

var _addBlank = function(){
    var jsonObj = {};

    jsonObj.hasPlus = false;
    jsonObj.plusId = null;
    jsonObj.element = '';
    jsonObj.isBlank = true;
    jsonObj.style = '';
    jsonObj.class = 'json-blank';

    return jsonObj;
};

var _addKey = function(key, plusId, numberSpaces, keyColor, keyHighlight){
    var jsonObj = {};

    jsonObj.hasPlus = false;
    jsonObj.plusId = null;
    jsonObj.isBlank = false;
    jsonObj.element = key;
    jsonObj.style = 'color:' + keyColor + '; background-color:' + keyHighlight;
    jsonObj.class = 'json-key';

    return jsonObj;
};

var _addTwoPoints = function(){
    var jsonObj = {};

    jsonObj.hasPlus = false;
    jsonObj.plusId = null;
    jsonObj.isBlank = false;
    jsonObj.element = ':';
    jsonObj.style = '';
    jsonObj.class = 'json-two-points';

    return jsonObj;
};

var _addValue = function(value, plusId, numberSpaces, keyColor, keyHighlight, valueColor, valueHighlight, stringColor, stringHighlight, braceColor, braceHighlight, bracketColor, bracketHighlight, hasComma){
    var jsonObj = {};

    if(value){
        if(typeof value === 'string'){
            jsonObj = _addStringValue(value, stringColor, stringHighlight);
        }
        else if(Array.isArray(value)){
            if(typeof value[0] === 'object'){

                jsonObj = _createJSONTree(value, numberSpaces, plusId, keyColor, keyHighlight, valueColor, valueHighlight, stringColor, stringHighlight, braceColor, braceHighlight, bracketColor, bracketHighlight, hasComma);
            }
            else{
                jsonObj = _addArrayStandardValues(value);
            }
        }
        else if(value && typeof value === 'object'){
            jsonObj = _addObjectValue(value, plusId, numberSpaces, keyColor, keyHighlight, valueColor, valueHighlight, stringColor, stringHighlight, braceColor, braceHighlight, bracketColor, bracketHighlight);
        }
        else{
            jsonObj = _addStandardValue(value, valueColor, valueHighlight);
        }
    }
    else{
        jsonObj = _addStringValue('null', stringColor, stringHighlight);
    }

    return jsonObj;
};

var _addStringValue = function(value, stringColor, stringHighlight){
    var jsonObj = {};

    jsonObj.hasPlus = false;
    jsonObj.plusId = null;
    jsonObj.isBlank = false;
    jsonObj.element = value;
    jsonObj.style = 'color:' + stringColor + '; background-color:' + stringHighlight;
    jsonObj.class = 'json-string';

    return jsonObj;
};

var _addObjectValue = function(value, plusId, numberSpacesInitial, keyColor, keyHighlight, valueColor, valueHighlight, stringColor, stringHighlight, braceColor, braceHighlight, bracketColor, bracketHighlight){
    return _createObject(value, plusId, numberSpacesInitial, keyColor, keyHighlight, valueColor, valueHighlight, stringColor, stringHighlight, braceColor, braceHighlight, bracketColor, bracketHighlight);
};

var _addStandardValue = function(value, valueColor, valueHighlight){
    var jsonObj = {};

    jsonObj.hasPlus = false;
    jsonObj.plusId = null;
    jsonObj.isBlank = false;
    jsonObj.element = value;
    jsonObj.style = 'color:' + valueColor + '; background-color:' + valueHighlight;
    jsonObj.class = 'json-value';

    return jsonObj;
};

var _addArrayStandardValues = function(value){
    var jsonObj = {},
        arrayValue = '[';

    value.forEach(function(arrayItem){
        arrayValue = arrayValue + arrayItem + ', ';
    });

    arrayValue = arrayValue.substr(0, arrayValue.length - 2) + ']';

    jsonObj.hasPlus = false;
    jsonObj.plusId = null;
    jsonObj.isBlank = false;
    jsonObj.element = arrayValue;
    jsonObj.style = '';
    jsonObj.class = '';

    return jsonObj;
};

var _addComma = function(){
    var jsonObj = {};

    jsonObj.hasPlus = false;
    jsonObj.plusId = null;
    jsonObj.isBlank = false;
    jsonObj.element = ',';
    jsonObj.style = '';
    jsonObj.class = '';

    return jsonObj;
};

var _createObject = function(obj, plusId, numberSpacesInitial, keyColor, keyHighlight, valueColor, valueHighlight, stringColor, stringHighlight, braceColor, braceHighlight, bracketColor, bracketHighlight) {
    var jsonLines = [],
        jsonLine = {
            elements: [],
            lines: []
        },
        jsonObj = {},
        numberSpaces = numberSpacesInitial;

    jsonLine = _addBrace('{', plusId, numberSpacesInitial, true, braceColor, braceHighlight);

    numberSpaces = numberSpaces + 1;
    Object.keys(obj).forEach(function(key, index){
        var hasComma = false,
            internalJsonLine = {
                elements: [],
                lines: []
            },
            newValue;

        plusId = plusId + 1;

        for(var counter = 0; counter < numberSpaces; counter = counter +1){
            internalJsonLine.elements.push(_addBlank());
        }

        internalJsonLine
            .elements
            .push(_addKey(key, plusId, numberSpaces, keyColor, keyHighlight));
        internalJsonLine
            .elements
            .push(_addTwoPoints());

        hasComma = index < (Object.keys(obj).length -1);

        newValue = _addValue(obj[key], plusId, numberSpaces, keyColor, keyHighlight, valueColor, valueHighlight, stringColor, stringHighlight, braceColor, braceHighlight, bracketColor, bracketHighlight, hasComma);

        if(Array.isArray(newValue) && newValue[0].elements[newValue[0].elements.length - 1].class === 'json-brace'){
            var openBrace = newValue[0].elements[newValue[0].elements.length - 1],
                closeBrace = newValue[1].elements[newValue[0].elements.length - 1];

            internalJsonLine
                .elements
                .push(openBrace);

            internalJsonLine.lines = internalJsonLine
                                        .lines
                                        .concat(newValue[0].lines);

            jsonLine.lines.push(internalJsonLine);

            internalJsonLine = {
                elements: [],
                lines: []
            };

            newValue[1].elements.forEach(function (element) {
                internalJsonLine
                    .elements
                    .push(element);
            });
        }
        else{
            internalJsonLine
                .elements
                .push(newValue);
        }

        jsonLine.lines.push(internalJsonLine);

        if(Array.isArray(internalJsonLine.elements[internalJsonLine.elements.length - 1])){

            var internalArray = internalJsonLine
                                    .elements[internalJsonLine
                                                .elements.length - 1];
            internalJsonLine.elements.pop();

            internalJsonLine.elements.push(internalArray[0].elements[0]);

            internalJsonLine.lines = internalArray[0].lines;

            jsonLine.lines.push(internalArray[internalArray.length - 1]);
        }
        else{
            if(hasComma && internalJsonLine.elements[internalJsonLine.elements.length - 1].element !== ','){
                internalJsonLine.elements.push(_addComma());
            }
        }
    });

    jsonLines.push(jsonLine);
    jsonLines.push(_addBrace('}', plusId, numberSpacesInitial, false, braceColor, braceHighlight));

    return jsonLines;
};

var _adjustLinePlusId = function(jsonLines){
    jsonLines.forEach(function(_line){
        var plusId = '';

        if(_line.elements){
            _line.elements.forEach(function(_element){
                if(_element.plusId){
                    plusId = _element.plusId;
                }
            });
        }

        if(_line.lines){
            _line.lines.forEach(function(_internalLine){
                _internalLine.plusId = plusId;
            });

            if(_line.lines.length > 0){
                _line.lines = _adjustLinePlusId(_line.lines);
            }
        }
    });
    return jsonLines;
};

var _createJSONTree = function(obj, numberSpaces, plusId, keyColor, keyHighlight, valueColor, valueHighlight, stringColor, stringHighlight, braceColor, braceHighlight, bracketColor, bracketHighlight, hasComma) {
    var jsonLines = [],
        jsonLine = {
            elements: [],
            lines: []
        },
        jsonObj = {
            'hasPlus': false,
            'newLine': false,
            'endLine': false
        },
        numberSpacesInitial = numberSpaces;

    if(Array.isArray(obj)){
        numberSpaces = numberSpaces + 1;

        jsonObj.hasPlus = true;
        jsonObj.plusId = 'plus_' + plusId;
        jsonObj.element = '[';
        jsonObj.isBlank = false;
        jsonObj.style = 'color:' + bracketColor + '; background-color:' + bracketHighlight;
        jsonObj.class = 'json-bracket';

        jsonLine.elements.push(jsonObj);

        obj.forEach(function(newObj, index){
            var newObject;

            plusId = plusId + 1;

            newObject = _createObject(newObj, plusId, numberSpaces, keyColor, keyHighlight, valueColor, valueHighlight, stringColor, stringHighlight, braceColor, braceHighlight, bracketColor, bracketHighlight);

            newObject.forEach(function(objectLine){
                jsonLine.lines.push(objectLine);
            });

            if(index < (obj.length -1)){
                jsonLine
                    .lines[jsonLine.lines.length - 1]
                        .elements.push(_addComma());
            }

            newObject.forEach(function(line){
                plusId = plusId + line.lines.length + 1;
            });
        });

        jsonLines.push(jsonLine);

        jsonObj = {
            'hasPlus': false,
            'newLine': false,
            'endLine': false
        };
        jsonLine = {
            elements: [],
            lines: []
        };

        for(var counter = 0; counter < numberSpacesInitial; counter = counter +1){
            jsonLine.elements.push(_addBlank());
        }

        jsonObj.hasPlus = false;
        jsonObj.plusId = null;
        jsonObj.element = ']';
        jsonObj.isBlank = false;
        jsonObj.style = 'color:' + bracketColor + '; background-color:' + bracketHighlight;
        jsonObj.class = 'json-bracket';

        jsonLine.elements.push(jsonObj);

        if(hasComma){
            jsonLine.elements.push(_addComma());
        }

        jsonLines.push(jsonLine);
    }
    else{
        jsonLines = _createObject(obj, plusId, numberSpaces, keyColor, keyHighlight, valueColor, valueHighlight, stringColor, stringHighlight, braceColor, braceHighlight, bracketColor, bracketHighlight);
    }

    return _adjustLinePlusId(jsonLines);
};

var _prettyPrint = function(    obj,
                                keyColor, keyHighlight,
                                valueColor, valueHighlight,
                                stringColor, stringHighlight,
                                braceColor, braceHighlight,
                                bracketColor, bracketHighlight) {

    var jsonTree = _createJSONTree( obj, 0, 0,
                                    keyColor, keyHighlight,
                                    valueColor, valueHighlight,
                                    stringColor, stringHighlight,
                                    braceColor, braceHighlight,
                                    bracketColor, bracketHighlight);

    return jsonTree;
};

export default Ember.Component.extend({
    tagName: 'code',
    optionsDefault: {
        keyColor: '#A52A2A',
        keyHighlight: '#FFFFFF',
        valueColor: '#000080',
        valueHighlight: '#FFFFFF',
        stringColor: '#C0FF3E',
        stringHighlight: '#FFFFFF',
        braceColor: '#000000',
        braceHighlight: '#FFFFFF',
        bracketColor: '#000000',
        bracketHighlight: '#FFFFFF'
    },
    options: {},
    pretty: function () {
        var jsonObj = this.get('jsonObj'),
            options,
            json_pretty;

        if(this.options){
            options = $.extend({}, this.optionsDefault, JSON.parse(this.options));
        }
        else{
            options = this.optionsDefault;
        }

        json_pretty = _prettyPrint( jsonObj,
                                    options['keyColor'], options['keyHighlight'],
                                    options['valueColor'], options['valueHighlight'],
                                    options['stringColor'], options['stringHighlight'],
                                    options['braceColor'], options['braceHighlight'],
                                    options['bracketColor'], options['bracketHighlight']);

        return json_pretty;
    }.property('jsonObj'),
    actions: {
        toggleExpand: function(plusId){
            if(Ember.$('#' + plusId).hasClass('fa-plus-square-o')){
                Ember.$('#' + plusId).removeClass('fa-plus-square-o');
                Ember.$('#' + plusId).addClass('fa-minus-square-o');
                Ember.$('.jsonTreeView').find('[data-id="' + plusId + '"]').show();
            }
            else{
                Ember.$('#' + plusId).removeClass('fa-minus-square-o');
                Ember.$('#' + plusId).addClass('fa-plus-square-o');
                Ember.$('.jsonTreeView').find('[data-id="' + plusId + '"]').hide();
            }
        }
    }
});<commit_msg>Adjusting the join of options when build the JSON object.<commit_after>import Ember from 'ember';

var _addBrace = function(element, plusId, numberSpaces, hasPlus, braceColor, braceHighlight){
    var jsonLine = {
            elements: [],
            lines: []
        },
        jsonObj = {};

    for(var counter = 0; counter < numberSpaces; counter = counter +1){
        jsonLine.elements.push(_addBlank());
    }

    jsonObj.hasPlus = hasPlus;
    jsonObj.plusId = 'plus_' + plusId;
    jsonObj.isBlank = false;
    jsonObj.element = element;
    jsonObj.style = 'color:' + braceColor + '; background-color:' + braceHighlight;
    jsonObj.class = 'json-brace';

    jsonLine.elements.push(jsonObj);

    return jsonLine;
};

var _addBlank = function(){
    var jsonObj = {};

    jsonObj.hasPlus = false;
    jsonObj.plusId = null;
    jsonObj.element = '';
    jsonObj.isBlank = true;
    jsonObj.style = '';
    jsonObj.class = 'json-blank';

    return jsonObj;
};

var _addKey = function(key, plusId, numberSpaces, keyColor, keyHighlight){
    var jsonObj = {};

    jsonObj.hasPlus = false;
    jsonObj.plusId = null;
    jsonObj.isBlank = false;
    jsonObj.element = key;
    jsonObj.style = 'color:' + keyColor + '; background-color:' + keyHighlight;
    jsonObj.class = 'json-key';

    return jsonObj;
};

var _addTwoPoints = function(){
    var jsonObj = {};

    jsonObj.hasPlus = false;
    jsonObj.plusId = null;
    jsonObj.isBlank = false;
    jsonObj.element = ':';
    jsonObj.style = '';
    jsonObj.class = 'json-two-points';

    return jsonObj;
};

var _addValue = function(value, plusId, numberSpaces, keyColor, keyHighlight, valueColor, valueHighlight, stringColor, stringHighlight, braceColor, braceHighlight, bracketColor, bracketHighlight, hasComma){
    var jsonObj = {};

    if(value){
        if(typeof value === 'string'){
            jsonObj = _addStringValue(value, stringColor, stringHighlight);
        }
        else if(Array.isArray(value)){
            if(typeof value[0] === 'object'){

                jsonObj = _createJSONTree(value, numberSpaces, plusId, keyColor, keyHighlight, valueColor, valueHighlight, stringColor, stringHighlight, braceColor, braceHighlight, bracketColor, bracketHighlight, hasComma);
            }
            else{
                jsonObj = _addArrayStandardValues(value);
            }
        }
        else if(value && typeof value === 'object'){
            jsonObj = _addObjectValue(value, plusId, numberSpaces, keyColor, keyHighlight, valueColor, valueHighlight, stringColor, stringHighlight, braceColor, braceHighlight, bracketColor, bracketHighlight);
        }
        else{
            jsonObj = _addStandardValue(value, valueColor, valueHighlight);
        }
    }
    else{
        jsonObj = _addStringValue('null', stringColor, stringHighlight);
    }

    return jsonObj;
};

var _addStringValue = function(value, stringColor, stringHighlight){
    var jsonObj = {};

    jsonObj.hasPlus = false;
    jsonObj.plusId = null;
    jsonObj.isBlank = false;
    jsonObj.element = value;
    jsonObj.style = 'color:' + stringColor + '; background-color:' + stringHighlight;
    jsonObj.class = 'json-string';

    return jsonObj;
};

var _addObjectValue = function(value, plusId, numberSpacesInitial, keyColor, keyHighlight, valueColor, valueHighlight, stringColor, stringHighlight, braceColor, braceHighlight, bracketColor, bracketHighlight){
    return _createObject(value, plusId, numberSpacesInitial, keyColor, keyHighlight, valueColor, valueHighlight, stringColor, stringHighlight, braceColor, braceHighlight, bracketColor, bracketHighlight);
};

var _addStandardValue = function(value, valueColor, valueHighlight){
    var jsonObj = {};

    jsonObj.hasPlus = false;
    jsonObj.plusId = null;
    jsonObj.isBlank = false;
    jsonObj.element = value;
    jsonObj.style = 'color:' + valueColor + '; background-color:' + valueHighlight;
    jsonObj.class = 'json-value';

    return jsonObj;
};

var _addArrayStandardValues = function(value){
    var jsonObj = {},
        arrayValue = '[';

    value.forEach(function(arrayItem){
        arrayValue = arrayValue + arrayItem + ', ';
    });

    arrayValue = arrayValue.substr(0, arrayValue.length - 2) + ']';

    jsonObj.hasPlus = false;
    jsonObj.plusId = null;
    jsonObj.isBlank = false;
    jsonObj.element = arrayValue;
    jsonObj.style = '';
    jsonObj.class = '';

    return jsonObj;
};

var _addComma = function(){
    var jsonObj = {};

    jsonObj.hasPlus = false;
    jsonObj.plusId = null;
    jsonObj.isBlank = false;
    jsonObj.element = ',';
    jsonObj.style = '';
    jsonObj.class = '';

    return jsonObj;
};

var _createObject = function(obj, plusId, numberSpacesInitial, keyColor, keyHighlight, valueColor, valueHighlight, stringColor, stringHighlight, braceColor, braceHighlight, bracketColor, bracketHighlight) {
    var jsonLines = [],
        jsonLine = {
            elements: [],
            lines: []
        },
        jsonObj = {},
        numberSpaces = numberSpacesInitial;

    jsonLine = _addBrace('{', plusId, numberSpacesInitial, true, braceColor, braceHighlight);

    numberSpaces = numberSpaces + 1;
    Object.keys(obj).forEach(function(key, index){
        var hasComma = false,
            internalJsonLine = {
                elements: [],
                lines: []
            },
            newValue;

        plusId = plusId + 1;

        for(var counter = 0; counter < numberSpaces; counter = counter +1){
            internalJsonLine.elements.push(_addBlank());
        }

        internalJsonLine
            .elements
            .push(_addKey(key, plusId, numberSpaces, keyColor, keyHighlight));
        internalJsonLine
            .elements
            .push(_addTwoPoints());

        hasComma = index < (Object.keys(obj).length -1);

        newValue = _addValue(obj[key], plusId, numberSpaces, keyColor, keyHighlight, valueColor, valueHighlight, stringColor, stringHighlight, braceColor, braceHighlight, bracketColor, bracketHighlight, hasComma);

        if(Array.isArray(newValue) && newValue[0].elements[newValue[0].elements.length - 1].class === 'json-brace'){
            var openBrace = newValue[0].elements[newValue[0].elements.length - 1],
                closeBrace = newValue[1].elements[newValue[0].elements.length - 1];

            internalJsonLine
                .elements
                .push(openBrace);

            internalJsonLine.lines = internalJsonLine
                                        .lines
                                        .concat(newValue[0].lines);

            jsonLine.lines.push(internalJsonLine);

            internalJsonLine = {
                elements: [],
                lines: []
            };

            newValue[1].elements.forEach(function (element) {
                internalJsonLine
                    .elements
                    .push(element);
            });
        }
        else{
            internalJsonLine
                .elements
                .push(newValue);
        }

        jsonLine.lines.push(internalJsonLine);

        if(Array.isArray(internalJsonLine.elements[internalJsonLine.elements.length - 1])){

            var internalArray = internalJsonLine
                                    .elements[internalJsonLine
                                                .elements.length - 1];
            internalJsonLine.elements.pop();

            internalJsonLine.elements.push(internalArray[0].elements[0]);

            internalJsonLine.lines = internalArray[0].lines;

            jsonLine.lines.push(internalArray[internalArray.length - 1]);
        }
        else{
            if(hasComma && internalJsonLine.elements[internalJsonLine.elements.length - 1].element !== ','){
                internalJsonLine.elements.push(_addComma());
            }
        }
    });

    jsonLines.push(jsonLine);
    jsonLines.push(_addBrace('}', plusId, numberSpacesInitial, false, braceColor, braceHighlight));

    return jsonLines;
};

var _adjustLinePlusId = function(jsonLines){
    jsonLines.forEach(function(_line){
        var plusId = '';

        if(_line.elements){
            _line.elements.forEach(function(_element){
                if(_element.plusId){
                    plusId = _element.plusId;
                }
            });
        }

        if(_line.lines){
            _line.lines.forEach(function(_internalLine){
                _internalLine.plusId = plusId;
            });

            if(_line.lines.length > 0){
                _line.lines = _adjustLinePlusId(_line.lines);
            }
        }
    });
    return jsonLines;
};

var _createJSONTree = function(obj, numberSpaces, plusId, keyColor, keyHighlight, valueColor, valueHighlight, stringColor, stringHighlight, braceColor, braceHighlight, bracketColor, bracketHighlight, hasComma) {
    var jsonLines = [],
        jsonLine = {
            elements: [],
            lines: []
        },
        jsonObj = {
            'hasPlus': false,
            'newLine': false,
            'endLine': false
        },
        numberSpacesInitial = numberSpaces;

    if(Array.isArray(obj)){
        numberSpaces = numberSpaces + 1;

        jsonObj.hasPlus = true;
        jsonObj.plusId = 'plus_' + plusId;
        jsonObj.element = '[';
        jsonObj.isBlank = false;
        jsonObj.style = 'color:' + bracketColor + '; background-color:' + bracketHighlight;
        jsonObj.class = 'json-bracket';

        jsonLine.elements.push(jsonObj);

        obj.forEach(function(newObj, index){
            var newObject;

            plusId = plusId + 1;

            newObject = _createObject(newObj, plusId, numberSpaces, keyColor, keyHighlight, valueColor, valueHighlight, stringColor, stringHighlight, braceColor, braceHighlight, bracketColor, bracketHighlight);

            newObject.forEach(function(objectLine){
                jsonLine.lines.push(objectLine);
            });

            if(index < (obj.length -1)){
                jsonLine
                    .lines[jsonLine.lines.length - 1]
                        .elements.push(_addComma());
            }

            newObject.forEach(function(line){
                plusId = plusId + line.lines.length + 1;
            });
        });

        jsonLines.push(jsonLine);

        jsonObj = {
            'hasPlus': false,
            'newLine': false,
            'endLine': false
        };
        jsonLine = {
            elements: [],
            lines: []
        };

        for(var counter = 0; counter < numberSpacesInitial; counter = counter +1){
            jsonLine.elements.push(_addBlank());
        }

        jsonObj.hasPlus = false;
        jsonObj.plusId = null;
        jsonObj.element = ']';
        jsonObj.isBlank = false;
        jsonObj.style = 'color:' + bracketColor + '; background-color:' + bracketHighlight;
        jsonObj.class = 'json-bracket';

        jsonLine.elements.push(jsonObj);

        if(hasComma){
            jsonLine.elements.push(_addComma());
        }

        jsonLines.push(jsonLine);
    }
    else{
        jsonLines = _createObject(obj, plusId, numberSpaces, keyColor, keyHighlight, valueColor, valueHighlight, stringColor, stringHighlight, braceColor, braceHighlight, bracketColor, bracketHighlight);
    }

    return _adjustLinePlusId(jsonLines);
};

var _prettyPrint = function(    obj,
                                keyColor, keyHighlight,
                                valueColor, valueHighlight,
                                stringColor, stringHighlight,
                                braceColor, braceHighlight,
                                bracketColor, bracketHighlight) {

    var jsonTree = _createJSONTree( obj, 0, 0,
                                    keyColor, keyHighlight,
                                    valueColor, valueHighlight,
                                    stringColor, stringHighlight,
                                    braceColor, braceHighlight,
                                    bracketColor, bracketHighlight);

    return jsonTree;
};

export default Ember.Component.extend({
    tagName: 'code',
    optionsDefault: {
        keyColor: '#A52A2A',
        keyHighlight: '#FFFFFF',
        valueColor: '#000080',
        valueHighlight: '#FFFFFF',
        stringColor: '#C0FF3E',
        stringHighlight: '#FFFFFF',
        braceColor: '#000000',
        braceHighlight: '#FFFFFF',
        bracketColor: '#000000',
        bracketHighlight: '#FFFFFF'
    },
    options: {},
    pretty: function () {
        var jsonObj = this.get('jsonObj'),
            options,
            json_pretty;

        if(this.options){
            if(typeof this.options === 'object'){
                options = $.extend({}, this.optionsDefault, this.options);
            }
            else{
                options = $.extend({}, this.optionsDefault, JSON.parse(this.options));
            }
        }
        else{
            options = this.optionsDefault;
        }

        this.set('options', options);

        json_pretty = _prettyPrint( jsonObj,
                                    options['keyColor'], options['keyHighlight'],
                                    options['valueColor'], options['valueHighlight'],
                                    options['stringColor'], options['stringHighlight'],
                                    options['braceColor'], options['braceHighlight'],
                                    options['bracketColor'], options['bracketHighlight']);

        return json_pretty;
    }.property('jsonObj'),
    actions: {
        toggleExpand: function(plusId){
            if(Ember.$('#' + plusId).hasClass('fa-plus-square-o')){
                Ember.$('#' + plusId).removeClass('fa-plus-square-o');
                Ember.$('#' + plusId).addClass('fa-minus-square-o');
                Ember.$('.jsonTreeView').find('[data-id="' + plusId + '"]').show();
            }
            else{
                Ember.$('#' + plusId).removeClass('fa-minus-square-o');
                Ember.$('#' + plusId).addClass('fa-plus-square-o');
                Ember.$('.jsonTreeView').find('[data-id="' + plusId + '"]').hide();
            }
        }
    }
});<|endoftext|>


<commit_before>/*
 * Unstrap v1.0.0-1
 * http://unstrap.org/components/dropdown
 * 2015
 * MIT license
 */
(function (global) {

	var version = '1.0.0',
	    collection = {};

	function extendUnstrap (v) {
	    var list = $$$.queryAll(collection[v].selectors[0]);
	    for (var i = 0; i < list.length; i++) {
	        collection[v].extend(list.item(i));
	    }
	}

	$$$.onReady(function () {
	    var observer = new MutationObserver(function (mut) {
	        mut.forEach(function (m) {
	            var n = m.addedNodes,
	                f;
	            for (var i=0; i<n.length; i++) {
	                var c = n.item(i).classList;
	                if (c) {
	                	for (var j = 0; j < c.length; j++) {
	                    	if (f = collection[c.item(j)]) {
	                        	f.extend(n.item(i));
	                    	}
	                	}
	            	}
	            }
	        });
	    });
	    Object.keys(collection).forEach(function (v) {
	        extendUnstrap(v);
	    })
	    observer.observe(document.body, {childList: true, subtree: true});
	});

	function define (a, b, c) {
		if (typeof a === 'function') {
			c = a;
		}

		var m = c();
		collection[m.name] = m;

	}

	// dropdown
	define(function () {

		function caret () {
			var c = document.createElement('span');
			c.classList.add('caret');
			return c;
		}

		function extend (elem) {
			var but = elem.querySelector('.dropdown-toggle');
			if (!but) return;
			Object.defineProperty(but, 'value', {
				set: function (val) {
					var v, li = this.nextElementSibling.querySelectorAll('li');
					for (var i = 0; i < li.length; i++) {
						v = li.item(i).getAttribute('data-value') || li.item(i).textContent;
						if (val === v) {
							but._value = val;
							but.textContent = li.textContent || val;
							but.appendChild(caret());
						}
					}
				},
				get: function () {
					 return but._value;
				}
			});
			but.nextElementSibling.addEventListener('click', function (evt) {
				but.value = evt.target.getAttribute('data-value') || evt.target.textContent;
				evt.target.parentNode.parentNode.parentNode.classList.remove('open');
			})
			but.addEventListener('click', function (evt) {
				var dd = evt.target.parentNode;
				dd.classList.toggle('open');
			})
			return elem;
		}

		return {
			version: '1.0.0',
			name: 'dropdown',
			selectors: ['.dropdown'],
			extend: extend
		}
	});

	return {
		version: version
	}

}(window))<commit_msg>onready<commit_after>/*
 * Unstrap v1.0.0-1
 * http://unstrap.org/components/dropdown
 * 2015
 * MIT license
 */
(function (global) {

	var version = '1.0.0',
	    collection = {};

	function onReady (func) {
        if (document.readyState === 'complete' || document.readyState === 'interactive') {
            doNext(func);
        } else if (document.addEventListener) {
            document.addEventListener('DOMContentLoaded', func, false);
        } else {
            document.attachEvent('onreadystatechange', func);
        }
    }

	function extendUnstrap (v) {
	    var list = $$$.queryAll(collection[v].selectors[0]);
	    for (var i = 0; i < list.length; i++) {
	        collection[v].extend(list.item(i));
	    }
	}

	onReady(function () {
	    var observer = new MutationObserver(function (mut) {
	        mut.forEach(function (m) {
	            var n = m.addedNodes,
	                f;
	            for (var i=0; i<n.length; i++) {
	                var c = n.item(i).classList;
	                if (c) {
	                	for (var j = 0; j < c.length; j++) {
	                    	if (f = collection[c.item(j)]) {
	                        	f.extend(n.item(i));
	                    	}
	                	}
	            	}
	            }
	        });
	    });
	    Object.keys(collection).forEach(function (v) {
	        extendUnstrap(v);
	    })
	    observer.observe(document.body, {childList: true, subtree: true});
	});

	function define (a, b, c) {
		if (typeof a === 'function') {
			c = a;
		}

		var m = c();
		collection[m.name] = m;

	}

	// dropdown
	define(function () {

		function caret () {
			var c = document.createElement('span');
			c.classList.add('caret');
			return c;
		}

		function extend (elem) {
			var but = elem.querySelector('.dropdown-toggle');
			if (!but) return;
			Object.defineProperty(but, 'value', {
				set: function (val) {
					var v, li = this.nextElementSibling.querySelectorAll('li');
					for (var i = 0; i < li.length; i++) {
						v = li.item(i).getAttribute('data-value') || li.item(i).textContent;
						if (val === v) {
							but._value = val;
							but.textContent = li.textContent || val;
							but.appendChild(caret());
						}
					}
				},
				get: function () {
					 return but._value;
				}
			});
			but.nextElementSibling.addEventListener('click', function (evt) {
				but.value = evt.target.getAttribute('data-value') || evt.target.textContent;
				evt.target.parentNode.parentNode.parentNode.classList.remove('open');
			})
			but.addEventListener('click', function (evt) {
				var dd = evt.target.parentNode;
				dd.classList.toggle('open');
			})
			return elem;
		}

		return {
			version: '1.0.0',
			name: 'dropdown',
			selectors: ['.dropdown'],
			extend: extend
		}
	});

	return {
		version: version
	}

}(window))<|endoftext|>


<commit_before>import Ember from 'ember';
import layout from '../templates/components/number-to-english';

export default Ember.Component.extend({
  layout,

  number: 0,

  words: Ember.computed('number', function() {

    let string = this.number.toString(), units, tens, scales, start, end, chunks,
                 chunksLen, chunk, ints, i, word, words, and = 'and';

    /* Is number zero? */
    if(parseInt(string) === 0) {
      return 'zero';
    }

    /* Array of units as words */
    units = [
      '', 'one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight',
      'nine', 'ten', 'eleven', 'twelve', 'thirteen', 'fourteen', 'fifteen',
      'sixteen', 'seventeen', 'eighteen', 'nineteen'
    ];

    /* Array of tens as words */
    tens = [
      '', '', 'twenty', 'thirty', 'forty', 'fifty', 'sixty', 'seventy',
      'eighty', 'ninety'
    ];

    /* Array of scales as words */
    scales = [
      '', 'thousand', 'million', 'billion', 'trillion', 'quadrillion',
      'quintillion', 'sextillion', 'septillion', 'octillion', 'nonillion',
      'decillion', 'undecillion', 'duodecillion', 'tredecillion',
      'quatttuor-decillion', 'quindecillion', 'sexdecillion',
      'septen-decillion', 'octodecillion', 'novemdecillion', 'vigintillion',
      'centillion'
    ];

    /* Split user arguemnt into 3 digit chunks from right to left */
    start = string.length;
    chunks = [];
    while(start > 0) {
      end = start;
      chunks.push(string.slice((start = Math.max(0, start - 3)), end));
    }

    /* Check if function has enough scale words to be able to stringify the user argument */
    chunksLen = chunks.length;
    if(chunksLen > scales.length) {
      return '';
    }

    /* Stringify each integer in each chunk */
    words = [];
    for(i = 0; i < chunksLen; i++) {
      chunk = parseInt(chunks[i]);
      if(chunk) {

        /* Split chunk into array of individual integers */
        ints = chunks[i].split( '' ).reverse().map(parseFloat);

        /* If tens integer is 1, i.e. 10, then add 10 to units integer */
        if(ints[1] === 1) {
          ints[0] += 10;
        }

        /* Add scale word if chunk is not zero and array item exists */
        if((word = scales[i])) {
          words.push(word);
        }

        /* Add unit word if array item exists */
        if((word = units[ints[0]])) {
          words.push(word);
        }

        /* Add tens word if array item exists */
        if((word = tens[ints[1]])) {
          words.push(word);
        }

        /* Add 'and' string after units or tens integer if: */
        if(ints[0] || ints[1]) {

          /* Chunk has a hundreds integer or chunk is the first of multiple chunks */
          if((ints[2] || !i && chunksLen) && string.length >= 3) {
            words.push(and);
          }
        }

        /* Add hundreds word if array item exists */
        if((word = units[ints[2]])) {
          words.push(word + ' hundred');
        }
      }
    }
    return words.reverse().join(' ');
  })
});
<commit_msg>[FEATURE] Decimal support:<commit_after>import Ember from 'ember';
import layout from '../templates/components/number-to-english';

export default Ember.Component.extend({
  layout,

  number: 0,
  decimal: 'word',

  integerToWord: function(number) {
    let units, tens, scales, start, end, chunks,
        chunksLen, chunk, ints, i, word, words, and = 'and';

    /* Is number zero? */
    if(parseInt(number) === 0) {
      return 'zero';
    }

    /* Array of units as words */
    units = [
      '', 'one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight',
      'nine', 'ten', 'eleven', 'twelve', 'thirteen', 'fourteen', 'fifteen',
      'sixteen', 'seventeen', 'eighteen', 'nineteen'
    ];

    /* Array of tens as words */
    tens = [
      '', '', 'twenty', 'thirty', 'forty', 'fifty', 'sixty', 'seventy',
      'eighty', 'ninety'
    ];

    /* Array of scales as words */
    scales = [
      '', 'thousand', 'million', 'billion', 'trillion', 'quadrillion',
      'quintillion', 'sextillion', 'septillion', 'octillion', 'nonillion',
      'decillion', 'undecillion', 'duodecillion', 'tredecillion',
      'quatttuor-decillion', 'quindecillion', 'sexdecillion',
      'septen-decillion', 'octodecillion', 'novemdecillion', 'vigintillion',
      'centillion'
    ];

    /* Split user arguemnt into 3 digit chunks from right to left */
    start = number.length;
    chunks = [];
    while(start > 0) {
      end = start;
      chunks.push(number.slice((start = Math.max(0, start - 3)), end));
    }

    /* Check if function has enough scale words to be able to stringify the user argument */
    chunksLen = chunks.length;
    if(chunksLen > scales.length) {
      return '';
    }

    /* Stringify each integer in each chunk */
    words = [];
    for(i = 0; i < chunksLen; i++) {
      chunk = parseInt(chunks[i]);
      if(chunk) {

        /* Split chunk into array of individual integers */
        ints = chunks[i].split( '' ).reverse().map(parseFloat);

        /* If tens integer is 1, i.e. 10, then add 10 to units integer */
        if(ints[1] === 1) {
          ints[0] += 10;
        }

        /* Add scale word if chunk is not zero and array item exists */
        if((word = scales[i])) {
          words.push(word);
        }

        /* Add unit word if array item exists */
        if((word = units[ints[0]])) {
          words.push(word);
        }

        /* Add tens word if array item exists */
        if((word = tens[ints[1]])) {
          words.push(word);
        }

        /* Add 'and' string after units or tens integer if: */
        if(ints[0] || ints[1]) {

          /* Chunk has a hundreds integer or chunk is the first of multiple chunks */
          if((ints[2] || !i && chunksLen) && number.length >= 3) {
            words.push(and);
          }
        }

        /* Add hundreds word if array item exists */
        if((word = units[ints[2]])) {
          words.push(word + ' hundred');
        }
      }
    }
    return words.reverse().join(' ');
  },

  word: Ember.computed('number', function() {
    let fullNumber = this.number.toString().split('.'), integerNumber = fullNumber[0], decimalNumber = fullNumber[1];
    let integerNumberInWords = this.integerToWords(integerNumber);
    let decimalNumberInWords;
    if (decimalNumber) {
      decimalNumberInWords = decimalNumber.length === 1 ? this.integerToWords(decimalNumber + "0") : this.integerToWords(decimalNumber);
    }

    return decimalNumberInWords ? `${integerNumberInWords} with ${decimalNumberInWords}` : integerNumberInWords;
  })
});
<|endoftext|>