<commit_before>var EventEmitter = require('events').EventEmitter
var addpre = require('./range').addPrefix
var PATH_SEP = require("./codec").PATH_SEP
var _nut = require('./nut')
var getPathArray = _nut.getPathArray
var resolveKeyPath = _nut.resolveKeyPath

var errors = require('levelup/lib/errors')

function isFunction (f) {
  return 'function' === typeof f
}

function isString (s) {
  return 'string' === typeof s
}

function isObject (o) {
  return o && 'object' === typeof o
}

var version = require('./package.json').version

var sublevel = module.exports = function (nut, prefix, createStream, options) {
  var emitter = new EventEmitter()
  emitter.sublevels = {}
  emitter.options = options

  emitter.version = version

  emitter.methods = {}
  prefix = prefix || []

  function errback (err) { if (err) emitter.emit('error', err) }

  createStream = createStream || function (e) { return e }

  function mergeOpts(opts) {
    var k, o = {}
    if(options)
      for(k in options)
        if(options[k] != undefined)o[k] = options[k]
    if(opts)
      for(k in opts)
        if(opts[k] != undefined) o[k] = opts[k]
    return o
  }

  emitter.put = function (key, value, opts, cb) {
    if('function' === typeof opts) cb = opts, opts = {}
    if(!cb) cb = errback

    nut.apply([{
      key: key, value: value,
      path: prefix.slice(), type: 'put'
    }], mergeOpts(opts), function (err) {
      if(!err) { emitter.emit('put', key, value); cb(null) }
      if(err) return cb(err)
    })
  }

  emitter.pathAsArray = function () {
    return prefix.slice()
  }

  emitter.path = function () {
    return PATH_SEP + prefix.join(PATH_SEP)
  }

  emitter.setPath = function (aPath) {
      aPath = getPathArray(aPath)
      if (aPath) {
          prefix = aPath
          return true
      } else {
          return false
      }
  }

  emitter.del = function (key, opts, cb) {
    if('function' === typeof opts) cb = opts, opts = {}
    if(!cb) cb = errback

    nut.apply([{
      key: key,
      path: prefix.slice(), type: 'del'
    }], mergeOpts(opts), function (err) {
      if(!err) { emitter.emit('del', key); cb(null) }
      if(err) return cb(err)
    })
  }

  emitter.batch = function (ops, opts, cb) {
    if('function' === typeof opts)
      cb = opts, opts = {}
    if(!cb) cb = errback

    ops = ops.map(function (op) {
      return {
        key:           op.key,
        value:         op.value,
        path:        op.path || prefix,
        keyEncoding:   op.keyEncoding,    // *
        valueEncoding: op.valueEncoding,  // * (TODO: encodings on sublevel)
        type:          op.type
      }
    })

    nut.apply(ops, mergeOpts(opts), function (err) {
      if(!err) { emitter.emit('batch', ops); cb(null) }
      if(err) return cb(err)
    })
  }

  emitter.get = function (key, opts, cb) {
    if('function' === typeof opts)
      cb = opts, opts = {}
    nut.get(key, prefix, mergeOpts(opts), function (err, value) {
      if(err) cb(new errors.NotFoundError('Key not found in database', err))
      else cb(null, value)
    })
  }

  emitter.sublevel = function (name, opts) {
    return emitter.sublevels[name] =
      emitter.sublevels[name] || sublevel(nut, prefix.concat(name), createStream, mergeOpts(opts))
  }

  function _addHook(key, callback, hooksAdd) {
      if(isFunction(key)) return hooksAdd([prefix], key)
      if(isString(key))   return hooksAdd(resolveKeyPath(prefix, key), callback)
      if(isObject(key))   return hooksAdd(addpre(prefix, key), callback)

      //TODO: handle ranges, needed for level-live-stream, etc.
      throw new Error('not implemented yet')
  }

  emitter.pre = function (key, hook) {
      return _addHook(key, hook, nut.pre)
  }

  emitter.post = function (key, hook) {
      return _addHook(key, hook, nut.post)
  }

  emitter.createReadStream = function (opts) {
    opts = mergeOpts(opts)
    opts.path = opts.path || prefix
    var stream
    var it = nut.iterator(opts, function (err, it) {
      stream.setIterator(it)
    })

    stream = createStream(opts, nut.createDecoder(opts))
    if(it) stream.setIterator(it)

    return stream
  }

  emitter.createValueStream = function (opts) {
    opts = opts || {}
    opts.values = true
    opts.keys = false
    return emitter.createReadStream(opts)
  }

  emitter.createKeyStream = function (opts) {
    opts = opts || {}
    opts.values = false
    opts.keys = true
    return emitter.createReadStream(opts)
  }

  emitter.close = function (cb) {
    //TODO: deregister all hooks
    process.nextTick(cb || function () {})
  }

  return emitter
}
<commit_msg>* [bug] mergeOptions can not merge null/emptystring attribute * createReadStream use options.start to speedup iterator<commit_after>var path = require("path")
var EventEmitter = require('events').EventEmitter
var addpre = require('./range').addPrefix
var PATH_SEP = require("./codec").PATH_SEP
var SUBKEY_SEP = require('./codec').SUBKEY_SEP
var _nut = require('./nut')
var getPathArray = _nut.getPathArray
var resolveKeyPath = _nut.resolveKeyPath
var pathArrayToPath = _nut.pathArrayToPath

var errors = require('levelup/lib/errors')

function isFunction (f) {
  return 'function' === typeof f
}

function isString (s) {
  return 'string' === typeof s
}

function isObject (o) {
  return o && 'object' === typeof o
}

var version = require('./package.json').version

var sublevel = module.exports = function (nut, prefix, createStream, options) {
  var emitter = new EventEmitter()
  emitter.sublevels = {}
  emitter.options = options

  emitter.version = version

  emitter.methods = {}
  prefix = prefix || []

  function errback (err) { if (err) emitter.emit('error', err) }

  createStream = createStream || function (e) { return e }

  function mergeOpts(opts) {
    var k, o = {}
    if(options)
      for(k in options)
        if(options[k] !== undefined)o[k] = options[k]
    if(opts)
      for(k in opts)
        if(opts[k] !== undefined) o[k] = opts[k]
    return o
  }

  emitter.put = function (key, value, opts, cb) {
    if('function' === typeof opts) cb = opts, opts = {}
    if(!cb) cb = errback

    nut.apply([{
      key: key, value: value,
      path: prefix.slice(), type: 'put'
    }], mergeOpts(opts), function (err) {
      if(!err) { emitter.emit('put', key, value); cb(null) }
      if(err) return cb(err)
    })
  }

  emitter.pathAsArray = function () {
    return prefix.slice()
  }

  emitter.path = function () {
    return PATH_SEP + prefix.join(PATH_SEP)
  }

  emitter.setPath = function (aPath) {
      aPath = getPathArray(aPath)
      if (aPath) {
          prefix = aPath
          return true
      } else {
          return false
      }
  }

  emitter.del = function (key, opts, cb) {
    if('function' === typeof opts) cb = opts, opts = {}
    if(!cb) cb = errback

    nut.apply([{
      key: key,
      path: prefix.slice(), type: 'del'
    }], mergeOpts(opts), function (err) {
      if(!err) { emitter.emit('del', key); cb(null) }
      if(err) return cb(err)
    })
  }

  emitter.batch = function (ops, opts, cb) {
    if('function' === typeof opts)
      cb = opts, opts = {}
    if(!cb) cb = errback

    ops = ops.map(function (op) {
      return {
        key:           op.key,
        value:         op.value,
        path:        op.path || prefix,
        keyEncoding:   op.keyEncoding,    // *
        valueEncoding: op.valueEncoding,  // * (TODO: encodings on sublevel)
        type:          op.type
      }
    })

    nut.apply(ops, mergeOpts(opts), function (err) {
      if(!err) { emitter.emit('batch', ops); cb(null) }
      if(err) return cb(err)
    })
  }

  emitter.get = function (key, opts, cb) {
    if('function' === typeof opts)
      cb = opts, opts = {}
    nut.get(key, prefix, mergeOpts(opts), function (err, value) {
      if(err) cb(new errors.NotFoundError('Key not found in database', err))
      else cb(null, value)
    })
  }

  emitter.sublevel = function (name, opts) {
    return emitter.sublevels[name] =
      emitter.sublevels[name] || sublevel(nut, prefix.concat(name), createStream, mergeOpts(opts))
  }

  function _addHook(key, callback, hooksAdd) {
      if(isFunction(key)) return hooksAdd([prefix], key)
      if(isString(key))   return hooksAdd(resolveKeyPath(prefix, key), callback)
      if(isObject(key))   return hooksAdd(addpre(prefix, key), callback)

      //TODO: handle ranges, needed for level-live-stream, etc.
      throw new Error('not implemented yet')
  }

  emitter.pre = function (key, hook) {
      return _addHook(key, hook, nut.pre)
  }

  emitter.post = function (key, hook) {
      return _addHook(key, hook, nut.post)
  }

  emitter.createReadStream = function (opts) {
    opts = mergeOpts(opts)
    opts.path = opts.path || prefix

    //set opts.start to speedup leveldb interator
    var vPath = pathArrayToPath(getPathArray(opts.path))
    //do not set opts.start if gte or min exists
    if (opts.gte === undefined && opts.min === undefined) {
        if (!opts.start) {
            opts.start = vPath + SUBKEY_SEP
        } else {
            opts.start = path.resolve(vPath, opts.start) + SUBKEY_SEP
        }
    }
    var stream
    var it = nut.iterator(opts, function (err, it) {
      stream.setIterator(it)
    })

    stream = createStream(opts, nut.createDecoder(opts))
    if(it) stream.setIterator(it)

    return stream
  }

  emitter.createValueStream = function (opts) {
    opts = opts || {}
    opts.values = true
    opts.keys = false
    return emitter.createReadStream(opts)
  }

  emitter.createKeyStream = function (opts) {
    opts = opts || {}
    opts.values = false
    opts.keys = true
    return emitter.createReadStream(opts)
  }

  emitter.close = function (cb) {
    //TODO: deregister all hooks
    process.nextTick(cb || function () {})
  }

  return emitter
}
<|endoftext|>


<commit_before>'use strict';

var Response = require('Response');
var EventEmitter = require('EventEmitter');

var sklad = new EventEmitter();

var storage = checkAndGetStorage();
var addListener;

module.exports = sklad;

sklad.isSupported = function () {
    return !!storage;
};

addListener = getListenerFunc();

addListener('storage', function (e) {
    var event = 'change:' + e.key;

    sklad.emit(event, e.newValue);
}, false);

sklad.set = function (key, value, strict) {
    var r = new Response();
    var queue = new Response.Queue();
    var stringValue = stringifyValue(value);

    queue
        .setData('params', {
            key: key,
            val: stringValue,
            strict: strict
        })
        .push(setItem)
        .push(getItem)
        .push(checkSavedItem)
        .start();

    return r.listen(queue);
};

sklad.get = function (key, type) {
    var r = new Response();
    var queue = new Response.Queue();

    queue
        .setData('params', {
            key: key,
            type: type
        })
        .push(getItem)
        .push(parseData)
        .push(queue.resolve)
        .start();

    return r.listen(queue);
};

sklad.getValue = function (key, type) {
    var r = this.get(key, type);
    return r.getResult();
};

function checkAndGetStorage() {
    var test = new Date;
    var stor;
    var result;
    try {
        (stor = window.localStorage).setItem(test, test);
        result = stor.getItem(test) == test;
        stor.removeItem(test);
        return result && stor;
    } catch (exception) {}
}

function getListenerFunc() {
    if ('v'=='\v') { // Note: IE listens on document
        return document.attachEvent.bind(document);
    } else if (window.opera || /webkit/i.test( navigator.userAgent )){ // Note: Opera and WebKits listens on window
        return window.addEventListener.bind(window);
    } else { // Note: FF listens on document.body or document
        return document.body.addEventListener.bind(document.body);
    }
}

function stringifyValue(value) {
    var result;
    if (typeof value === 'string') {
        return value;
    } else {
        result = JSON.stringify(value);
        return result;
    }
}

function setItem() {
    var params = this.getData('params');

    this.invoke(storage.setItem, [params.key, params.val], storage);
}

function getItem() {
    var params = this.getData('params');

    return this.invoke(storage.getItem, [params.key], storage);
}

function parseData(data) {
    var params = this.getData('params');

    if (params.type === 'json') {
        return this.invoke(JSON.parse, [data]);
    } else {
        this.resolve(data);
    }
}

function checkSavedItem(data) {
    var params = this.getData('params');

    if (params.strict && data !== params.val) {
        this.reject();
    } else {
        this.resolve(data);
    }
}
<commit_msg>добавил jsdoc к методам<commit_after>'use strict';

/**
 * @fileOverview sklad.
 */

var Response = require('Response');
var EventEmitter = require('EventEmitter');

var sklad = new EventEmitter();

var storage = checkAndGetStorage();
var addListener = getListenerFunc();

module.exports = sklad;

/**
 * Проверяет доступность localStorage
 * @return {Boolean}
 */
sklad.isSupported = function () {
    return !!storage;
};

/**
 * Подписываемся на изменение значений в localStorage
 * из других вкладок браузера
 */
addListener('storage', triggerChangeEvent, false);

/**
 * @param {Object} e Объект события изменение значений в localStorage
 */
function triggerChangeEvent(e) {
    var event = 'change:' + e.key;

    sklad.emit(event, e.newValue);
}

/**
 * Устанавливаем значение в localStorage
 * @param {*} key строка или все что может быть приведенно к строке
 *                ключ в localStorage для сохраняемого значения
 * @param {*} value сохраняемое значение
 * @param {Boolean} [strict] флаг для проверки сохраненного значения
 * @return {Response}
 */
sklad.set = function (key, value, strict) {
    var r = new Response();
    var queue = new Response.Queue();
    var stringValue = JSON.stringify(value);

    queue
        .setData('params', {
            key: key,
            val: stringValue,
            strict: strict
        })
        .push(setItem)
        .push(getItem)
        .push(checkSavedItem)
        .start();

    return r.listen(queue);
};

/**
 * Достаём значение из localStorage
 * @param {*} key строка или все что может быть приведенно к строке
 *                ключ сохраненного значения в localStorage
 * @param {String} [type] если указан как 'json' парсим значение
 *                      если нет отдаём строку
 * @return {Response}
 */
sklad.get = function (key, type) {
    var r = new Response();
    var queue = new Response.Queue();

    queue
        .setData('params', {
            key: key,
            type: type
        })
        .push(getItem)
        .push(parseData)
        .push(queue.resolve)
        .start();

    return r.listen(queue);
};

/**
 * Метод для получения непосредственно значения
 * а не экземпляра Response
 * @param {*} key строка или все что может быть приведенно к строке
 *                ключ сохраненного значения в localStorage
 * @param {String} [type] если указан как 'json' парсим значение
 *                      если нет отдаём строку
 * @return {*} значение из localStorage
 */
sklad.getValue = function (key, type) {
    var r = this.get(key, type);
    return r.getResult();
};

/**
 * Проверяем наличие localStorage
 * если доступен возвращаем ссылку на него
 * если не доступен возвращаем false
 * @return {Storage|Boolean}
 */
function checkAndGetStorage() {
    var test = new Date;
    var stor;
    var result;
    try {
        (stor = window.localStorage).setItem(test, test);
        result = stor.getItem(test) == test;
        stor.removeItem(test);
        return result && stor;
    } catch (exception) {
        return false;
    }
}

/**
 * В зависимости от браузера возвращаем нужную функцию для подписки на событие 'storage'
 * @return {Function}
 */
function getListenerFunc() {
    if ('v'=='\v') {
        // Note: IE listens on document
        return document.attachEvent.bind(document);
    } else if (window.opera || /webkit/i.test( navigator.userAgent )){
        // Note: Opera and WebKits listens on window
        return window.addEventListener.bind(window);
    } else {
        // Note: FF listens on document.body or document
        return document.body.addEventListener.bind(document.body);
    }
}

/**
 * Безопасный метод для записи в localStorage
 * если ловим ошибки реджектим очередь
 * @this {Queue}
 */
function setItem() {
    var params = this.getData('params');

    this.invoke(storage.setItem, [params.key, params.val], storage);
}

/**
 * Безопасный метод для чтения из localStorage
 * @this {Queue}
 * @return {*} значение из localStorage
 */
function getItem() {
    var params = this.getData('params');

    return this.invoke(storage.getItem, [params.key], storage);
}

/**
 * В зависимости от ожидаемого формата ответа парсим строку в json
 * или отдаём сырые данные
 * @this {Queue}
 * @return {*}
 */
function parseData(data) {
    var params = this.getData('params');

    if (params.type === 'json') {
        return this.invoke(JSON.parse, [data]);
    } else {
        this.resolve(data);
    }
}

/**
 * Проверяем сохраненное значение если указан strict режим
 * @this {Queue}
 */
function checkSavedItem(data) {
    var params = this.getData('params');

    if (params.strict && data !== params.val) {
        this.reject();
    } else {
        this.resolve(data);
    }
}
<|endoftext|>


<commit_before>// load KS library
S.src(".kslate.js");

// full <-> three-quarters <-> two-thirds <-> one-half <-> one-third <-> one-quarter
// grow moves to the left and shrink moves to the right

KS.register(KS.mode("full-screen"), ["0:ctrl,cmd"]);

// top (NORTH) (UP), right (EAST) (RIGHT), bottom (SOUTH) (DOWN), left (WEST) (LEFT)
KS.register(KS.mode("left"), ["left:ctrl,cmd"]);
KS.register(KS.mode("right"), ["right:ctrl,cmd"]);
KS.register(KS.mode("top"), ["up:ctrl,cmd"]);
KS.register(KS.mode("bottom"), ["down:ctrl,cmd"]);

// hsize 1 (1), 1/2 (2), 1/3 (3), 2/3 (4)
KS.register(KS.mode("width-full"), ["1:ctrl,cmd"]);
KS.register(KS.mode("width-half"), ["2:ctrl,cmd"]);
KS.register(KS.mode("width-third"), ["3:ctrl,cmd"]);
KS.register(KS.mode("width-two-thirds"), ["4:ctrl,cmd"]);

// vsize 1, 1/2, 1/3, 2/3
KS.register(KS.mode("height-full"), ["5:ctrl,cmd"]);
KS.register(KS.mode("height-half"), ["6:ctrl,cmd"]);
KS.register(KS.mode("height-third"), ["7:ctrl,cmd"]);
KS.register(KS.mode("height-two-thirds"), ["8:ctrl,cmd"]);

// KS.register(KS.mode("full-screen"), ["1:ctrl,cmd"]);
// KS.register(KS.mode("left-half"), ["2:ctrl,cmd"]);
// KS.register(KS.mode("right-half"), ["3:ctrl,cmd"]);
// KS.register(KS.mode("left-third"), ["4:ctrl,cmd"]);
// KS.register(KS.mode("middle-third"), ["5:ctrl,cmd"]);
// KS.register(KS.mode("right-third"), ["6:ctrl,cmd"]);
// KS.register(KS.mode("right-two-thirds"), ["7:ctrl,cmd"]);
// KS.register(KS.mode("top-half"), ["8:ctrl,cmd"]);
// KS.register(KS.mode("bottom-half"), ["9:ctrl,cmd"]);

// KS.register(KS.op("grow-left"), ["left:ctrl,cmd"]);
// KS.register(KS.op("shrink-left"), ["left:shift,ctrl,cmd"]);

// KS.register(KS.op("nudge", "left"), ["left:shift,ctrl,cmd"]);
// KS.register(KS.op("nudge", "right"), ["right:shift,ctrl,cmd"]);
// KS.register(KS.op("nudge", "up"), ["up:shift,ctrl,cmd"]);
// KS.register(KS.op("nudge", "down"), ["down:shift,ctrl,cmd"]);

// KS.register(KS.op("resize", "grow-horizontal"), ["right:shift,ctrl,cmd"]);
// KS.register(KS.op("resize", "shrink-horizontal"), ["left:shift,ctrl,cmd"]);

// KS.register(KS.op("resize", "grow-vertical"), ["up:shift,ctrl,cmd"]);
// KS.register(KS.op("resize", "shrink-vertical"), ["down:shift,ctrl,cmd"]);

// KS.register(KS.op("resize", "full-width"), ["right:ctrl,alt,cmd"]);
// KS.register(KS.op("resize", "half-width"), ["left:ctrl,alt,cmd"]);

// KS.register(KS.op("resize", "full-height"), ["up:ctrl,alt,cmd"]);
// KS.register(KS.op("resize", "half-height"), ["down:ctrl,alt,cmd"]);

KS.register(KS.op("resize", "grow"), ["g:ctrl,cmd"]);
// KS.register(KS.op("resize", "shrink"), ["s:ctrl,cmd"]);

KS.register(KS.op("retile"), ["t:ctrl,cmd"]);
KS.register(KS.op("retile-all-main"), ["t:ctrl,alt,cmd"]);
KS.register(KS.op("tile-swap"), ["s:ctrl,cmd"]);

KS.register(KS.op("promote"), ["p:ctrl,cmd"]);
KS.register(KS.op("demote"), ["d:ctrl,cmd"]);

// TODO: Stick window on corner: it's real estate no longer available.

// Bind All. NOTE: some of these may *not* work if you have not
// removed the expose/spaces/mission control bindings.
S.bnda({
    // Grid
    // "esc:ctrl" : S.op("grid"),

    "-:ctrl,cmd": S.op("relaunch"),

    "tab:ctrl,cmd": S.op("switch"),

    // "b:ctrl,cmd" : S.op("focus", { "app" : "Blue Jeans" }),
    "c:ctrl,cmd" : S.op("focus", { "app" : "Slack" }),
    "e:ctrl,cmd" : S.op("focus", { "app" : "Emacs" }),
    // "p:ctrl,cmd" : S.op("focus", { "app" : "Safari Technology Preview" }),
    "o:ctrl,cmd" : S.op("focus", { "app" : "Microsoft Outlook" }),
    "i:ctrl,cmd" : S.op("focus", { "app" : "iTerm2" }),
    "f:ctrl,cmd" : S.op("focus", { "app" : "Firefox" }),

    // change which window is in focus
    "right:cmd" : S.op("focus", { "direction" : "right" }),
    "left:cmd" : S.op("focus", { "direction" : "left" }),
    "up:cmd" : S.op("focus", { "direction" : "up" }),
    "down:cmd" : S.op("focus", { "direction" : "down" }),

    // Window Hints
    "esc:cmd" : S.op("hint")
});

// Configs
S.configAll({
    "defaultToCurrentScreen" : true,
    "checkDefaultsOnLoad" : true
});

// Log that we're done configuring
S.log("[SLATE] -------------- Finished Loading Config --------------");
<commit_msg>remove unused and commented out config lines<commit_after>// load KS library
S.src(".kslate.js");

// full <-> three-quarters <-> two-thirds <-> one-half <-> one-third <-> one-quarter
// grow moves to the left and shrink moves to the right

KS.register(KS.mode("full-screen"), ["0:ctrl,cmd"]);

// top (NORTH) (UP), right (EAST) (RIGHT), bottom (SOUTH) (DOWN), left (WEST) (LEFT)
KS.register(KS.mode("left"), ["left:ctrl,cmd"]);
KS.register(KS.mode("right"), ["right:ctrl,cmd"]);
KS.register(KS.mode("top"), ["up:ctrl,cmd"]);
KS.register(KS.mode("bottom"), ["down:ctrl,cmd"]);

// hsize 1 (1), 1/2 (2), 1/3 (3), 2/3 (4)
KS.register(KS.mode("width-full"), ["1:ctrl,cmd"]);
KS.register(KS.mode("width-half"), ["2:ctrl,cmd"]);
KS.register(KS.mode("width-third"), ["3:ctrl,cmd"]);
KS.register(KS.mode("width-two-thirds"), ["4:ctrl,cmd"]);

// vsize 1, 1/2, 1/3, 2/3
KS.register(KS.mode("height-full"), ["5:ctrl,cmd"]);
KS.register(KS.mode("height-half"), ["6:ctrl,cmd"]);
KS.register(KS.mode("height-third"), ["7:ctrl,cmd"]);
KS.register(KS.mode("height-two-thirds"), ["8:ctrl,cmd"]);

KS.register(KS.op("resize", "grow"), ["g:ctrl,cmd"]);
// KS.register(KS.op("resize", "shrink"), ["s:ctrl,cmd"]);

KS.register(KS.op("retile"), ["t:ctrl,cmd"]);
KS.register(KS.op("retile-all-main"), ["t:ctrl,alt,cmd"]);
KS.register(KS.op("tile-swap"), ["s:ctrl,cmd"]);

KS.register(KS.op("promote"), ["p:ctrl,cmd"]);
KS.register(KS.op("demote"), ["d:ctrl,cmd"]);

// TODO: Stick window on corner: it's real estate no longer available.

// Bind All. NOTE: some of these may *not* work if you have not
// removed the expose/spaces/mission control bindings.
S.bnda({
    // Grid
    // "esc:ctrl" : S.op("grid"),

    "-:ctrl,cmd": S.op("relaunch"),

    "tab:ctrl,cmd": S.op("switch"),

    // "b:ctrl,cmd" : S.op("focus", { "app" : "Blue Jeans" }),
    "c:ctrl,cmd" : S.op("focus", { "app" : "Slack" }),
    "e:ctrl,cmd" : S.op("focus", { "app" : "Emacs" }),
    // "p:ctrl,cmd" : S.op("focus", { "app" : "Safari Technology Preview" }),
    "o:ctrl,cmd" : S.op("focus", { "app" : "Microsoft Outlook" }),
    "i:ctrl,cmd" : S.op("focus", { "app" : "iTerm2" }),
    "f:ctrl,cmd" : S.op("focus", { "app" : "Firefox" }),

    // change which window is in focus
    "right:cmd" : S.op("focus", { "direction" : "right" }),
    "left:cmd" : S.op("focus", { "direction" : "left" }),
    "up:cmd" : S.op("focus", { "direction" : "up" }),
    "down:cmd" : S.op("focus", { "direction" : "down" }),

    // Window Hints
    "esc:cmd" : S.op("hint")
});

// Configs
S.configAll({
    "defaultToCurrentScreen" : true,
    "checkDefaultsOnLoad" : true
});

// Log that we're done configuring
S.log("[SLATE] -------------- Finished Loading Config --------------");
<|endoftext|>


<commit_before>/**
 * 移动webapp开发滑动组件
 * 可应用于图片轮播、查看大图翻页、tab切换等使用场景
 * @author zhangchen2397@126.com
 * ----------------------------------------------
 *
 * 对外调用接口及自定义事件
 *
 */

( function( root, factory ) {
    if ( typeof define === 'function' ) {
        define( 'slide', [ 'jqmobi' ], function( $ ) {
            return factory( root, $ );
        } );
    } else {
        root.slide = factory( root, root.$ );
    }
} )( window, function( root, $ ) {

    var slide = function( config ) {
        this.defaultConfig = {
            el: 'slide',
            speed: 3000,
            autoPlay: true,
            continuous: true,
            navClass: 'nav-list'
        };

        this.config = $.extend( this.defaultConfig, config || {} );

        this.init.call( this );
    };

    $.extend( slide.prototype, {
        init: function() {
            this._cache();
            this._createNav();
            this._actContinue();
            this._initEvent();
        },

        _cache: function() {
            var me = this,
                config = this.config;

            this.el = ( typeof config.el === 'string' ) ? $( '#' + config.el ) : config.el;
            this.imgUl = $( this.el.children()[ 0 ] );
            this.imgList = this.imgUl.find( 'li' );
            this.imgLen = this.imgList.length;

            this.baseWidth = this.el.width();
            this.endX = 0;
            this.curIdx = 1;
        },

        _createNav: function() {
            var me = this,
                config = this.config,
                tempHtml = '
';

            this.imgList.each( function( idx, el ) {
                idx = idx + 1;
                tempHtml += '
' + idx + '
';
            } );

            tempHtml += '
';

            this.el.append( tempHtml );

            this.navUl = this.el.find( 'ul.' + config.navClass );
            this.navList = this.navUl.find( 'li' );
        },

        _actContinue: function() {
            var me = this,
                config = this.config;

            if ( config.continuous ) {
                this.imgUl.append( $( this.imgList[ 0 ] ).clone() );
                this.imgUl.append( $( this.imgList[ this.imgLen - 1 ] ).clone() );
            }
        },

        _initEvent: function() {
            var me = this,
                imgUl = this.imgUl;

            imgUl.delegate( 'li', 'touchstart', $.proxy( this._startHandler, this ) );
            imgUl.delegate( 'li', 'touchmove', $.proxy( this._moveHandler, this ) );
            imgUl.delegate( 'li', 'touchend', $.proxy( this._endHandler, this ) );
        },

        _startHandler: function( event ) {
            var me = this,
                config = this.config,
                target = event.target;

            me.startTime = +new Date();
            me.startX = event.touches[ 0 ].pageX;
        },

        _moveHandler: function( event ) {
            var me = this,
                config = this.config;

            event.preventDefault();

            this.differX = event.targetTouches[ 0 ].pageX - me.startX;
            this.offsetX = this.differX + this.endX;

            this.imgUl.css( {
                webkitTransition: '-webkit-transform 0s',
                webkitTransform: 'translate3d(' + this.offsetX + 'px, 0, 0 )'
            } );
        },

        _endHandler: function() {
            var me = this,
                config = this.config;

            if ( this.differX > 0 ) {
                this.curIdx--;
            } else {
                this.curIdx++;
            }

            this.endX = -this.baseWidth * ( this.curIdx - 1 );

            this.imgUl.css( {
                webkitTransition: '-webkit-transform 0.3s',
                webkitTransform: 'translate3d(' + this.endX + 'px, 0, 0 )'
            } );
        },

        _testMerge: function() {
            console.log('haha');
            console.log('testMerge');
            console.log('test merge');
            console.log('modify by testMerge');
        },

        modifyByMaster: function() {
            console.log('master');
        },

        testMergeCustom: function() {
            console.log('testMerge');
        }
    } );

    return slide;
} );
<commit_msg>up by zc<commit_after>/**
 * 移动webapp开发滑动组件
 * 可应用于图片轮播、查看大图翻页、tab切换等使用场景
 * @author zhangchen2397@126.com
 * ----------------------------------------------
 *
 * 对外调用接口及自定义事件
 *
 */

( function( root, factory ) {
    if ( typeof define === 'function' ) {
        define( 'slide', [ 'jqmobi', 'zepto', 'event' ], function( $, Zepto, Event ) {
            return factory( root, $ );
        } );
    } else {
        root.slide = factory( root, root.$ );
    }
} )( window, function( root, $ ) {

    var slide = function( config ) {
        this.defaultConfig = {
            el: 'slide',
            speed: 3000,
            autoPlay: true,
            continuous: true,
            navClass: 'nav-list'
        };

        console.log(Zepto);

        this.config = $.extend( this.defaultConfig, config || {} );

        this.init.call( this );
    };

    $.extend( slide.prototype, {
        init: function() {
            this._cache();
            this._createNav();
            this._actContinue();
            this._initEvent();
        },

        _cache: function() {
            var me = this,
                config = this.config;

            this.el = ( typeof config.el === 'string' ) ? $( '#' + config.el ) : config.el;
            this.imgUl = $( this.el.children()[ 0 ] );
            this.imgList = this.imgUl.find( 'li' );
            this.imgLen = this.imgList.length;

            this.baseWidth = this.el.width();
            this.endX = 0;
            this.curIdx = 1;
        },

        _createNav: function() {
            var me = this,
                config = this.config,
                tempHtml = '
';

            this.imgList.each( function( idx, el ) {
                idx = idx + 1;
                tempHtml += '
' + idx + '
';
            } );

            tempHtml += '
';

            this.el.append( tempHtml );

            this.navUl = this.el.find( 'ul.' + config.navClass );
            this.navList = this.navUl.find( 'li' );
        },

        _actContinue: function() {
            var me = this,
                config = this.config;

            if ( config.continuous ) {
                this.imgUl.append( $( this.imgList[ 0 ] ).clone() );
                this.imgUl.append( $( this.imgList[ this.imgLen - 1 ] ).clone() );
            }
        },

        _initEvent: function() {
            var me = this,
                imgUl = this.imgUl;

            imgUl.delegate( 'li', 'touchstart', $.proxy( this._startHandler, this ) );
            imgUl.delegate( 'li', 'touchmove', $.proxy( this._moveHandler, this ) );
            imgUl.delegate( 'li', 'touchend', $.proxy( this._endHandler, this ) );
        },

        _startHandler: function( event ) {
            var me = this,
                config = this.config,
                target = event.target;

            me.startTime = +new Date();
            me.startX = event.touches[ 0 ].pageX;
        },

        _moveHandler: function( event ) {
            var me = this,
                config = this.config;

            event.preventDefault();

            this.differX = event.targetTouches[ 0 ].pageX - me.startX;
            this.offsetX = this.differX + this.endX;

            this.imgUl.css( {
                webkitTransition: '-webkit-transform 0s',
                webkitTransform: 'translate3d(' + this.offsetX + 'px, 0, 0 )'
            } );
        },

        _endHandler: function() {
            var me = this,
                config = this.config;

            if ( this.differX > 0 ) {
                this.curIdx--;
            } else {
                this.curIdx++;
            }

            this.endX = -this.baseWidth * ( this.curIdx - 1 );

            this.imgUl.css( {
                webkitTransition: '-webkit-transform 0.3s',
                webkitTransform: 'translate3d(' + this.endX + 'px, 0, 0 )'
            } );
        },

        _testMerge: function() {
            console.log('haha');
            console.log('testMerge');
            console.log('test merge');
            console.log('modify by testMerge');
        },

        modifyByMaster: function() {
            console.log('master');
        },

        testMergeCustom: function() {
            console.log('testMerge');
        }
    } );

    return slide;
} );
<|endoftext|>


<commit_before>(function() {
  'use strict';
  /*global require,phantom,console,setInterval,clearInterval */

  /**
   * Borrowed from https://github.com/ariya/phantomjs/blob/master/examples/waitfor.js
   *
   * Wait until the test condition is true or a timeout occurs. Useful for waiting
   * on a server response or for a ui change (fadeIn, etc.) to occur.
   *
   * @param testFx javascript condition that evaluates to a boolean,
   * it can be passed in as a string (e.g.: '1 == 1' or '$('#bar').is(':visible')' or
   * as a callback function.
   * @param onReady what to do when testFx condition is fulfilled,
   * it can be passed in as a string (e.g.: '1 == 1' or '$('#bar').is(':visible')' or
   * as a callback function.
   * @param timeOutMillis the max amount of time to wait. If not specified, 3 sec is used.
   */
  function waitFor(testFx, onReady, timeOutMillis) {
    var maxtimeOutMillis = timeOutMillis ? timeOutMillis : 3000, //< Default Max Timout is 3s
    start = new Date().getTime(),
    condition = false,
    interval = setInterval(function() {
      if ( (new Date().getTime() - start < maxtimeOutMillis) && !condition ) {
        // If not time-out yet and condition not yet fulfilled
        condition = testFx(); //< defensive code
      } else {
        if(!condition) {
          // If condition still not fulfilled (timeout but condition is 'false')
          console.log('"waitFor()" timeout');
          phantom.exit(1);
        } else {
          // Condition fulfilled (timeout and/or condition is 'true')
          console.log('"waitFor()" finished in ' + (new Date().getTime() - start) + 'ms.');
          onReady(); //< Do what it's supposed to do once the condition is fulfilled
          clearInterval(interval); //< Stop this interval
        }
      }
    }, 10000); //< repeat check every 10s
  }

  var poster = require('webpage').create(),
  system = require('system'), fs = require('fs');

  var processedCount = 0;

  poster.onResourceError = function(resourceError) {
    console.log('Error submit data to ' + resourceError.url + ', error ' + resourceError.errorCode + ': ' + resourceError.errorString);
  };

  function post2Influx(country, site, version, loadTime, failed, size) {
    var settings = {
      operation: 'POST',
      encoding: 'utf8',
      headers: {
        'Content-Type': 'application/x-www-form-urlencoded'
      }
    };
    var ts = Date.now();
    var tags = 'country='+country+',site='+site+',version='+version;
    var load_time_str = 'load_time,'+tags+' value=' +loadTime +' ' + ts;
    var fail_count_str = 'fail_count,'+tags+' value=' + (failed?1:0) +' ' + ts;
    var size_str = 'size,'+tags+' value=' + size +' ' + ts;
    settings.data = load_time_str + '\n' + fail_count_str + '\n' + size_str;
    poster.open('http://influx.getlantern.org:8086/write?db=sonar', settings, function(postStatus) {
    });
  }

  function postData(country, site, version, loadTime, failed, size) {
    var instance = country + '-' + site + '-' + version;
    var settings = {
      operation: 'POST',
      encoding: 'utf8',
      headers: {
        'Content-Type': 'application/json'
      },
      data: JSON.stringify({
        dims: {
          sonar: instance
        },
        increments: { failCount : failed ? 1 : 0, totalSize: size },
        gauges: { loadTime: loadTime}
      })
    };

    poster.open('http://pure-journey-3547.herokuapp.com/stats/' + instance, settings, function(postStatus) {
      console.log('Submit to statshub: ' + postStatus + ', data = ' + settings.data);
      processedCount = processedCount + 1;
      post2Influx(country, site, version, loadTime, failed, size);
    });
  }

  function process(country, site, url, version) {
    var page = require('webpage').create();
    var size = 0;

    page.onResourceError = function(resourceError) {
      console.log('Error load resource #' + resourceError.id + ', ' + resourceError.url + ', error ' + resourceError.errorCode + ': ' + resourceError.errorString);
      page.stop();
      processedCount = processedCount + 1;
    };
    page.onResourceReceived = function(response) {
      if (response.bodySize !== undefined) {
        size = size + response.bodySize;
      }
    };
    var started = Date.now();
    page.onLoadFinished = function (status) {
      var loadTime = Date.now() - started;
      console.log(site + ' (' + url + '): ' + status + ', takes ' + loadTime + 'ms, size = ' + size + ' bytes');
      postData(country, site, version, loadTime, status === 'fail', size);
    };
    page.open(url);
  }

  function main(version, sleepMinutes) {
    var content = fs.read('config.json');
    var config = JSON.parse(content);
    var country = config.country;
    if (country === undefined) {
      console.log('Please specify country in config');
      phantom.exit(1);
    }
    for (var i in config.sites) {
      process(country, config.sites[i][0], config.sites[i][1], version);
    }
    if (sleepMinutes > 0) {
      console.log('Sleep ' +  sleepMinutes + ' minutes before next round');
      setTimeout(function() {main(version, sleepMinutes); }, sleepMinutes*60*1000);
    } else {
      waitFor(function() {
        return processedCount === config.sites.length;
      }, function() { phantom.exit(); },
      4*60*1000); // wait for at most 4 min
    }

  }

  if (system.args.length < 2) {
    console.log('Usage: sonar.js <lantern version> [sleep minutes before next round]');
    phantom.exit(1);
  } else {
    var version = system.args[1];
    var sleepMinutes = 0;
    if (system.args.length === 3) {
      sleepMinutes = parseInt(system.args[2], 10);
    }

    main(version, sleepMinutes);
  }

}());
<commit_msg>use nanosecond<commit_after>(function() {
  'use strict';
  /*global require,phantom,console,setInterval,clearInterval */

  /**
   * Borrowed from https://github.com/ariya/phantomjs/blob/master/examples/waitfor.js
   *
   * Wait until the test condition is true or a timeout occurs. Useful for waiting
   * on a server response or for a ui change (fadeIn, etc.) to occur.
   *
   * @param testFx javascript condition that evaluates to a boolean,
   * it can be passed in as a string (e.g.: '1 == 1' or '$('#bar').is(':visible')' or
   * as a callback function.
   * @param onReady what to do when testFx condition is fulfilled,
   * it can be passed in as a string (e.g.: '1 == 1' or '$('#bar').is(':visible')' or
   * as a callback function.
   * @param timeOutMillis the max amount of time to wait. If not specified, 3 sec is used.
   */
  function waitFor(testFx, onReady, timeOutMillis) {
    var maxtimeOutMillis = timeOutMillis ? timeOutMillis : 3000, //< Default Max Timout is 3s
    start = new Date().getTime(),
    condition = false,
    interval = setInterval(function() {
      if ( (new Date().getTime() - start < maxtimeOutMillis) && !condition ) {
        // If not time-out yet and condition not yet fulfilled
        condition = testFx(); //< defensive code
      } else {
        if(!condition) {
          // If condition still not fulfilled (timeout but condition is 'false')
          console.log('"waitFor()" timeout');
          phantom.exit(1);
        } else {
          // Condition fulfilled (timeout and/or condition is 'true')
          console.log('"waitFor()" finished in ' + (new Date().getTime() - start) + 'ms.');
          onReady(); //< Do what it's supposed to do once the condition is fulfilled
          clearInterval(interval); //< Stop this interval
        }
      }
    }, 10000); //< repeat check every 10s
  }

  var poster = require('webpage').create(),
  system = require('system'), fs = require('fs');

  var processedCount = 0;

  poster.onResourceError = function(resourceError) {
    console.log('Error submit data to ' + resourceError.url + ', error ' + resourceError.errorCode + ': ' + resourceError.errorString);
  };

  function post2Influx(country, site, version, loadTime, failed, size) {
    var settings = {
      operation: 'POST',
      encoding: 'utf8',
      headers: {
        'Content-Type': 'application/x-www-form-urlencoded'
      }
    };
    var ts = Date.now()*1000000; // nanosecond
    var tags = 'country='+country+',site='+site+',version='+version;
    var load_time_str = 'load_time,'+tags+' value=' +loadTime +' ' + ts;
    var fail_count_str = 'fail_count,'+tags+' value=' + (failed?1:0) +' ' + ts;
    var size_str = 'size,'+tags+' value=' + size +' ' + ts;
    settings.data = load_time_str + '\n' + fail_count_str + '\n' + size_str;
    poster.open('http://influx.getlantern.org:8086/write?db=sonar', settings, function(postStatus) {
    });
  }

  function postData(country, site, version, loadTime, failed, size) {
    var instance = country + '-' + site + '-' + version;
    var settings = {
      operation: 'POST',
      encoding: 'utf8',
      headers: {
        'Content-Type': 'application/json'
      },
      data: JSON.stringify({
        dims: {
          sonar: instance
        },
        increments: { failCount : failed ? 1 : 0, totalSize: size },
        gauges: { loadTime: loadTime}
      })
    };

    poster.open('http://pure-journey-3547.herokuapp.com/stats/' + instance, settings, function(postStatus) {
      console.log('Submit to statshub: ' + postStatus + ', data = ' + settings.data);
      processedCount = processedCount + 1;
      post2Influx(country, site, version, loadTime, failed, size);
    });
  }

  function process(country, site, url, version) {
    var page = require('webpage').create();
    var size = 0;

    page.onResourceError = function(resourceError) {
      console.log('Error load resource #' + resourceError.id + ', ' + resourceError.url + ', error ' + resourceError.errorCode + ': ' + resourceError.errorString);
      page.stop();
      processedCount = processedCount + 1;
    };
    page.onResourceReceived = function(response) {
      if (response.bodySize !== undefined) {
        size = size + response.bodySize;
      }
    };
    var started = Date.now();
    page.onLoadFinished = function (status) {
      var loadTime = Date.now() - started;
      console.log(site + ' (' + url + '): ' + status + ', takes ' + loadTime + 'ms, size = ' + size + ' bytes');
      postData(country, site, version, loadTime, status === 'fail', size);
    };
    page.open(url);
  }

  function main(version, sleepMinutes) {
    var content = fs.read('config.json');
    var config = JSON.parse(content);
    var country = config.country;
    if (country === undefined) {
      console.log('Please specify country in config');
      phantom.exit(1);
    }
    for (var i in config.sites) {
      process(country, config.sites[i][0], config.sites[i][1], version);
    }
    if (sleepMinutes > 0) {
      console.log('Sleep ' +  sleepMinutes + ' minutes before next round');
      setTimeout(function() {main(version, sleepMinutes); }, sleepMinutes*60*1000);
    } else {
      waitFor(function() {
        return processedCount === config.sites.length;
      }, function() { phantom.exit(); },
      4*60*1000); // wait for at most 4 min
    }

  }

  if (system.args.length < 2) {
    console.log('Usage: sonar.js <lantern version> [sleep minutes before next round]');
    phantom.exit(1);
  } else {
    var version = system.args[1];
    var sleepMinutes = 0;
    if (system.args.length === 3) {
      sleepMinutes = parseInt(system.args[2], 10);
    }

    main(version, sleepMinutes);
  }

}());
<|endoftext|>


<commit_before>/*
 * sorts.js - a variety of sorting algorithms and other functions
 */

// assert - this function will wrap the above assert method and
// remove the need to pass in a string, instead, the expression
// that is given will be used as the message passed into assert.
function assert(expression) {
    assert(expression, '');
}

// assert - this function will add support for assertions. Two items
// can be passed in and their equality will be tested. If they are
// evaluated to be equal, then the function will silently return,
// otherwise an error will be thrown using the message given in the
// second argument.
//
// This function will add support for assertions so that I
// can actually do quick inline testing of my code to see that certain
// properties hold during execution and that the outputs of functions
// are what I expect.
//
// Example:
// assert(4<5, "comparing 4 and 5");
// > undefined
// assert(4>5, "comparing 4 and 5");
// > Error: Assertion Violated: comparing 4 and 5
function assert(expression, message) {
    if(!expression) {
        var errorMessage = '';
        if(message) {
            errorMessage = "Assertion Violated: " + message;
        }
        else {
            errorMessage = "Assertion Violated";
        }

        throw new Error(errorMessage);
    }
}

// getMax - this function will take an array of integers and find
// the max value in that array.
//
// Example:
// getMax([1,2,3])
// > 3
function getMax(arr) {
    // if the array is empty, return undefined
    if(arr.length == 0) {
        return undefined;
    }
    var max = arr[0];
    for(var i = 0; i < arr.length; i++) {
        if(max < arr[i]) {
            max = arr[i];
        }
    }

    return max;
}

// testing out getMax()
assert(getMax([1,2,3]) == 3, "using getMax");
assert(getMax([5,2,4]) == 5, "using getMax");
assert(getMax([-1,-2,-3]) == -1, "using getMax");
assert(getMax([10]) == 10);
assert(getMax([]) == undefined);

// getMin - this function will take an array of integers and find
// the min value in that array.
//
// Example:
// getMin([1,2,3])
// > 1
function getMin(arr) {
    // if the array is empty, return undefined
    if(arr.length == 0) {
        return undefined;
    }
    var min = arr[0];
    for(var i = 0; i < arr.length; i++) {
        if(min > arr[i]) {
            min = arr[i];
        }
    }

    return min;
}

// testing out getMin()
assert(getMin([1,2,3]) == 1);
assert(getMin([5,2,4]) == 2);
assert(getMin([-1,-2,-3]) == -3);
assert(getMin([10]) == 10);
assert(getMin([]) == undefined);

// compareArrays - given two arrays of integers, this function will
// first compare the length of the arrays. If the lengths differ, then
// false is immediately returned, otherwise the function will then
// check the equality of each integer in the array. If the all match,
// then true is returned. If even one does not match, then false is
// returned.
//
// Examples:
// compareArrays([1,2,3],[1,2,3])
// > true
// compareArrays([1,2,3],[1,2,4])
// > false
function compareArrays(arr1, arr2) {
    if(arr1.length != arr2.length) {
        return false;
    }

    for(var i = 0; i < arr1.length; i++) {
        if(arr1[i] != arr2[i]) {
            return false;
        }
    }
    return true;
}

// selectionSort - this function will take an array of integers and
// sort it using the selection sort algorithm. The resulting sorted
// list will be returned.
//
// Pseudocode:
// create a sortedArray that is empty
// while the length of the given array is greater than 0
//  grab the first item in the array and assign to min
//  the minIndex is assigned to 0
//  for each subsequent item in the given array
//      if min is greater than the curr item
//          then min is assigned to be the curr item
//          minIndex is assigned to the index of curr item
//  splice/remove minIndex from the given array
//  push/append the min value onto the sortedArray
// return the sorted array
//
// Example:
// selectionSort([3,2,1])
// > [1,2,3]
function selectionSort(arr) {
    var sortedArr = [];
    while(arr.length > 0) {
        var min = arr[0],
            minIndex = 0;
        for(var i = 1; i < arr.length; i++) {
            if(min > arr[i]) {
                min = arr[i];
                minIndex = i;
            }
        }
        arr.splice(minIndex, 1);
        sortedArr.push(min);
    }
    return sortedArr;
}

// testing out selectionSort()
assert(compareArrays(selectionSort([1,2,3]),[1,2,3]));
assert(compareArrays(selectionSort([3,2,1]),[1,2,3]));
assert(compareArrays(selectionSort([]),[]));
assert(compareArrays(selectionSort([5]),[5]));

// insertionSort - this function sorts a given array based on the
// insertion sort algorithm.
//
// Pseudocode:
//
function insertionSort(arr) {
    for(var i = 1; i < arr.length; i++) {
        var val = arr[i],
            holePos = i;
        while(holePos > 0 && val < arr[holePos - 1]) {
            arr[holePos] = arr[holePos - 1];
            holePos = holePos - 1;
        }
        arr[holePos] = val;
    }
    return arr;
}

// testing out insertionSort()
assert(compareArrays(insertionSort([1,2,3]),[1,2,3]));
assert(compareArrays(insertionSort([3,2,1]),[1,2,3]));
assert(compareArrays(insertionSort([]),[]));
assert(compareArrays(insertionSort([5]),[5]));

// quickSort - this function sorts a given array based on the Quick
// Sort algorithm.
//
// Pseudocode:
// if the length of array is <= 1
//  then return the array
// Splice the arr.length/2 item from the array, put in pivot
// for each remaining item in arr
//  if item is <= pivot then add to less
//  if item is > pivot then add to more
// concatenate quickSort(less), pivot, quickSort(more)
// return the concatenated list
function quickSort(arr) {
    if(arr.length <= 1) {
        return arr;
    }

    var pivot = arr.splice(arr.length/2, 1)[0],
        less = [],
        more = [];
    for(var i = 0; i < arr.length; i++) {
        if(arr[i] <= pivot) {
            less.push(arr[i]);
        }
        else {
            // it is greater
            more.push(arr[i]);
        }
    }
    var sortedArr = quickSort(less).concat(pivot, quickSort(more));
    return sortedArr;
}

// testing out quickSort()
assert(compareArrays(quickSort([]),[]));
assert(compareArrays(quickSort([11]),[11]));
assert(compareArrays(quickSort([9,8]),[8,9]));
assert(compareArrays(quickSort([1,2,3]),[1,2,3]));
assert(compareArrays(quickSort([3,2,1]),[1,2,3]));
assert(compareArrays(quickSort([6,4,5,7,4,2,3,5,7,9,6,4,2,1,2,3,11,12,9,7,13,6]),
            [ 1, 2, 2, 2, 3, 3, 4, 4, 4, 5, 5, 6, 6, 6, 7, 7, 7, 9, 9, 11, 12, 13 ]));

// mergeSort - this function sorts a given array based on the Merge
// Sort algorithm.
//
// Pseudocode:
// if the length of array is <= 1
//  then return the array
// create a list for the left and right side and get the middle value
// which is half the length of the array
// Partition the arr such that:
//  Everything before the middle index goes in left
//  Everything at or after the middle index goes in right
// Call mergeSort on the left array
// Call mergeSort on the right array
// Call merge on the left and right
// Return the resulting merged array
function mergeSort(arr) {
    if(arr.length <= 1) {
        return arr;
    }
    var middle = arr.length/2,
        left = arr.slice(0,middle),
        right = arr.slice(middle,arr.length);
    left = mergeSort(left);
    right = mergeSort(right);
    return merge(left,right);
}

// merge: Array<Integer>:left Array<Integer>:right -> Array<Integer>:result
// this function merges two given arrays for the mergeSort
// function and returns the resulting merged array.
//
// Pseudocode:
// while the length of left > 0 OR length of right > 0
//  if the length of left > 0 AND length of right > 0 then
//      if left[0] <= right[0] then
//          splice the value at left[0]
//          append the spliced value to result
//      else
//          splice the value at right[0]
//          append the spliced value to result
//  else if length of left > 0 then
//      splice the value at left[0]
//      append the spliced value to result
//  else if length of right > 0 then
//      splice the value at right[0]
//      append the spliced value to result
// return result
function merge(left,right) {
    var result = [];
    while(left.length > 0 || right.length > 0) {
        if(left.length > 0 && right.length > 0) {
            if(left[0] <= right[0]) {
                result.push(left.splice(0,1)[0]);
            }
            else {
                result.push(right.splice(0,1)[0]);
            }
        }
        else if(left.length > 0) {
            result.push(left.splice(0,1)[0]);
        }
        else if(right.length > 0) {
            result.push(right.splice(0,1)[0]);
        }
    }
    return result;
}

// testing out mergeSort()
assert(compareArrays(mergeSort([]),[]));
assert(compareArrays(mergeSort([11]),[11]));
assert(compareArrays(mergeSort([9,8]),[8,9]));
assert(compareArrays(mergeSort([1,2,3]),[1,2,3]));
assert(compareArrays(mergeSort([3,2,1]),[1,2,3]));
assert(compareArrays(mergeSort([6,4,5,7,4,2,3,5,7,9,6,4,2,1,2,3,11,12,9,7,13,6]),
            [ 1, 2, 2, 2, 3, 3, 4, 4, 4, 5, 5, 6, 6, 6, 7, 7, 7, 9, 9, 11, 12, 13 ]));

// testing sorting against itself
assert(compareArrays(insertionSort([4,5,9,2,7,1,1,1,6]), selectionSort([4,5,9,2,7,1,1,1,6])));

<commit_msg>Removing the duplicate assert method, apparently JavaScript doesn't support method overloading.<commit_after>/*
 * sorts.js - a variety of sorting algorithms and other functions
 */

// assert - this function will add support for assertions. Two items
// can be passed in and their equality will be tested. If they are
// evaluated to be equal, then the function will silently return,
// otherwise an error will be thrown using the message given in the
// second argument.
//
// This function will add support for assertions so that I
// can actually do quick inline testing of my code to see that certain
// properties hold during execution and that the outputs of functions
// are what I expect.
//
// Example:
// assert(4<5, "comparing 4 and 5");
// > undefined
// assert(4>5, "comparing 4 and 5");
// > Error: Assertion Violated: comparing 4 and 5
function assert(expression, message) {
    if(!expression) {
        var errorMessage = '';
        if(message) {
            errorMessage = "Assertion Violated: " + message;
        }
        else {
            errorMessage = "Assertion Violated";
        }

        throw new Error(errorMessage);
    }
}

// getMax - this function will take an array of integers and find
// the max value in that array.
//
// Example:
// getMax([1,2,3])
// > 3
function getMax(arr) {
    // if the array is empty, return undefined
    if(arr.length == 0) {
        return undefined;
    }
    var max = arr[0];
    for(var i = 0; i < arr.length; i++) {
        if(max < arr[i]) {
            max = arr[i];
        }
    }

    return max;
}

// testing out getMax()
assert(getMax([1,2,3]) == 3, "using getMax");
assert(getMax([5,2,4]) == 5, "using getMax");
assert(getMax([-1,-2,-3]) == -1, "using getMax");
assert(getMax([10]) == 10);
assert(getMax([]) == undefined);

// getMin - this function will take an array of integers and find
// the min value in that array.
//
// Example:
// getMin([1,2,3])
// > 1
function getMin(arr) {
    // if the array is empty, return undefined
    if(arr.length == 0) {
        return undefined;
    }
    var min = arr[0];
    for(var i = 0; i < arr.length; i++) {
        if(min > arr[i]) {
            min = arr[i];
        }
    }

    return min;
}

// testing out getMin()
assert(getMin([1,2,3]) == 1);
assert(getMin([5,2,4]) == 2);
assert(getMin([-1,-2,-3]) == -3);
assert(getMin([10]) == 10);
assert(getMin([]) == undefined);

// compareArrays - given two arrays of integers, this function will
// first compare the length of the arrays. If the lengths differ, then
// false is immediately returned, otherwise the function will then
// check the equality of each integer in the array. If the all match,
// then true is returned. If even one does not match, then false is
// returned.
//
// Examples:
// compareArrays([1,2,3],[1,2,3])
// > true
// compareArrays([1,2,3],[1,2,4])
// > false
function compareArrays(arr1, arr2) {
    if(arr1.length != arr2.length) {
        return false;
    }

    for(var i = 0; i < arr1.length; i++) {
        if(arr1[i] != arr2[i]) {
            return false;
        }
    }
    return true;
}

// selectionSort - this function will take an array of integers and
// sort it using the selection sort algorithm. The resulting sorted
// list will be returned.
//
// Pseudocode:
// create a sortedArray that is empty
// while the length of the given array is greater than 0
//  grab the first item in the array and assign to min
//  the minIndex is assigned to 0
//  for each subsequent item in the given array
//      if min is greater than the curr item
//          then min is assigned to be the curr item
//          minIndex is assigned to the index of curr item
//  splice/remove minIndex from the given array
//  push/append the min value onto the sortedArray
// return the sorted array
//
// Example:
// selectionSort([3,2,1])
// > [1,2,3]
function selectionSort(arr) {
    var sortedArr = [];
    while(arr.length > 0) {
        var min = arr[0],
            minIndex = 0;
        for(var i = 1; i < arr.length; i++) {
            if(min > arr[i]) {
                min = arr[i];
                minIndex = i;
            }
        }
        arr.splice(minIndex, 1);
        sortedArr.push(min);
    }
    return sortedArr;
}

// testing out selectionSort()
assert(compareArrays(selectionSort([1,2,3]),[1,2,3]));
assert(compareArrays(selectionSort([3,2,1]),[1,2,3]));
assert(compareArrays(selectionSort([]),[]));
assert(compareArrays(selectionSort([5]),[5]));

// insertionSort - this function sorts a given array based on the
// insertion sort algorithm.
//
// Pseudocode:
//
function insertionSort(arr) {
    for(var i = 1; i < arr.length; i++) {
        var val = arr[i],
            holePos = i;
        while(holePos > 0 && val < arr[holePos - 1]) {
            arr[holePos] = arr[holePos - 1];
            holePos = holePos - 1;
        }
        arr[holePos] = val;
    }
    return arr;
}

// testing out insertionSort()
assert(compareArrays(insertionSort([1,2,3]),[1,2,3]));
assert(compareArrays(insertionSort([3,2,1]),[1,2,3]));
assert(compareArrays(insertionSort([]),[]));
assert(compareArrays(insertionSort([5]),[5]));

// quickSort - this function sorts a given array based on the Quick
// Sort algorithm.
//
// Pseudocode:
// if the length of array is <= 1
//  then return the array
// Splice the arr.length/2 item from the array, put in pivot
// for each remaining item in arr
//  if item is <= pivot then add to less
//  if item is > pivot then add to more
// concatenate quickSort(less), pivot, quickSort(more)
// return the concatenated list
function quickSort(arr) {
    if(arr.length <= 1) {
        return arr;
    }

    var pivot = arr.splice(arr.length/2, 1)[0],
        less = [],
        more = [];
    for(var i = 0; i < arr.length; i++) {
        if(arr[i] <= pivot) {
            less.push(arr[i]);
        }
        else {
            // it is greater
            more.push(arr[i]);
        }
    }
    var sortedArr = quickSort(less).concat(pivot, quickSort(more));
    return sortedArr;
}

// testing out quickSort()
assert(compareArrays(quickSort([]),[]));
assert(compareArrays(quickSort([11]),[11]));
assert(compareArrays(quickSort([9,8]),[8,9]));
assert(compareArrays(quickSort([1,2,3]),[1,2,3]));
assert(compareArrays(quickSort([3,2,1]),[1,2,3]));
assert(compareArrays(quickSort([6,4,5,7,4,2,3,5,7,9,6,4,2,1,2,3,11,12,9,7,13,6]),
            [ 1, 2, 2, 2, 3, 3, 4, 4, 4, 5, 5, 6, 6, 6, 7, 7, 7, 9, 9, 11, 12, 13 ]));

// mergeSort - this function sorts a given array based on the Merge
// Sort algorithm.
//
// Pseudocode:
// if the length of array is <= 1
//  then return the array
// create a list for the left and right side and get the middle value
// which is half the length of the array
// Partition the arr such that:
//  Everything before the middle index goes in left
//  Everything at or after the middle index goes in right
// Call mergeSort on the left array
// Call mergeSort on the right array
// Call merge on the left and right
// Return the resulting merged array
function mergeSort(arr) {
    if(arr.length <= 1) {
        return arr;
    }
    var middle = arr.length/2,
        left = arr.slice(0,middle),
        right = arr.slice(middle,arr.length);
    left = mergeSort(left);
    right = mergeSort(right);
    return merge(left,right);
}

// merge: Array<Integer>:left Array<Integer>:right -> Array<Integer>:result
// this function merges two given arrays for the mergeSort
// function and returns the resulting merged array.
//
// Pseudocode:
// while the length of left > 0 OR length of right > 0
//  if the length of left > 0 AND length of right > 0 then
//      if left[0] <= right[0] then
//          splice the value at left[0]
//          append the spliced value to result
//      else
//          splice the value at right[0]
//          append the spliced value to result
//  else if length of left > 0 then
//      splice the value at left[0]
//      append the spliced value to result
//  else if length of right > 0 then
//      splice the value at right[0]
//      append the spliced value to result
// return result
function merge(left,right) {
    var result = [];
    while(left.length > 0 || right.length > 0) {
        if(left.length > 0 && right.length > 0) {
            if(left[0] <= right[0]) {
                result.push(left.splice(0,1)[0]);
            }
            else {
                result.push(right.splice(0,1)[0]);
            }
        }
        else if(left.length > 0) {
            result.push(left.splice(0,1)[0]);
        }
        else if(right.length > 0) {
            result.push(right.splice(0,1)[0]);
        }
    }
    return result;
}

// testing out mergeSort()
assert(compareArrays(mergeSort([]),[]));
assert(compareArrays(mergeSort([11]),[11]));
assert(compareArrays(mergeSort([9,8]),[8,9]));
assert(compareArrays(mergeSort([1,2,3]),[1,2,3]));
assert(compareArrays(mergeSort([3,2,1]),[1,2,3]));
assert(compareArrays(mergeSort([6,4,5,7,4,2,3,5,7,9,6,4,2,1,2,3,11,12,9,7,13,6]),
            [ 1, 2, 2, 2, 3, 3, 4, 4, 4, 5, 5, 6, 6, 6, 7, 7, 7, 9, 9, 11, 12, 13 ]));

// testing sorting against itself
assert(compareArrays(insertionSort([4,5,9,2,7,1,1,1,6]), selectionSort([4,5,9,2,7,1,1,1,6])));

<|endoftext|>


<commit_before>'use strict';

var { NativeModules, NativeEventEmitter } = require('react-native');
var RNSound = NativeModules.RNSound;
var IsAndroid = RNSound.IsAndroid;
var IsWindows = RNSound.IsWindows;
var resolveAssetSource = require("react-native/Libraries/Image/resolveAssetSource");
var nextKey = 0;

var eventEmitter = new NativeEventEmitter(RNSound);

function isRelativePath(path) {
  return !/^(\/|http(s?)|asset)/.test(path);
}

function Sound(filename, basePath, onError, options) {
  var asset = resolveAssetSource(filename);
  if (asset) {
    this._filename = asset.uri;
    onError = basePath;
  } else {
    this._filename = basePath ? basePath + '/' + filename : filename;

    if (IsAndroid && !basePath && isRelativePath(filename)) {
      this._filename = filename.toLowerCase().replace(/\.[^.]+$/, '');
    }
  }

  this.registerOnPlay = function() {
    if (this.onPlaySubscription != null) {
      console.warn('On Play change event listener is already registered');
      return;
    }

    if (!IsWindows) {
      this.onPlaySubscription = eventEmitter.addListener(
        'onPlayChange',
        (param) => {
          const { isPlaying, playerKey } = param;
          if (playerKey === this._key) {
            if (isPlaying) {
              this._playing = true;
            }
            else {
              this._playing = false;
            }
          }
        },
      );
    }
  }

  this._loaded = false;
  this._playing = false;
  this._key = nextKey++;
  this._duration = -1;
  this._numberOfChannels = -1;
  this._volume = 1;
  this._pan = 0;
  this._numberOfLoops = 0;
  this._speed = 1;
  RNSound.prepare(this._filename, this._key, options || {}, (error, props) => {
    if (props) {
      if (typeof props.duration === 'number') {
        this._duration = props.duration;
      }
      if (typeof props.numberOfChannels === 'number') {
        this._numberOfChannels = props.numberOfChannels;
      }
    }
    if (error === null) {
      this._loaded = true;
      this.registerOnPlay();
    }
    onError && onError(error, props);
  });
}

Sound.prototype.isLoaded = function() {
  return this._loaded;
};

Sound.prototype.play = function(onEnd) {
  if (this._loaded) {
    RNSound.play(this._key, (successfully) => onEnd && onEnd(successfully));
    if (IsAndroid) {
      // For Android
      // Manually call native setSpeed() after native play() to apply current speed.
      // Native setSpeed method should be called only if the media player is already playing.
      // To prevent android from playing automatically when setSpeed is called.
      RNSound.setSpeed(this._key, this._speed);
    }
  }
  else {
    onEnd && onEnd(false);
  }
  return this;
};

Sound.prototype.pause = function(callback) {
  if (this._loaded) {
    RNSound.pause(this._key, () => {
      this._playing = false;
      callback && callback();
    });
  }
  return this;
};

Sound.prototype.stop = function(callback) {
  if (this._loaded) {
    RNSound.stop(this._key, () => {
      this._playing = false;
      callback && callback();
    });
  }
  return this;
};

Sound.prototype.setStreamType = function(streamType, callback) {
  // For android only.
  // reset() is required before setStreamType
  // sets streamType, and prepare again.
  if (streamType == null) {
    const error = new Error('invalid streamType');
    throw error;
    callback && callback(error);
    return this;
  }
  if (!IsAndroid) {
    const error = new Error('setStreamType is only supported in Android');
    throw error;
    callback && callback(error);
    return this;
  }
  if (this._loaded) {
    const error = new Error('reset() is required If player is already prepared.');
    throw error;
    callback && callback(error);
    return this;
  }
  RNSound.prepare(this._filename, this._key, { audioStreamType: streamType } || {}, (error, props) => {
    if (props) {
      if (typeof props.duration === 'number') {
        this._duration = props.duration;
      }
      if (typeof props.numberOfChannels === 'number') {
        this._numberOfChannels = props.numberOfChannels;
      }
    }
    if (error === null) {
      this._loaded = true;
      callback && callback();
    } else {
      callback && callback(error);
    }
  });
}

Sound.prototype.reset = function() {
  if (this._loaded && IsAndroid) {
    RNSound.reset(this._key);
    this._playing = false;
    this._loaded = false;
  }
  return this;
};

Sound.prototype.release = function() {
  if (this._loaded) {
    RNSound.release(this._key);
    this._loaded = false;
    if (!IsWindows) {
      if (this.onPlaySubscription != null) {
        this.onPlaySubscription.remove();
        this.onPlaySubscription = null;
      }
    }
  }
  return this;
};

Sound.prototype.getDuration = function() {
  return this._duration;
};

Sound.prototype.getNumberOfChannels = function() {
  return this._numberOfChannels;
};

Sound.prototype.getVolume = function() {
  return this._volume;
};

Sound.prototype.setVolume = function(value) {
  this._volume = value;
  if (this._loaded) {
    if (IsAndroid || IsWindows) {
      RNSound.setVolume(this._key, value, value);
    } else {
      RNSound.setVolume(this._key, value);
    }
  }
  return this;
};

Sound.prototype.getSystemVolume = function(callback) {
  if (IsAndroid) {
    RNSound.getSystemVolume(callback);
  }
  return this;
};

Sound.prototype.setSystemVolume = function(value) {
  if (IsAndroid) {
    RNSound.setSystemVolume(value);
  }
  return this;
};

Sound.prototype.getPan = function() {
  return this._pan;
};

Sound.prototype.setPan = function(value) {
  if (this._loaded) {
    RNSound.setPan(this._key, this._pan = value);
  }
  return this;
};

Sound.prototype.getNumberOfLoops = function() {
  return this._numberOfLoops;
};

Sound.prototype.setNumberOfLoops = function(value) {
  this._numberOfLoops = value;
  if (this._loaded) {
    if (IsAndroid || IsWindows) {
      RNSound.setLooping(this._key, !!value);
    } else {
      RNSound.setNumberOfLoops(this._key, value);
    }
  }
  return this;
};

Sound.prototype.setSpeed = function(value) {
  this._speed = value;
  if (this._loaded) {
    if (!IsWindows && !IsAndroid) {
      RNSound.setSpeed(this._key, value);
    } else if (IsAndroid) {
      // For Android
      // Call native setSpeed method only if the media player is already playing.
      // To prevent android from playing automatically when setSpeed is called.
      if (this._playing) {
        RNSound.setSpeed(this._key, value);
      }
    }
  }
  return this;
};

Sound.prototype.getCurrentTime = function(callback) {
  if (this._loaded) {
    RNSound.getCurrentTime(this._key, callback);
  }
};

Sound.prototype.setCurrentTime = function(value) {
  if (this._loaded) {
    RNSound.setCurrentTime(this._key, value);
  }
  return this;
};

// android only
Sound.prototype.setSpeakerphoneOn = function(value) {
  if (IsAndroid) {
    RNSound.setSpeakerphoneOn(this._key, value);
  } else if (!IsAndroid && !IsWindows) {
    RNSound.setSpeakerphoneOn(value);
  }
};

Sound.prototype.isPlaying = function() {
  return this._playing;
};

Sound.adjustStreamVolume = function(streamType, direction, flags) {
  if (IsAndroid) {
    RNSound.adjustStreamVolume(streamType, direction, flags);
  }
}

Sound.setStreamMute = function(streamType, state) {
  if (IsAndroid) {
    RNSound.setStreamMute(streamType, state);
  }
}

Sound.isHeadsetPlugged = function() {
  return RNSound.isHeadsetPlugged();
};

Sound.registerHeadsetPlugChangeListener = function(headsetPluggedInListener) {
  // console.log('Register headset plug change event listener');
  if (this.headsetPluggedInSubscription != null) {
    console.warn('Headset plug change event listener is already registered');
    return;
  }

  if (!IsWindows) {
    // Remove route change listener first
    RNSound.addRouteChangeListener();
    this.headsetPluggedInSubscription = eventEmitter.addListener(
      'RouteChange',
      headsetPluggedInListener,
    );
  }
};

Sound.unregisterHeadsetPlugChangeListener = function() {
  // console.log('Unregister headset plug change event listener');
  if (!IsWindows) {
    if (this.headsetPluggedInSubscription != null) {
      RNSound.removeRouteChangeListener();
      this.headsetPluggedInSubscription.remove();
      this.headsetPluggedInSubscription = null;
    }
  }
}

// ios only

// This is deprecated.  Call the static one instead.

Sound.prototype.setCategory = function(value) {
  Sound.setCategory(value, false);
}

Sound.enable = function(enabled) {
  RNSound.enable(enabled);
};

Sound.enableInSilenceMode = function(enabled) {
  if (!IsAndroid && !IsWindows) {
    RNSound.enableInSilenceMode(enabled);
  }
};

Sound.setActive = function(value) {
  if (!IsAndroid && !IsWindows) {
    RNSound.setActive(value);
  }
};

Sound.setCategory = function(value, mixWithOthers = false, allowBluetooth = false) {
  if (!IsAndroid && !IsWindows) {
    RNSound.setCategory(value, mixWithOthers, allowBluetooth);
  }
};

Sound.setMode = function(value) {
  if (!IsAndroid && !IsWindows) {
    RNSound.setMode(value);
  }
};

Sound.MAIN_BUNDLE = RNSound.MainBundlePath;
Sound.DOCUMENT = RNSound.NSDocumentDirectory;
Sound.LIBRARY = RNSound.NSLibraryDirectory;
Sound.CACHES = RNSound.NSCachesDirectory;

module.exports = Sound;
<commit_msg>Make setSpeakerphoneOn be static method for iOS (#41)<commit_after>'use strict';

var { NativeModules, NativeEventEmitter } = require('react-native');
var RNSound = NativeModules.RNSound;
var IsAndroid = RNSound.IsAndroid;
var IsWindows = RNSound.IsWindows;
var resolveAssetSource = require("react-native/Libraries/Image/resolveAssetSource");
var nextKey = 0;

var eventEmitter = new NativeEventEmitter(RNSound);

function isRelativePath(path) {
  return !/^(\/|http(s?)|asset)/.test(path);
}

function Sound(filename, basePath, onError, options) {
  var asset = resolveAssetSource(filename);
  if (asset) {
    this._filename = asset.uri;
    onError = basePath;
  } else {
    this._filename = basePath ? basePath + '/' + filename : filename;

    if (IsAndroid && !basePath && isRelativePath(filename)) {
      this._filename = filename.toLowerCase().replace(/\.[^.]+$/, '');
    }
  }

  this.registerOnPlay = function() {
    if (this.onPlaySubscription != null) {
      console.warn('On Play change event listener is already registered');
      return;
    }

    if (!IsWindows) {
      this.onPlaySubscription = eventEmitter.addListener(
        'onPlayChange',
        (param) => {
          const { isPlaying, playerKey } = param;
          if (playerKey === this._key) {
            if (isPlaying) {
              this._playing = true;
            }
            else {
              this._playing = false;
            }
          }
        },
      );
    }
  }

  this._loaded = false;
  this._playing = false;
  this._key = nextKey++;
  this._duration = -1;
  this._numberOfChannels = -1;
  this._volume = 1;
  this._pan = 0;
  this._numberOfLoops = 0;
  this._speed = 1;
  RNSound.prepare(this._filename, this._key, options || {}, (error, props) => {
    if (props) {
      if (typeof props.duration === 'number') {
        this._duration = props.duration;
      }
      if (typeof props.numberOfChannels === 'number') {
        this._numberOfChannels = props.numberOfChannels;
      }
    }
    if (error === null) {
      this._loaded = true;
      this.registerOnPlay();
    }
    onError && onError(error, props);
  });
}

Sound.prototype.isLoaded = function() {
  return this._loaded;
};

Sound.prototype.play = function(onEnd) {
  if (this._loaded) {
    RNSound.play(this._key, (successfully) => onEnd && onEnd(successfully));
    if (IsAndroid) {
      // For Android
      // Manually call native setSpeed() after native play() to apply current speed.
      // Native setSpeed method should be called only if the media player is already playing.
      // To prevent android from playing automatically when setSpeed is called.
      RNSound.setSpeed(this._key, this._speed);
    }
  }
  else {
    onEnd && onEnd(false);
  }
  return this;
};

Sound.prototype.pause = function(callback) {
  if (this._loaded) {
    RNSound.pause(this._key, () => {
      this._playing = false;
      callback && callback();
    });
  }
  return this;
};

Sound.prototype.stop = function(callback) {
  if (this._loaded) {
    RNSound.stop(this._key, () => {
      this._playing = false;
      callback && callback();
    });
  }
  return this;
};

Sound.prototype.setStreamType = function(streamType, callback) {
  // For android only.
  // reset() is required before setStreamType
  // sets streamType, and prepare again.
  if (streamType == null) {
    const error = new Error('invalid streamType');
    throw error;
    callback && callback(error);
    return this;
  }
  if (!IsAndroid) {
    const error = new Error('setStreamType is only supported in Android');
    throw error;
    callback && callback(error);
    return this;
  }
  if (this._loaded) {
    const error = new Error('reset() is required If player is already prepared.');
    throw error;
    callback && callback(error);
    return this;
  }
  RNSound.prepare(this._filename, this._key, { audioStreamType: streamType } || {}, (error, props) => {
    if (props) {
      if (typeof props.duration === 'number') {
        this._duration = props.duration;
      }
      if (typeof props.numberOfChannels === 'number') {
        this._numberOfChannels = props.numberOfChannels;
      }
    }
    if (error === null) {
      this._loaded = true;
      callback && callback();
    } else {
      callback && callback(error);
    }
  });
}

Sound.prototype.reset = function() {
  if (this._loaded && IsAndroid) {
    RNSound.reset(this._key);
    this._playing = false;
    this._loaded = false;
  }
  return this;
};

Sound.prototype.release = function() {
  if (this._loaded) {
    RNSound.release(this._key);
    this._loaded = false;
    if (!IsWindows) {
      if (this.onPlaySubscription != null) {
        this.onPlaySubscription.remove();
        this.onPlaySubscription = null;
      }
    }
  }
  return this;
};

Sound.prototype.getDuration = function() {
  return this._duration;
};

Sound.prototype.getNumberOfChannels = function() {
  return this._numberOfChannels;
};

Sound.prototype.getVolume = function() {
  return this._volume;
};

Sound.prototype.setVolume = function(value) {
  this._volume = value;
  if (this._loaded) {
    if (IsAndroid || IsWindows) {
      RNSound.setVolume(this._key, value, value);
    } else {
      RNSound.setVolume(this._key, value);
    }
  }
  return this;
};

Sound.prototype.getSystemVolume = function(callback) {
  if (IsAndroid) {
    RNSound.getSystemVolume(callback);
  }
  return this;
};

Sound.prototype.setSystemVolume = function(value) {
  if (IsAndroid) {
    RNSound.setSystemVolume(value);
  }
  return this;
};

Sound.prototype.getPan = function() {
  return this._pan;
};

Sound.prototype.setPan = function(value) {
  if (this._loaded) {
    RNSound.setPan(this._key, this._pan = value);
  }
  return this;
};

Sound.prototype.getNumberOfLoops = function() {
  return this._numberOfLoops;
};

Sound.prototype.setNumberOfLoops = function(value) {
  this._numberOfLoops = value;
  if (this._loaded) {
    if (IsAndroid || IsWindows) {
      RNSound.setLooping(this._key, !!value);
    } else {
      RNSound.setNumberOfLoops(this._key, value);
    }
  }
  return this;
};

Sound.prototype.setSpeed = function(value) {
  this._speed = value;
  if (this._loaded) {
    if (!IsWindows && !IsAndroid) {
      RNSound.setSpeed(this._key, value);
    } else if (IsAndroid) {
      // For Android
      // Call native setSpeed method only if the media player is already playing.
      // To prevent android from playing automatically when setSpeed is called.
      if (this._playing) {
        RNSound.setSpeed(this._key, value);
      }
    }
  }
  return this;
};

Sound.prototype.getCurrentTime = function(callback) {
  if (this._loaded) {
    RNSound.getCurrentTime(this._key, callback);
  }
};

Sound.prototype.setCurrentTime = function(value) {
  if (this._loaded) {
    RNSound.setCurrentTime(this._key, value);
  }
  return this;
};

// android only
Sound.prototype.setSpeakerphoneOn = function(value) {
  if (IsAndroid) {
    RNSound.setSpeakerphoneOn(this._key, value);
  } else if (!IsAndroid && !IsWindows) {
    console.wran('Use static method Sound.setSpeakerphoneOn() instead for iOS!')
  }
};

Sound.prototype.isPlaying = function() {
  return this._playing;
};

Sound.adjustStreamVolume = function(streamType, direction, flags) {
  if (IsAndroid) {
    RNSound.adjustStreamVolume(streamType, direction, flags);
  }
}

Sound.setStreamMute = function(streamType, state) {
  if (IsAndroid) {
    RNSound.setStreamMute(streamType, state);
  }
}

Sound.isHeadsetPlugged = function() {
  return RNSound.isHeadsetPlugged();
};

Sound.registerHeadsetPlugChangeListener = function(headsetPluggedInListener) {
  // console.log('Register headset plug change event listener');
  if (this.headsetPluggedInSubscription != null) {
    console.warn('Headset plug change event listener is already registered');
    return;
  }

  if (!IsWindows) {
    // Remove route change listener first
    RNSound.addRouteChangeListener();
    this.headsetPluggedInSubscription = eventEmitter.addListener(
      'RouteChange',
      headsetPluggedInListener,
    );
  }
};

Sound.unregisterHeadsetPlugChangeListener = function() {
  // console.log('Unregister headset plug change event listener');
  if (!IsWindows) {
    if (this.headsetPluggedInSubscription != null) {
      RNSound.removeRouteChangeListener();
      this.headsetPluggedInSubscription.remove();
      this.headsetPluggedInSubscription = null;
    }
  }
}

// ios only

// This is deprecated.  Call the static one instead.

Sound.prototype.setCategory = function(value) {
  Sound.setCategory(value, false);
}

Sound.enable = function(enabled) {
  RNSound.enable(enabled);
};

Sound.enableInSilenceMode = function(enabled) {
  if (!IsAndroid && !IsWindows) {
    RNSound.enableInSilenceMode(enabled);
  }
};

Sound.setActive = function(value) {
  if (!IsAndroid && !IsWindows) {
    RNSound.setActive(value);
  }
};

Sound.setCategory = function(value, mixWithOthers = false, allowBluetooth = false) {
  if (!IsAndroid && !IsWindows) {
    RNSound.setCategory(value, mixWithOthers, allowBluetooth);
  }
};

Sound.setSpeakerphoneOnIos = function(value) {
  if (!IsAndroid && !IsWindows) {
    RNSound.setSpeakerphoneOn(value);
  }
};

Sound.setMode = function(value) {
  if (!IsAndroid && !IsWindows) {
    RNSound.setMode(value);
  }
};

Sound.MAIN_BUNDLE = RNSound.MainBundlePath;
Sound.DOCUMENT = RNSound.NSDocumentDirectory;
Sound.LIBRARY = RNSound.NSLibraryDirectory;
Sound.CACHES = RNSound.NSCachesDirectory;

module.exports = Sound;
<|endoftext|>