<commit_before>'use strict'

const record = require('node-record-lpcm16')
const stream = require('stream')
const {Detector, Models} = require('snowboy')

const ERROR = {
  NOT_STARTED : "NOT_STARTED",
  INVALID_INDEX : "INVALID_INDEX"
}

const CloudSpeechRecognizer = {}
CloudSpeechRecognizer.init = recognizer => {
  const csr = new stream.Writable()
  csr.listening = false
  csr.recognizer = recognizer
  return csr
}

CloudSpeechRecognizer.startStreaming = (options, audioStream, cloudSpeechRecognizer) => {
  if (cloudSpeechRecognizer.listening) {
    return
  }

  cloudSpeechRecognizer.listening = true

  const recognizer = cloudSpeechRecognizer.recognizer
  const recognitionStream = recognizer.createRecognizeStream({
    config: {
      encoding: 'LINEAR16',
      sampleRate: 16000,
      languageCode: options.language
    },
    singleUtterance: true,
    interimResults: true,
    verbose: true
  })

  recognitionStream.on('error', err => cloudSpeechRecognizer.emit('error', err))

  recognitionStream.on('data', data => {
    if (data) {
      cloudSpeechRecognizer.emit('data', data)
      if (data.endpointerType === 'END_OF_UTTERANCE') {
        cloudSpeechRecognizer.listening = false
        audioStream.unpipe(recognitionStream)
      }
    }
  })

  audioStream.pipe(recognitionStream)
}

const Sonus = {}
Sonus.annyang = require('./lib/annyang-core.js')

Sonus.init = (options, recognizer) => {
  // don't mutate options
  const opts = Object.assign({}, options),
    models = new Models(),
    sonus = new stream.Writable(),
    csr = CloudSpeechRecognizer.init(recognizer)
  sonus.mic = {}
  sonus.started = false

  // If we don't have any hotwords passed in, add the default global model
  opts.hotwords = opts.hotwords || [1]
  opts.hotwords.forEach(model => {
    models.add({
      file: model.file || 'node_modules/snowboy/resources/snowboy.umdl',
      sensitivity: model.sensitivity || '0.5',
      hotwords: model.hotword || 'default'
    })
  })

  // defaults
  opts.models = models
  opts.resource = opts.resource || 'node_modules/snowboy/resources/common.res'
  opts.audioGain = opts.audioGain || 2.0
  opts.language = opts.language || 'en-US' //https://cloud.google.com/speech/docs/languages

  const detector = sonus.detector = new Detector(opts)

  detector.on('silence', () => sonus.emit('silence'))
  detector.on('sound', () => sonus.emit('sound'))

  // When a hotword is detected pipe the audio stream to speech detection
  detector.on('hotword', (index, hotword) => {
    sonus.trigger(index, hotword)
  })

  csr.on('error', error => sonus.emit('error', { streamingError: error }))

  let transcriptEmpty = true
  csr.on('data', data => {
    const result = data.results[0]
    const finalEmpty = (data.endpointerType === 'END_OF_UTTERANCE' && transcriptEmpty)
    if (result) {
      transcriptEmpty = false
      if (result.isFinal) {
        sonus.emit('final-result', result.transcript)
        Sonus.annyang.trigger(result.transcript)
        transcriptEmpty = true //reset transcript
      } else {
        sonus.emit('partial-result', result.transcript)
      }
    } else if (finalEmpty) {
      sonus.emit('final-result', "")
    }
  })

  sonus.trigger = (index, hotword) => {
    if(sonus.started){
      try{
        let triggerHotword = (index == 0)? hotword : models.lookup(index)
        sonus.emit('hotword', index, triggerHotword)
        CloudSpeechRecognizer.startStreaming(opts, sonus.mic, csr)
      } catch (e) {
        throw ERROR.INVALID_INDEX
      }
    } else {
      throw ERROR.NOT_STARTED
    }
  }

  return sonus
}

Sonus.start = sonus => {
  sonus.mic = record.start({
    threshold: 0,
    verbose: false
  })

  sonus.mic.pipe(sonus.detector)
  sonus.started = true
}

Sonus.trigger = (sonus, index, hotword) => sonus.trigger(index, hotword)

Sonus.pause = sonus => sonus.mic.pause()

Sonus.resume = sonus => sonus.mic.resume()

Sonus.stop = () => record.stop()

module.exports = Sonus
<commit_msg>move endpointerType check logic to condition<commit_after>'use strict'

const record = require('node-record-lpcm16')
const stream = require('stream')
const {Detector, Models} = require('snowboy')

const ERROR = {
  NOT_STARTED : "NOT_STARTED",
  INVALID_INDEX : "INVALID_INDEX"
}

const CloudSpeechRecognizer = {}
CloudSpeechRecognizer.init = recognizer => {
  const csr = new stream.Writable()
  csr.listening = false
  csr.recognizer = recognizer
  return csr
}

CloudSpeechRecognizer.startStreaming = (options, audioStream, cloudSpeechRecognizer) => {
  if (cloudSpeechRecognizer.listening) {
    return
  }

  cloudSpeechRecognizer.listening = true

  const recognizer = cloudSpeechRecognizer.recognizer
  const recognitionStream = recognizer.createRecognizeStream({
    config: {
      encoding: 'LINEAR16',
      sampleRate: 16000,
      languageCode: options.language
    },
    singleUtterance: true,
    interimResults: true,
    verbose: true
  })

  recognitionStream.on('error', err => cloudSpeechRecognizer.emit('error', err))

  recognitionStream.on('data', data => {
    if (data) {
      cloudSpeechRecognizer.emit('data', data)
      if (data.endpointerType === 'END_OF_UTTERANCE') {
        cloudSpeechRecognizer.listening = false
        audioStream.unpipe(recognitionStream)
      }
    }
  })

  audioStream.pipe(recognitionStream)
}

const Sonus = {}
Sonus.annyang = require('./lib/annyang-core.js')

Sonus.init = (options, recognizer) => {
  // don't mutate options
  const opts = Object.assign({}, options),
    models = new Models(),
    sonus = new stream.Writable(),
    csr = CloudSpeechRecognizer.init(recognizer)
  sonus.mic = {}
  sonus.started = false

  // If we don't have any hotwords passed in, add the default global model
  opts.hotwords = opts.hotwords || [1]
  opts.hotwords.forEach(model => {
    models.add({
      file: model.file || 'node_modules/snowboy/resources/snowboy.umdl',
      sensitivity: model.sensitivity || '0.5',
      hotwords: model.hotword || 'default'
    })
  })

  // defaults
  opts.models = models
  opts.resource = opts.resource || 'node_modules/snowboy/resources/common.res'
  opts.audioGain = opts.audioGain || 2.0
  opts.language = opts.language || 'en-US' //https://cloud.google.com/speech/docs/languages

  const detector = sonus.detector = new Detector(opts)

  detector.on('silence', () => sonus.emit('silence'))
  detector.on('sound', () => sonus.emit('sound'))

  // When a hotword is detected pipe the audio stream to speech detection
  detector.on('hotword', (index, hotword) => {
    sonus.trigger(index, hotword)
  })

  csr.on('error', error => sonus.emit('error', { streamingError: error }))

  let transcriptEmpty = true
  csr.on('data', data => {
    const result = data.results[0]
    if (result) {
      transcriptEmpty = false
      if (result.isFinal) {
        sonus.emit('final-result', result.transcript)
        Sonus.annyang.trigger(result.transcript)
        transcriptEmpty = true //reset transcript
      } else {
        sonus.emit('partial-result', result.transcript)
      }
    } else if (data.endpointerType === 'END_OF_UTTERANCE' && transcriptEmpty) {
      sonus.emit('final-result', "")
    }
  })

  sonus.trigger = (index, hotword) => {
    if(sonus.started){
      try{
        let triggerHotword = (index == 0)? hotword : models.lookup(index)
        sonus.emit('hotword', index, triggerHotword)
        CloudSpeechRecognizer.startStreaming(opts, sonus.mic, csr)
      } catch (e) {
        throw ERROR.INVALID_INDEX
      }
    } else {
      throw ERROR.NOT_STARTED
    }
  }

  return sonus
}

Sonus.start = sonus => {
  sonus.mic = record.start({
    threshold: 0,
    verbose: false
  })

  sonus.mic.pipe(sonus.detector)
  sonus.started = true
}

Sonus.trigger = (sonus, index, hotword) => sonus.trigger(index, hotword)

Sonus.pause = sonus => sonus.mic.pause()

Sonus.resume = sonus => sonus.mic.resume()

Sonus.stop = () => record.stop()

module.exports = Sonus
<|endoftext|>


<commit_before>'use strict';

module.exports = {
	root: true,
	env: {
		node: true,
		es6: true
	},
	rules: {
		'no-var': 1,
		'prefer-const': 1,
		'no-const-assign': 2,
		'prefer-spread': 1,
		'prefer-template': 1,
		'no-arrow-condition': 2,
		'arrow-spacing': [2, {'before': true, 'after': true}],
		'prefer-arrow-callback': 1,
		'arrow-body-style': [1, 'as-needed'],
		// Possible Errors
		'comma-dangle': 0,
		'no-cond-assign': [2, 'always'],
		'no-console': 0,
		'no-constant-condition': 2,
		'no-control-regex': 2,
		'no-debugger': 2,
		'no-dupe-args': 2,
		'no-dupe-keys': 2,
		'no-duplicate-case': 2,
		'no-empty-character-class': 2,
		'no-empty': 2,
		'no-ex-assign': 2,
		'no-extra-boolean-cast': 1,
		'no-extra-parens': 0,
		'no-extra-semi': 2,
		'no-func-assign': 2,
		'no-inner-declarations': 2,
		'no-invalid-regexp': 2,
		'no-irregular-whitespace': 1,
		'no-negated-in-lhs': 2,
		'no-obj-calls': 2,
		'no-regex-spaces': 2,
		'quote-props': [1, 'as-needed'],
		'no-sparse-arrays': 2,
		'no-unreachable': 2,
		'use-isnan': 2,
		'valid-jsdoc': 0,
		'valid-typeof': 2,
		// Best Practices
		'block-scoped-var': 0,
		'complexity': 0,
		'consistent-return': 0,
		'curly': [2, 'all'],
		'default-case': 1,
		'dot-notation': [2, {
			'allowKeywords': true
		}],
		'dot-location': [2, 'property'],
		'eqeqeq': 2,
		'guard-for-in': 2,
		'no-alert': 2,
		'no-caller': 2,
		'no-div-regex': 0,
		'no-else-return': 0,
		'no-empty-label': 2,
		'no-eq-null': 2,
		'no-eval': 2,
		'no-extend-native': 2,
		'no-extra-bind': 2,
		'no-fallthrough': 2,
		'no-floating-decimal': 2,
		'no-implied-eval': 2,
		'no-iterator': 2,
		'no-labels': 2,
		'no-lone-blocks': 2,
		'no-loop-func': 2,
		'no-multi-spaces': 0,
		'no-multi-str': 2,
		'no-native-reassign': 2,
		'no-new-func': 2,
		'no-new-wrappers': 2,
		'no-new': 2,
		'no-octal-escape': 2,
		'no-octal': 2,
		'no-param-reassign': 0,
		'no-process-env': 0,
		'no-proto': 2,
		'no-redeclare': 2,
		'no-return-assign': 2,
		'no-script-url': 2,
		'no-self-compare': 2,
		'no-sequences': 2,
		'no-throw-literal': 2,
		'no-unused-expressions': 2,
		'no-void': 2,
		'no-warning-comments': 0,
		'no-with': 2,
		'radix': 2,
		'vars-on-top': 0,
		'wrap-iife': [2, 'any'],
		'yoda': 2,
		// Strict
		'strict': [2, 'global'],
		// Variables
		'no-catch-shadow': 0,
		'no-delete-var': 2,
		'no-shadow-restricted-names': 2,
		'no-shadow': 0,
		'no-undef-init': 2,
		'no-undef': 2,
		'no-undefined': 0,
		'no-unused-vars': 1,
		'no-use-before-define': 0,
		// Stylistic Issues
		'brace-style': [1, 'stroustrup', {
			'allowSingleLine': true
		}],
		'camelcase': [1, {
			'properties': 'never'
		}],
		'comma-spacing': [1, {
			'before': false,
			'after': true
		}],
		'consistent-this': 0,
		'comma-style': [1, 'last'],
		'eol-last': 1,
		'func-names': 0,
		'func-style': 0,
		'indent': [1, 'tab'],
		'key-spacing': [1, {
			'beforeColon': false,
			'afterColon': true
		}],
		'linebreak-style': 1,
		'max-nested-callbacks': 0,
		'new-cap': 0,
		'new-parens': 1,
		'newline-after-var': 0,
		'no-array-constructor': 1,
		'no-continue': 1,
		'no-inline-comments': 1,
		'no-lonely-if': 1,
		'no-mixed-spaces-and-tabs': 1,
		'no-multiple-empty-lines': [1, {
			'max': 1
		}],
		'no-nested-ternary': 1,
		'no-new-object': 1,
		'no-spaced-func': 1,
		'no-ternary': 0,
		'no-trailing-spaces': 1,
		'no-underscore-dangle': 0,
		'no-unneeded-ternary': 1,
		'one-var': [1, 'never'],
		'padded-blocks': [1, 'never'],
		'operator-assignment': 0,
		'operator-linebreak': [1, 'after'],
		'padded-blocks': [1, 'never'],
		'quote-props': 0,
		'semi': [1, 'always'],
		'quotes': [1, 'single', 'avoid-escape'],
		'semi-spacing': [1, {
			'before': false,
			'after': true
		}],
		'semi': [1, 'always'],
		'sort-vars': [1, {
			'ignoreCase': true
		}],
		'space-after-keywords': 1,
		'space-before-blocks': 1,
		'space-before-function-paren': [1, {
			'anonymous': 'always',
			'named': 'never'
		}],
		'object-curly-spacing': [1, 'never'],
		'array-bracket-spacing': [1, 'never'],
		'space-in-parens': [1, 'never'],
		'space-infix-ops': 1,
		'space-return-throw-case': 1,
		'space-unary-ops': [1, {
			'words': true, 'nonwords': false
		}],
		'spaced-comment': [1, 'always'],
		'wrap-regex': 0
	}
};
<commit_msg>Stop preferring template strings over concatenation<commit_after>'use strict';

module.exports = {
	root: true,
	env: {
		node: true,
		es6: true
	},
	rules: {
		'no-var': 1,
		'prefer-const': 1,
		'no-const-assign': 2,
		'prefer-spread': 1,
		'prefer-template': 0,
		'no-arrow-condition': 2,
		'arrow-spacing': [2, {'before': true, 'after': true}],
		'prefer-arrow-callback': 1,
		'arrow-body-style': [1, 'as-needed'],
		// Possible Errors
		'comma-dangle': 0,
		'no-cond-assign': [2, 'always'],
		'no-console': 0,
		'no-constant-condition': 2,
		'no-control-regex': 2,
		'no-debugger': 2,
		'no-dupe-args': 2,
		'no-dupe-keys': 2,
		'no-duplicate-case': 2,
		'no-empty-character-class': 2,
		'no-empty': 2,
		'no-ex-assign': 2,
		'no-extra-boolean-cast': 1,
		'no-extra-parens': 0,
		'no-extra-semi': 2,
		'no-func-assign': 2,
		'no-inner-declarations': 2,
		'no-invalid-regexp': 2,
		'no-irregular-whitespace': 1,
		'no-negated-in-lhs': 2,
		'no-obj-calls': 2,
		'no-regex-spaces': 2,
		'quote-props': [1, 'as-needed'],
		'no-sparse-arrays': 2,
		'no-unreachable': 2,
		'use-isnan': 2,
		'valid-jsdoc': 0,
		'valid-typeof': 2,
		// Best Practices
		'block-scoped-var': 0,
		'complexity': 0,
		'consistent-return': 0,
		'curly': [2, 'all'],
		'default-case': 1,
		'dot-notation': [2, {
			'allowKeywords': true
		}],
		'dot-location': [2, 'property'],
		'eqeqeq': 2,
		'guard-for-in': 2,
		'no-alert': 2,
		'no-caller': 2,
		'no-div-regex': 0,
		'no-else-return': 0,
		'no-empty-label': 2,
		'no-eq-null': 2,
		'no-eval': 2,
		'no-extend-native': 2,
		'no-extra-bind': 2,
		'no-fallthrough': 2,
		'no-floating-decimal': 2,
		'no-implied-eval': 2,
		'no-iterator': 2,
		'no-labels': 2,
		'no-lone-blocks': 2,
		'no-loop-func': 2,
		'no-multi-spaces': 0,
		'no-multi-str': 2,
		'no-native-reassign': 2,
		'no-new-func': 2,
		'no-new-wrappers': 2,
		'no-new': 2,
		'no-octal-escape': 2,
		'no-octal': 2,
		'no-param-reassign': 0,
		'no-process-env': 0,
		'no-proto': 2,
		'no-redeclare': 2,
		'no-return-assign': 2,
		'no-script-url': 2,
		'no-self-compare': 2,
		'no-sequences': 2,
		'no-throw-literal': 2,
		'no-unused-expressions': 2,
		'no-void': 2,
		'no-warning-comments': 0,
		'no-with': 2,
		'radix': 2,
		'vars-on-top': 0,
		'wrap-iife': [2, 'any'],
		'yoda': 2,
		// Strict
		'strict': [2, 'global'],
		// Variables
		'no-catch-shadow': 0,
		'no-delete-var': 2,
		'no-shadow-restricted-names': 2,
		'no-shadow': 0,
		'no-undef-init': 2,
		'no-undef': 2,
		'no-undefined': 0,
		'no-unused-vars': 1,
		'no-use-before-define': 0,
		// Stylistic Issues
		'brace-style': [1, 'stroustrup', {
			'allowSingleLine': true
		}],
		'camelcase': [1, {
			'properties': 'never'
		}],
		'comma-spacing': [1, {
			'before': false,
			'after': true
		}],
		'consistent-this': 0,
		'comma-style': [1, 'last'],
		'eol-last': 1,
		'func-names': 0,
		'func-style': 0,
		'indent': [1, 'tab'],
		'key-spacing': [1, {
			'beforeColon': false,
			'afterColon': true
		}],
		'linebreak-style': 1,
		'max-nested-callbacks': 0,
		'new-cap': 0,
		'new-parens': 1,
		'newline-after-var': 0,
		'no-array-constructor': 1,
		'no-continue': 1,
		'no-inline-comments': 1,
		'no-lonely-if': 1,
		'no-mixed-spaces-and-tabs': 1,
		'no-multiple-empty-lines': [1, {
			'max': 1
		}],
		'no-nested-ternary': 1,
		'no-new-object': 1,
		'no-spaced-func': 1,
		'no-ternary': 0,
		'no-trailing-spaces': 1,
		'no-underscore-dangle': 0,
		'no-unneeded-ternary': 1,
		'one-var': [1, 'never'],
		'padded-blocks': [1, 'never'],
		'operator-assignment': 0,
		'operator-linebreak': [1, 'after'],
		'padded-blocks': [1, 'never'],
		'quote-props': 0,
		'semi': [1, 'always'],
		'quotes': [1, 'single', 'avoid-escape'],
		'semi-spacing': [1, {
			'before': false,
			'after': true
		}],
		'semi': [1, 'always'],
		'sort-vars': [1, {
			'ignoreCase': true
		}],
		'space-after-keywords': 1,
		'space-before-blocks': 1,
		'space-before-function-paren': [1, {
			'anonymous': 'always',
			'named': 'never'
		}],
		'object-curly-spacing': [1, 'never'],
		'array-bracket-spacing': [1, 'never'],
		'space-in-parens': [1, 'never'],
		'space-infix-ops': 1,
		'space-return-throw-case': 1,
		'space-unary-ops': [1, {
			'words': true, 'nonwords': false
		}],
		'spaced-comment': [1, 'always'],
		'wrap-regex': 0
	}
};
<|endoftext|>


<commit_before>var express = require('express');
var app = express();
var fs = require("fs"); // for reading settings
var modules = [];
var pug = require('pug');

var bodyParser = require('body-parser'); // Basic parser (no multipart support)
var uep = bodyParser.urlencoded({
    extended: false
});

var multer = require('multer');
var storage = multer.diskStorage({
    destination: (req, file, cb) => {
        cb(null, './uploads');
    },
    filename: (req, file, cb) => {
        cb(null, 'wallpaper.jpg');
    }
});
var upload = multer({
    storage: storage
});

var settings = {
    load: () => {
        serverLog('Looking for local settings file');
        if (fs.existsSync('./settings.json')) {
            serverLog('Found local settings file, overriding defaults');
            var settingsFile = fs.readFileSync('./settings.json');
            settings.current = JSON.parse(settingsFile);
        } else {
            serverLog('Couldn\'t find local settings file, usings defaults');
        }
    },
    save: () => {
        serverLog('Saving settings to local file');
        var stringified = JSON.stringify(settings.current, null, 4);
        fs.writeFileSync('./settings.json', stringified);
    },
    current: {
        modules: [{
            name: "bookmarks-module",
            active: true,
            jsEntry: "bookmarks"
        }, {
            name: "system-info-module",
            active: true,
            jsEntry: "systemInfoStart"
        }, {
            name: "media-controls-module",
            active: true,
            jsEntry: "media"
        }, {
            name: "reddit-wallpapers-module",
            active: true,
            jsEntry: "reddit"
        }]
    }
};

function loadModules() {
    return new Promise((resolve, reject) => {
        settings.load();
        serverLog("Loading modules!");
        var promises = [];
        for (var module in settings.current.modules) {
            if (settings.current.modules[module].active) {
                modules[module] = require("./modules/" + settings.current.modules[module].name);
                promises.push(modules[module](app));
            }
        }
        Promise.all(promises).then(() => {
            resolve(`Loaded ${promises.length} modules!`);
        }).catch((err)=> {
            reject(err);
        });
    });
}

function serverLog(text) {
    var date = new Date();
    console.log("[ " + date.getHours() + ":" + date.getMinutes() + ":" + date.getSeconds() + " ] " + text);
}

app.get('/', function(req, res) {
    serverLog("Serving main page");
    var generalSettings = pug.renderFile('views/generalSettings.pug', {
        settings: settings.current
    });
    var renderInfo = {
        settings: settings.current,
        generalSettings,
        modules: [],
    };
    renderInfo.modules.push({
        module: "General settings",
        settingsView: pug.renderFile(`${__dirname}/views/generalSettings.pug`, {
            settings: settings.current
        })
    });
    var settingsReady = new Promise((Resolve, Reject) => {
        var settingsPromises = [];
        modules.forEach((moduleItem) => {
            settingsPromises.push(moduleItem.getSettings());
        });
        var settingsRendered = [];
        Promise.all(settingsPromises).then((value) => {
            value.forEach((promiseValue) => {
                settingsRendered.push(promiseValue);
            });
            Resolve(settingsRendered);
        }).catch((err) => {
            console.log(err);
        });
    });
    var mainReady = new Promise((Resolve, Reject) => {
        var mainPromises = [];
        modules.forEach((moduleItem) => {
            mainPromises.push(moduleItem.getMainView());
        });
        var mainRendered = [];
        Promise.all(mainPromises).then((value) => {
            value.forEach((promiseValue) => {
                mainRendered.push(promiseValue);
            });
            Resolve(mainRendered);
        }).catch((err) => {
            console.log(err);
        });
    });
    Promise.all([mainReady, settingsReady]).then((value) => {
        modules.forEach((moduleItem, argIndex) => {
            var moduleObject = {
                module: moduleItem.name,
                mainView: value[0][argIndex],
                settingsView: value[1][argIndex]
            };
            renderInfo.modules.push(moduleObject);
        });
        res.render('app', renderInfo);
    }).catch((err) => {
        console.log(err);
        res.status(500).send({
            error: 'Something went wrong, sorry'
        });
    });
});

app.get('/wallpaper.jpg', (req, res) => {
    fs.access(`${__dirname}/uploads/wallpaper.jpg`, (err) => {
        if (err) {
            res.sendFile(`${__dirname}/static/wall.jpg`);
        } else {
            res.sendFile(`${__dirname}/uploads/wallpaper.jpg`);
        }
    });
});

app.post('/updateModules', uep, (req, res) => {
    var updatedModules = [];
    JSON.parse(req.body.modules).forEach((item) => {
        updatedModules[item.name] = item.active;
    });
    /* jshint ignore:start */
    settings.current.modules.forEach((moduleItem) => {
        if (typeof updatedModules[moduleItem.name] !== "undefinied") {
            moduleItem.active = updatedModules[moduleItem.name];
        }
    });
    /* jshint ignore:end */
    settings.save();
    res.sendStatus(200);
    process.exit();
});

app.post('/changeWallpaper', upload.single('wallpaper'), (req, res) => {
    res.redirect('/');
});

var server = app.listen(8081, function() {
    app.use("/", express.static('.'));
    app.use("/static", express.static('./static'));
    app.set('view engine', 'pug');
    var views = [`${__dirname}/views`];
    settings.current.modules.forEach(function(moduleItem) {
        views.push(`${__dirname}/modules/${moduleItem.name}/views`);
    });
    app.set('views', views);
    loadModules().then((res) => {
        serverLog(res);
        serverLog("Server up!");
    });
});
<commit_msg>Added new route for thumbnails. If no thumbnail is present placekolder gets sent<commit_after>var express = require('express');
var app = express();
var fs = require("fs"); // for reading settings
var modules = [];
var pug = require('pug');

var bodyParser = require('body-parser'); // Basic parser (no multipart support)
var uep = bodyParser.urlencoded({
    extended: false
});

var multer = require('multer');
var storage = multer.diskStorage({
    destination: (req, file, cb) => {
        cb(null, './uploads');
    },
    filename: (req, file, cb) => {
        cb(null, 'wallpaper.jpg');
    }
});
var upload = multer({
    storage: storage
});

var settings = {
    load: () => {
        serverLog('Looking for local settings file');
        if (fs.existsSync('./settings.json')) {
            serverLog('Found local settings file, overriding defaults');
            var settingsFile = fs.readFileSync('./settings.json');
            settings.current = JSON.parse(settingsFile);
        } else {
            serverLog('Couldn\'t find local settings file, usings defaults');
        }
    },
    save: () => {
        serverLog('Saving settings to local file');
        var stringified = JSON.stringify(settings.current, null, 4);
        fs.writeFileSync('./settings.json', stringified);
    },
    current: {
        modules: [{
            name: "bookmarks-module",
            active: true,
            jsEntry: "bookmarks"
        }, {
            name: "system-info-module",
            active: true,
            jsEntry: "systemInfoStart"
        }, {
            name: "media-controls-module",
            active: true,
            jsEntry: "media"
        }, {
            name: "reddit-wallpapers-module",
            active: true,
            jsEntry: "reddit"
        }]
    }
};

function loadModules() {
    return new Promise((resolve, reject) => {
        settings.load();
        serverLog("Loading modules!");
        var promises = [];
        for (var module in settings.current.modules) {
            if (settings.current.modules[module].active) {
                modules[module] = require("./modules/" + settings.current.modules[module].name);
                promises.push(modules[module](app));
            }
        }
        Promise.all(promises).then(() => {
            resolve(`Loaded ${promises.length} modules!`);
        }).catch((err) => {
            reject(err);
        });
    });
}

function serverLog(text) {
    var date = new Date();
    console.log("[ " + date.getHours() + ":" + date.getMinutes() + ":" + date.getSeconds() + " ] " + text);
}

app.get('/', function(req, res) {
    serverLog("Serving main page");
    var generalSettings = pug.renderFile('views/generalSettings.pug', {
        settings: settings.current
    });
    var renderInfo = {
        settings: settings.current,
        generalSettings,
        modules: [],
    };
    renderInfo.modules.push({
        module: "General settings",
        settingsView: pug.renderFile(`${__dirname}/views/generalSettings.pug`, {
            settings: settings.current
        })
    });
    var settingsReady = new Promise((Resolve, Reject) => {
        var settingsPromises = [];
        modules.forEach((moduleItem) => {
            settingsPromises.push(moduleItem.getSettings());
        });
        var settingsRendered = [];
        Promise.all(settingsPromises).then((value) => {
            value.forEach((promiseValue) => {
                settingsRendered.push(promiseValue);
            });
            Resolve(settingsRendered);
        }).catch((err) => {
            console.log(err);
        });
    });
    var mainReady = new Promise((Resolve, Reject) => {
        var mainPromises = [];
        modules.forEach((moduleItem) => {
            mainPromises.push(moduleItem.getMainView());
        });
        var mainRendered = [];
        Promise.all(mainPromises).then((value) => {
            value.forEach((promiseValue) => {
                mainRendered.push(promiseValue);
            });
            Resolve(mainRendered);
        }).catch((err) => {
            console.log(err);
        });
    });
    Promise.all([mainReady, settingsReady]).then((value) => {
        modules.forEach((moduleItem, argIndex) => {
            var moduleObject = {
                module: moduleItem.name,
                mainView: value[0][argIndex],
                settingsView: value[1][argIndex]
            };
            renderInfo.modules.push(moduleObject);
        });
        res.render('app', renderInfo);
    }).catch((err) => {
        console.log(err);
        res.status(500).send({
            error: 'Something went wrong, sorry'
        });
    });
});

app.get('/wallpaper.jpg', (req, res) => {
    fs.access(`${__dirname}/uploads/wallpaper.jpg`, (err) => {
        if (err) {
            res.sendFile(`${__dirname}/static/wall.jpg`);
        } else {
            res.sendFile(`${__dirname}/uploads/wallpaper.jpg`);
        }
    });
});

app.post('/updateModules', uep, (req, res) => {
    var updatedModules = [];
    JSON.parse(req.body.modules).forEach((item) => {
        updatedModules[item.name] = item.active;
    });
    /* jshint ignore:start */
    settings.current.modules.forEach((moduleItem) => {
        if (typeof updatedModules[moduleItem.name] !== "undefinied") {
            moduleItem.active = updatedModules[moduleItem.name];
        }
    });
    /* jshint ignore:end */
    settings.save();
    res.sendStatus(200);
    process.exit();
});

app.post('/changeWallpaper', upload.single('wallpaper'), (req, res) => {
    res.redirect('/');
});

app.get('/thumbnails/:id', (req, res) => {
    fs.access(`thumbnails/${req.params.id}`, (err) => {
        if (err) {
            res.sendFile(`${__dirname}/thumbnails/placeholder.png`);
        } else {
            res.sendFile(`${__dirname}/thumbnails/${req.params.id}`);
        }
    });
});

var server = app.listen(8081, function() {
    app.use("/", express.static('.'));
    app.use("/static", express.static('./static'));
    app.set('view engine', 'pug');
    var views = [`${__dirname}/views`];
    settings.current.modules.forEach(function(moduleItem) {
        views.push(`${__dirname}/modules/${moduleItem.name}/views`);
    });
    app.set('views', views);
    loadModules().then((res) => {
        serverLog(res);
        serverLog("Server up!");
    });
});
<|endoftext|>


<commit_before>/*!
 * use <https://github.com/jonschlinkert/use>
 *
 * Copyright (c) 2015, Jon Schlinkert.
 * Licensed under the MIT License.
 */

'use strict';

var utils = require('./utils')

module.exports = function base(app, opts) {
  if (!utils.isObject(app) && typeof app !== 'function') {
    throw new TypeError('use: expect `app` be an object or function');
  }

  opts = utils.isObject(opts) ? opts : {};
  opts.prop = typeof opts.prop === 'string' ? opts.prop : 'fns';
  opts.prop = opts.prop.length > 0 ? opts.prop : 'fns';

  if (!utils.isArray(app[opts.prop])) {
    utils.define(app, opts.prop, []);
  }

  /**
   * Define a plugin function to be passed to use. The only
   * parameter exposed to the plugin is `app`, the object or function.
   * passed to `use(app)`. `app` is also exposed as `this` in plugins.
   *
   * Additionally, **if a plugin returns a function, the function will
   * be pushed onto the `fns` array**, allowing the plugin to be
   * called at a later point by the `run` method.
   *
   * ```js
   * var use = require('use');
   *
   * // define a plugin
   * function foo(app) {
   *   // do stuff
   * }
   *
   * var app = function(){};
   * use(app);
   *
   * // register plugins
   * app.use(foo);
   * app.use(bar);
   * app.use(baz);
   * ```
   * @name .use
   * @param {Function} `fn` plugin function to call
   * @api public
   */

  utils.define(app, 'use', use);

  /**
   * Run all plugins on `fns`. Any plugin that returns a function
   * when called by `use` is pushed onto the `fns` array.
   *
   * ```js
   * var config = {};
   * app.run(config);
   * ```
   * @name .run
   * @param {Object} `value` Object to be modified by plugins.
   * @return {Object} Returns the object passed to `run`
   * @api public
   */

  utils.define(app, 'run', function (val) {
    var self = this || app;
    var fns = self[opts.prop];
    decorate(val);
    var len = fns.length, i = -1;
    while (++i < len) val.use(fns[i]);
    return val;
  });

  /**
   * Call plugin `fn`. If a function is returned push it into the
   * `fns` array to be called by the `run` method.
   */

  function use(fn) {
    var self = this || app;
    var plugin = fn.call(self, self);
    if (typeof plugin === 'function') {
      var fns = self[opts.prop];
      fns.push(plugin);
    }
    return self;
  }

  /**
   * Ensure the `.use` method exists on `val`
   */

  function decorate(val) {
    if (utils.isObject(val) && (!val.use || !val.run)) {
      base(val);
    }
  }

  return app;
};
<commit_msg>allow passing options to each plugin and tranform/merge/extend/reuse them with `opts.fn` if given<commit_after>/*!
 * use <https://github.com/jonschlinkert/use>
 *
 * Copyright (c) 2015, Jon Schlinkert.
 * Licensed under the MIT License.
 */

'use strict';

var utils = require('./utils')

module.exports = function base(app, opts) {
  if (!utils.isObject(app) && typeof app !== 'function') {
    throw new TypeError('use: expect `app` be an object or function');
  }

  opts = utils.isObject(opts) ? opts : {};
  opts.prop = typeof opts.prop === 'string' ? opts.prop : 'fns';
  opts.prop = opts.prop.length > 0 ? opts.prop : 'fns';

  if (!utils.isArray(app[opts.prop])) {
    utils.define(app, opts.prop, []);
  }

  /**
   * Define a plugin function to be passed to use. The only
   * parameter exposed to the plugin is `app`, the object or function.
   * passed to `use(app)`. `app` is also exposed as `this` in plugins.
   *
   * Additionally, **if a plugin returns a function, the function will
   * be pushed onto the `fns` array**, allowing the plugin to be
   * called at a later point by the `run` method.
   *
   * ```js
   * var use = require('use');
   *
   * // define a plugin
   * function foo(app) {
   *   // do stuff
   * }
   *
   * var app = function(){};
   * use(app);
   *
   * // register plugins
   * app.use(foo);
   * app.use(bar);
   * app.use(baz);
   * ```
   * @name .use
   * @param {Function} `fn` plugin function to call
   * @api public
   */

  utils.define(app, 'use', use);

  /**
   * Run all plugins on `fns`. Any plugin that returns a function
   * when called by `use` is pushed onto the `fns` array.
   *
   * ```js
   * var config = {};
   * app.run(config);
   * ```
   * @name .run
   * @param {Object} `value` Object to be modified by plugins.
   * @return {Object} Returns the object passed to `run`
   * @api public
   */

  utils.define(app, 'run', function (val) {
    var self = this || app;
    var fns = self[opts.prop];
    decorate(val);
    var len = fns.length, i = -1;
    while (++i < len) val.use(fns[i]);
    return val;
  });

  /**
   * Call plugin `fn`. If a function is returned push it into the
   * `fns` array to be called by the `run` method.
   */

  function use(fn, options) {
    if (typeof fn !== 'function') {
      throw new TypeError('.use expect `fn` be function');
    }
    var self = this || app;

    if (typeof opts.fn === 'function') {
      opts.fn.call(self, self, options);
    }

    var plugin = fn.call(self, self);
    if (typeof plugin === 'function') {
      var fns = self[opts.prop];
      fns.push(plugin);
    }
    return self;
  }

  /**
   * Ensure the `.use` method exists on `val`
   */

  function decorate(val) {
    if (utils.isObject(val) && (!val.use || !val.run)) {
      base(val);
    }
  }

  return app;
};
<|endoftext|>


<commit_before>/**
 * Module dependencies
 */
var request = require('superagent')
  , _ = require('underscore');

// Endpoint template
var endpoint = _.template('http://<%=service%>/service/<%=method%>/lomadee/<%=id%>/<%=country%>/');

module.exports = function (opts) {
  return new Buscape(opts);
};

var Buscape = function Buscape (opts) {
  // Use production by default
  this._service = 'bws.buscape.com';

  // Allow keywords string in place of opts
  opts = 'string' === typeof opts ? {keywords: opts} : opts;

  if (opts.keywords) {
    this._keywords = opts.keywords;
    this.mode = 'search';
  }
};

// Set application id
Buscape.prototype.id = function (id) {
  return this._id = id, this;
};

// Set price range
Buscape.prototype.price = function (price) {
  // Allow min..max style pricerange
  price = ('string' === typeof price) ? price.split('..') : price;

  if (price[0]) this._minPrice = price[0];
  if (price[1]) this._maxPrice = price[0];

  return this;
};

// Set search country
Buscape.prototype.country = function (country) {
  return this._country = country, this;
};

// Enable or disable test mode (make calls to the sanbox, rather than primary)
Buscape.prototype.test = function (test) {
  // Default test to true
  test = (arguments.length === 0 ? true : !!test)
  return this._service = test ? 'sandbox.buscape.com' : 'bws.buscape.com', this;
};

Buscape.prototype.limit = function (limit) {
  // Don't accept falsy or 0 limits
  if (!limit) return this;
  return this._limit = limit, this;
};

Buscape.prototype.one = function (one) {
  // Default one to true
  one = (arguments.length === 0 ? true : !!one)
  return this._one = one, this;
};

Buscape.prototype.done = function (cb) {
  request
    .get(endpoint({
      service: this._service,
      method: 'findProductList',
      id: this._id,
      country: this._country || 'BR'
    }))
    .query({keyword: this._keywords})
    .query({priceMin: this._minPrice})
    .query({priceMax: this._priceMax})
    .query({format: 'json'})
    .end(function (err, res) {
      if (err) return cb(err);
      if (!res.body.product) return cb(new Error('Invalid response'));

      // Format results
      var formatted = format(res.body.product);

      // Limit
      if (this._limit) {
        formatted = _.first(formatted, this._limit);
      }

      // One
      if (this._one) {
        formatted = _.first(formatted);
      }

      return cb(null, formatted);
    }.bind(this));
};

var format = function (products) {
  return products.map(function (product) {
    var p = product.product
      , name = p.productname || p.productshortname
      , price = p.pricemin || p.pricemax
      , offers = p.numoffers
      , currency = p.currency.abbreviation
      , link = productLink(p.links)
      , id = p.id;

    // Filter unusable results
    if (!p || !name || !price || !link) return null;

    return {
      name: name,
      listPrice: price,
      remaining: offers,
      currency: currency,
      url: link,
      id: id
    }
  })
  .filter(function (p) {
    return p !== null
  });
};

var productLink = function (links) {
  var productLink = _.find(links, function (l) {
    return l.link.type === 'product';
  });

  return productLink.link.url || null;
};
<commit_msg>Allow addition of a client ip<commit_after>/**
 * Module dependencies
 */
var request = require('superagent')
  , _ = require('underscore');

// Endpoint template
var endpoint = _.template('http://<%=service%>/service/<%=method%>/lomadee/<%=id%>/<%=country%>/');

module.exports = function (opts) {
  return new Buscape(opts);
};

var Buscape = function Buscape (opts) {
  // Use production by default
  this._service = 'bws.buscape.com';

  // Allow keywords string in place of opts
  opts = 'string' === typeof opts ? {keywords: opts} : opts;

  if (opts.keywords) {
    this._keywords = opts.keywords;
    this.mode = 'search';
  }
};

// Set application id
Buscape.prototype.id = function (id) {
  return this._id = id, this;
};

// Set price range
Buscape.prototype.price = function (price) {
  // Allow min..max style pricerange
  price = ('string' === typeof price) ? price.split('..') : price;

  if (price[0]) this._minPrice = price[0];
  if (price[1]) this._maxPrice = price[0];

  return this;
};

// Set search country
Buscape.prototype.country = function (country) {
  return this._country = country, this;
};

// Enable or disable test mode (make calls to the sanbox, rather than primary)
Buscape.prototype.test = function (test) {
  // Default test to true
  test = (arguments.length === 0 ? true : !!test)
  return this._service = test ? 'sandbox.buscape.com' : 'bws.buscape.com', this;
};

// Return a limited number of results
Buscape.prototype.limit = function (limit) {
  // Don't accept falsy or 0 limits
  if (!limit) return this;
  return this._limit = limit, this;
};

// Return only one result
Buscape.prototype.one = function (one) {
  // Default one to true
  one = (arguments.length === 0 ? true : !!one)
  return this._one = one, this;
};

// Set caller ip address
Buscape.prototype.client = function (ip) {
  return this._client = ip, this;
}

Buscape.prototype.done = function (cb) {
  request
    .get(endpoint({
      service: this._service,
      method: 'findProductList',
      id: this._id,
      country: this._country || 'BR'
    }))
    .query({keyword: this._keywords})
    .query({priceMin: this._minPrice})
    .query({priceMax: this._priceMax})
    .query({clientIp: this._client})
    .query({format: 'json'})
    .end(function (err, res) {
      if (err) return cb(err);
      if (!res.body.product) return cb(new Error('Invalid response'));

      // Format results
      var formatted = format(res.body.product);

      // Limit
      if (this._limit) {
        formatted = _.first(formatted, this._limit);
      }

      // One
      if (this._one) {
        formatted = _.first(formatted);
      }

      return cb(null, formatted);
    }.bind(this));
};

var format = function (products) {
  return products.map(function (product) {
    var p = product.product
      , name = p.productname || p.productshortname
      , price = p.pricemin || p.pricemax
      , offers = p.numoffers
      , currency = p.currency.abbreviation
      , link = productLink(p.links)
      , id = p.id;

    // Filter unusable results
    if (!p || !name || !price || !link) return null;

    return {
      name: name,
      listPrice: price,
      remaining: offers,
      currency: currency,
      url: link,
      id: id
    }
  })
  .filter(function (p) {
    return p !== null
  });
};

var productLink = function (links) {
  var productLink = _.find(links, function (l) {
    return l.link.type === 'product';
  });

  return productLink.link.url || null;
};
<|endoftext|>


<commit_before>'use strict'
const r = require('ramda')
const express = require('express')
const alexa = require('alexa-app')
const handlers = require('./handlers')

const app = express()
const alexaApp = new alexa.app(process.env.APP_NAME || 'alexa');

app.set('view engine', 'ejs')
alexaApp.express({
  endpoint: 'alexa',
  expressApp: app,
  checkCert: process.env.ENV === 'production' ? true : false,
  debug: process.env.ENV === 'production' ? false : true,
})

alexaApp.launch((req, res) => response.say('Try asking about the next train on an operator from a station.'))
alexaApp.intent('trainIntent', {
  'slots': {
    'ON': 'LITERAL',
    'FROM': 'LITERAL',
    'TO': 'LITERAL'
  },
  'utterances': [
    'next train on {-|ON} from {-|FROM}',
    'next train on {-|ON} from {-|FROM} to {-|TO}'
  ]
}, (req, res) => handlers.next({
  ON: req.slot('ON'),
  FROM: req.slot('FROM'),
  TO: req.slot('TO')
})
  .collect()
  .toCallback((err, res) => {
    if (err) return res.say(err.message)
    return r.compose(
      x => res.say(x),
      obj => `The next trains on ${obj.operator_name} from ${obj.stop_name}` +
        (req.slot('TO') ? ` to ${req.slot('TO')}` : ``) +
        ` are ${obj.schedules}`,
      r.evolve({
        schedules: r.compose(
          r.map(schedule => `${schedule.origin_departure_time} to ${schedule.trip_headsign}`),
          r.join(', '))
      }),
      r.identity)(res)
  }))

app.get('/next/:on/:from/:to', (req, res, next) => handlers.next(req.params)
  .collect()
  .tap(json => res.json(json))
  .toCallback(err => next(err)))

app.get('/next/:on/:from', (req, res, next) => handlers.next(req.params)
  .collect()
  .tap(json => res.json(json))
  .toCallback(err => next(err)))

app.listen(process.env.PORT || 8080, () =>
  console.log(`listening on ${process.env.PORT || 8080} in ${process.env.ENV || 'development'} mode`))
<commit_msg>Match express params to alexa params<commit_after>'use strict'
const r = require('ramda')
const express = require('express')
const alexa = require('alexa-app')
const handlers = require('./handlers')

const app = express()
const alexaApp = new alexa.app(process.env.APP_NAME || 'alexa');

app.set('view engine', 'ejs')
alexaApp.express({
  endpoint: 'alexa',
  expressApp: app,
  checkCert: process.env.ENV === 'production' ? true : false,
  debug: process.env.ENV === 'production' ? false : true,
})

alexaApp.launch((req, res) => response.say('Try asking about the next train on an operator from a station.'))
alexaApp.intent('trainIntent', {
  'slots': {
    'ON': 'LITERAL',
    'FROM': 'LITERAL',
    'TO': 'LITERAL'
  },
  'utterances': [
    'next train on {-|ON} from {-|FROM}',
    'next train on {-|ON} from {-|FROM} to {-|TO}'
  ]
}, (req, res) => handlers.next({
  ON: req.slot('ON'),
  FROM: req.slot('FROM'),
  TO: req.slot('TO')
})
  .collect()
  .toCallback((err, res) => {
    if (err) return res.say(err.message)
    return r.compose(
      x => res.say(x),
      obj => `The next trains on ${obj.operator_name} from ${obj.stop_name}` +
        (req.slot('TO') ? ` to ${req.slot('TO')}` : ``) +
        ` are ${obj.schedules}`,
      r.evolve({
        schedules: r.compose(
          r.map(schedule => `${schedule.origin_departure_time} to ${schedule.trip_headsign}`),
          r.join(', '))
      }),
      r.identity)(res)
  }))

app.get('/next/:ON/:FROM/:TO', (req, res, next) => handlers.next(req.params)
  .collect()
  .tap(json => res.json(json))
  .toCallback(err => next(err)))

app.get('/next/:ON/:FROM', (req, res, next) => handlers.next(req.params)
  .collect()
  .tap(json => res.json(json))
  .toCallback(err => next(err)))

app.listen(process.env.PORT || 8080, () =>
  console.log(`listening on ${process.env.PORT || 8080} in ${process.env.ENV || 'development'} mode`))
<|endoftext|>


<commit_before>/**
 * @providesModule Notifications
 */

'use strict';

var RNNotificationsComponent = require( './component' );

var AppState = RNNotificationsComponent.state;
var RNNotifications = RNNotificationsComponent.component;

var Platform = require('react-native').Platform;

var Notifications = {
	handler: RNNotifications,
	onRegister: false,
	onError: false,
	onNotification: false,
  onRemoteFetch: false,
	isLoaded: false,
	hasPoppedInitialNotification: false,

	isPermissionsRequestPending: false,

	permissions: {
		alert: true,
		badge: true,
		sound: true
	}
};

Notifications.callNative = function(name: String, params: Array) {
	if ( typeof this.handler[name] === 'function' ) {
		if ( typeof params !== 'array' &&
			 typeof params !== 'object' ) {
			params = [];
		}

		return this.handler[name](...params);
	} else {
		return null;
	}
};

/**
 * Configure local and remote notifications
 * @param {Object}		options
 * @param {function}	options.onRegister - Fired when the user registers for remote notifications.
 * @param {function}	options.onNotification - Fired when a remote notification is received.
 * @param {function} 	options.onError - None
 * @param {Object}		options.permissions - Permissions list
 * @param {Boolean}		options.requestPermissions - Check permissions when register
 */
Notifications.configure = function(options: Object) {
	if ( typeof options.onRegister !== 'undefined' ) {
		this.onRegister = options.onRegister;
	}

	if ( typeof options.onError !== 'undefined' ) {
		this.onError = options.onError;
	}

	if ( typeof options.onNotification !== 'undefined' ) {
		this.onNotification = options.onNotification;
	}

	if ( typeof options.permissions !== 'undefined' ) {
		this.permissions = options.permissions;
	}

	if ( typeof options.senderID !== 'undefined' ) {
		this.senderID = options.senderID;
	}

	if ( typeof options.onRemoteFetch !== 'undefined' ) {
		this.onRemoteFetch = options.onRemoteFetch;
	}

	if ( this.isLoaded === false ) {
		this._onRegister = this._onRegister.bind(this);
		this._onNotification = this._onNotification.bind(this);
		this._onRemoteFetch = this._onRemoteFetch.bind(this);
		this.callNative( 'addEventListener', [ 'register', this._onRegister ] );
		this.callNative( 'addEventListener', [ 'notification', this._onNotification ] );
		this.callNative( 'addEventListener', [ 'localNotification', this._onNotification ] );
		Platform.OS === 'android' ? this.callNative( 'addEventListener', [ 'remoteFetch', this._onRemoteFetch ] ) : null

		this.isLoaded = true;
	}

	if ( this.hasPoppedInitialNotification === false &&
			( options.popInitialNotification === undefined || options.popInitialNotification === true ) ) {
		this.popInitialNotification(function(firstNotification) {
			if ( firstNotification !== null ) {
				this._onNotification(firstNotification, true);
			}
		}.bind(this));
		this.hasPoppedInitialNotification = true;
	}

	if ( options.requestPermissions !== false ) {
		this._requestPermissions();
	}

};

/* Unregister */
Notifications.unregister = function() {
	this.callNative( 'removeEventListener', [ 'register', this._onRegister ] )
	this.callNative( 'removeEventListener', [ 'notification', this._onNotification ] )
	this.callNative( 'removeEventListener', [ 'localNotification', this._onNotification ] )
	Platform.OS === 'android' ? this.callNative( 'removeEventListener', [ 'remoteFetch', this._onRemoteFetch ] ) : null
};

/**
 * Local Notifications
 * @param {Object}		details
 * @param {String}		details.message - The message displayed in the notification alert.
 * @param {String}		details.title  -  ANDROID ONLY: The title displayed in the notification alert.
 * @param {String}		details.ticker -  ANDROID ONLY: The ticker displayed in the status bar.
 * @param {Object}		details.userInfo -  iOS ONLY: The userInfo used in the notification alert.
 */
Notifications.localNotification = function(details: Object) {
	if ( Platform.OS === 'ios' ) {
		let soundName = 'default'; // play sound (and vibrate) as default behaviour

		if(details.hasOwnProperty("playSound")) {
			if(details.playSound) {
				if(details.soundName) {
					soundName = details.soundName;
				}
			} else {
				soundName = '';// empty string results in no sound (and no vibration)
			}
		}

		// for valid fields see: https://developer.apple.com/library/ios/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/Chapters/IPhoneOSClientImp.html
		// alertTitle only valid for apple watch: https://developer.apple.com/library/ios/documentation/iPhone/Reference/UILocalNotification_Class/#//apple_ref/occ/instp/UILocalNotification/alertTitle

		this.handler.presentLocalNotification({
			alertTitle: details.title,
			alertBody: details.message,
			alertAction: details.alertAction,
			category: details.category,
			soundName: soundName,
			applicationIconBadgeNumber: details.number,
			userInfo: details.userInfo
		});
	} else {
		this.handler.presentLocalNotification(details);
	}
};

/**
 * Local Notifications Schedule
 * @param {Object}		details (same as localNotification)
 * @param {Date}		details.date - The date and time when the system should deliver the notification
 */
Notifications.localNotificationSchedule = function(details: Object) {
	if ( Platform.OS === 'ios' ) {
		this.handler.scheduleLocalNotification({
			fireDate: details.date.toISOString(),
			alertBody: details.message,
			userInfo: details.userInfo
		});
	} else {
		details.fireDate = details.date.getTime();
		delete details.date;
		this.handler.scheduleLocalNotification(details);
	}
};

/* Internal Functions */
Notifications._onRegister = function(token: String) {
	if ( this.onRegister !== false ) {
		this.onRegister({
			token: token,
			os: Platform.OS
		});
	}
};

Notifications._onRemoteFetch = function(notificationData: Object) {
	if ( this.onRemoteFetch !== false ) {
		this.onRemoteFetch(notificationData)
	}
}

Notifications._onNotification = function(data, isFromBackground = null) {
	if ( isFromBackground === null ) {
		isFromBackground = (
			data.foreground === false ||
			AppState.currentState === 'background'
		);
	}

	if ( this.onNotification !== false ) {
		if ( Platform.OS === 'ios' ) {
			this.onNotification({
				foreground: ! isFromBackground,
				userInteraction: isFromBackground,
				message: data.getMessage(),
				data: data.getData(),
				badge: data.getBadgeCount(),
				alert: data.getAlert(),
				sound: data.getSound(),
				finish: data.finish
			});
		} else {
			var notificationData = {
				foreground: ! isFromBackground,
				...data
			};

			if ( typeof notificationData.data === 'string' ) {
				try {
					notificationData.data = JSON.parse(notificationData.data);
				} catch(e) {
					/* void */
				}
			}

			this.onNotification(notificationData);
		}
	}
};

/* onResultPermissionResult */
Notifications._onPermissionResult = function() {
	this.isPermissionsRequestPending = false;
};

// Prevent requestPermissions called twice if ios result is pending
Notifications._requestPermissions = function() {
	if ( Platform.OS === 'ios' ) {
		if ( this.isPermissionsRequestPending === false ) {
			this.isPermissionsRequestPending = true;
			return this.callNative( 'requestPermissions', [ this.permissions ])
							.then(this._onPermissionResult.bind(this))
							.catch(this._onPermissionResult.bind(this));
		}
	} else if ( typeof this.senderID !== 'undefined' ) {
		return this.callNative( 'requestPermissions', [ this.senderID ]);
	}
};

// Stock requestPermissions function
Notifications.requestPermissions = function() {
	if ( Platform.OS === 'ios' ) {
		return this.callNative( 'requestPermissions', [ this.permissions ]);
	} else if ( typeof this.senderID !== 'undefined' ) {
		return this.callNative( 'requestPermissions', [ this.senderID ]);
	}
};

/* Fallback functions */
Notifications.presentLocalNotification = function() {
	return this.callNative('presentLocalNotification', arguments);
};

Notifications.scheduleLocalNotification = function() {
	return this.callNative('scheduleLocalNotification', arguments);
};

Notifications.cancelLocalNotifications = function() {
	return this.callNative('cancelLocalNotifications', arguments);
};

Notifications.cancelAllLocalNotifications = function() {
	return this.callNative('cancelAllLocalNotifications', arguments);
};

Notifications.setApplicationIconBadgeNumber = function() {
	return this.callNative('setApplicationIconBadgeNumber', arguments);
};

Notifications.getApplicationIconBadgeNumber = function() {
	return this.callNative('getApplicationIconBadgeNumber', arguments);
};

Notifications.popInitialNotification = function(handler) {
	this.callNative('getInitialNotification').then(function(result){
		handler(result);
	});
};

Notifications.abandonPermissions = function() {
	return this.callNative('abandonPermissions', arguments);
};

Notifications.checkPermissions = function() {
	return this.callNative('checkPermissions', arguments);
};

Notifications.registerNotificationActions = function() {
	return this.callNative('registerNotificationActions', arguments)
}

Notifications.clearAllNotifications = function() {
	// Only available for Android
	return this.callNative('clearAllNotifications', arguments)
}

module.exports = Notifications;
<commit_msg>added delegate finish method to android for consistency (it is a no-op)<commit_after>/**
 * @providesModule Notifications
 */

'use strict';

var RNNotificationsComponent = require( './component' );

var AppState = RNNotificationsComponent.state;
var RNNotifications = RNNotificationsComponent.component;

var Platform = require('react-native').Platform;

var Notifications = {
	handler: RNNotifications,
	onRegister: false,
	onError: false,
	onNotification: false,
  onRemoteFetch: false,
	isLoaded: false,
	hasPoppedInitialNotification: false,

	isPermissionsRequestPending: false,

	permissions: {
		alert: true,
		badge: true,
		sound: true
	}
};

Notifications.callNative = function(name: String, params: Array) {
	if ( typeof this.handler[name] === 'function' ) {
		if ( typeof params !== 'array' &&
			 typeof params !== 'object' ) {
			params = [];
		}

		return this.handler[name](...params);
	} else {
		return null;
	}
};

/**
 * Configure local and remote notifications
 * @param {Object}		options
 * @param {function}	options.onRegister - Fired when the user registers for remote notifications.
 * @param {function}	options.onNotification - Fired when a remote notification is received.
 * @param {function} 	options.onError - None
 * @param {Object}		options.permissions - Permissions list
 * @param {Boolean}		options.requestPermissions - Check permissions when register
 */
Notifications.configure = function(options: Object) {
	if ( typeof options.onRegister !== 'undefined' ) {
		this.onRegister = options.onRegister;
	}

	if ( typeof options.onError !== 'undefined' ) {
		this.onError = options.onError;
	}

	if ( typeof options.onNotification !== 'undefined' ) {
		this.onNotification = options.onNotification;
	}

	if ( typeof options.permissions !== 'undefined' ) {
		this.permissions = options.permissions;
	}

	if ( typeof options.senderID !== 'undefined' ) {
		this.senderID = options.senderID;
	}

	if ( typeof options.onRemoteFetch !== 'undefined' ) {
		this.onRemoteFetch = options.onRemoteFetch;
	}

	if ( this.isLoaded === false ) {
		this._onRegister = this._onRegister.bind(this);
		this._onNotification = this._onNotification.bind(this);
		this._onRemoteFetch = this._onRemoteFetch.bind(this);
		this.callNative( 'addEventListener', [ 'register', this._onRegister ] );
		this.callNative( 'addEventListener', [ 'notification', this._onNotification ] );
		this.callNative( 'addEventListener', [ 'localNotification', this._onNotification ] );
		Platform.OS === 'android' ? this.callNative( 'addEventListener', [ 'remoteFetch', this._onRemoteFetch ] ) : null

		this.isLoaded = true;
	}

	if ( this.hasPoppedInitialNotification === false &&
			( options.popInitialNotification === undefined || options.popInitialNotification === true ) ) {
		this.popInitialNotification(function(firstNotification) {
			if ( firstNotification !== null ) {
				this._onNotification(firstNotification, true);
			}
		}.bind(this));
		this.hasPoppedInitialNotification = true;
	}

	if ( options.requestPermissions !== false ) {
		this._requestPermissions();
	}

};

/* Unregister */
Notifications.unregister = function() {
	this.callNative( 'removeEventListener', [ 'register', this._onRegister ] )
	this.callNative( 'removeEventListener', [ 'notification', this._onNotification ] )
	this.callNative( 'removeEventListener', [ 'localNotification', this._onNotification ] )
	Platform.OS === 'android' ? this.callNative( 'removeEventListener', [ 'remoteFetch', this._onRemoteFetch ] ) : null
};

/**
 * Local Notifications
 * @param {Object}		details
 * @param {String}		details.message - The message displayed in the notification alert.
 * @param {String}		details.title  -  ANDROID ONLY: The title displayed in the notification alert.
 * @param {String}		details.ticker -  ANDROID ONLY: The ticker displayed in the status bar.
 * @param {Object}		details.userInfo -  iOS ONLY: The userInfo used in the notification alert.
 */
Notifications.localNotification = function(details: Object) {
	if ( Platform.OS === 'ios' ) {
		let soundName = 'default'; // play sound (and vibrate) as default behaviour

		if(details.hasOwnProperty("playSound")) {
			if(details.playSound) {
				if(details.soundName) {
					soundName = details.soundName;
				}
			} else {
				soundName = '';// empty string results in no sound (and no vibration)
			}
		}

		// for valid fields see: https://developer.apple.com/library/ios/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/Chapters/IPhoneOSClientImp.html
		// alertTitle only valid for apple watch: https://developer.apple.com/library/ios/documentation/iPhone/Reference/UILocalNotification_Class/#//apple_ref/occ/instp/UILocalNotification/alertTitle

		this.handler.presentLocalNotification({
			alertTitle: details.title,
			alertBody: details.message,
			alertAction: details.alertAction,
			category: details.category,
			soundName: soundName,
			applicationIconBadgeNumber: details.number,
			userInfo: details.userInfo
		});
	} else {
		this.handler.presentLocalNotification(details);
	}
};

/**
 * Local Notifications Schedule
 * @param {Object}		details (same as localNotification)
 * @param {Date}		details.date - The date and time when the system should deliver the notification
 */
Notifications.localNotificationSchedule = function(details: Object) {
	if ( Platform.OS === 'ios' ) {
		this.handler.scheduleLocalNotification({
			fireDate: details.date.toISOString(),
			alertBody: details.message,
			userInfo: details.userInfo
		});
	} else {
		details.fireDate = details.date.getTime();
		delete details.date;
		this.handler.scheduleLocalNotification(details);
	}
};

/* Internal Functions */
Notifications._onRegister = function(token: String) {
	if ( this.onRegister !== false ) {
		this.onRegister({
			token: token,
			os: Platform.OS
		});
	}
};

Notifications._onRemoteFetch = function(notificationData: Object) {
	if ( this.onRemoteFetch !== false ) {
		this.onRemoteFetch(notificationData)
	}
}

Notifications._onNotification = function(data, isFromBackground = null) {
	if ( isFromBackground === null ) {
		isFromBackground = (
			data.foreground === false ||
			AppState.currentState === 'background'
		);
	}

	if ( this.onNotification !== false ) {
		if ( Platform.OS === 'ios' ) {
			this.onNotification({
				foreground: ! isFromBackground,
				userInteraction: isFromBackground,
				message: data.getMessage(),
				data: data.getData(),
				badge: data.getBadgeCount(),
				alert: data.getAlert(),
				sound: data.getSound(),
				finish: data.finish
			});
		} else {
			var notificationData = {
				foreground: ! isFromBackground,
  			finish: () => {},
				...data
			};

			if ( typeof notificationData.data === 'string' ) {
				try {
					notificationData.data = JSON.parse(notificationData.data);
				} catch(e) {
					/* void */
				}
			}

			this.onNotification(notificationData);
		}
	}
};

/* onResultPermissionResult */
Notifications._onPermissionResult = function() {
	this.isPermissionsRequestPending = false;
};

// Prevent requestPermissions called twice if ios result is pending
Notifications._requestPermissions = function() {
	if ( Platform.OS === 'ios' ) {
		if ( this.isPermissionsRequestPending === false ) {
			this.isPermissionsRequestPending = true;
			return this.callNative( 'requestPermissions', [ this.permissions ])
							.then(this._onPermissionResult.bind(this))
							.catch(this._onPermissionResult.bind(this));
		}
	} else if ( typeof this.senderID !== 'undefined' ) {
		return this.callNative( 'requestPermissions', [ this.senderID ]);
	}
};

// Stock requestPermissions function
Notifications.requestPermissions = function() {
	if ( Platform.OS === 'ios' ) {
		return this.callNative( 'requestPermissions', [ this.permissions ]);
	} else if ( typeof this.senderID !== 'undefined' ) {
		return this.callNative( 'requestPermissions', [ this.senderID ]);
	}
};

/* Fallback functions */
Notifications.presentLocalNotification = function() {
	return this.callNative('presentLocalNotification', arguments);
};

Notifications.scheduleLocalNotification = function() {
	return this.callNative('scheduleLocalNotification', arguments);
};

Notifications.cancelLocalNotifications = function() {
	return this.callNative('cancelLocalNotifications', arguments);
};

Notifications.cancelAllLocalNotifications = function() {
	return this.callNative('cancelAllLocalNotifications', arguments);
};

Notifications.setApplicationIconBadgeNumber = function() {
	return this.callNative('setApplicationIconBadgeNumber', arguments);
};

Notifications.getApplicationIconBadgeNumber = function() {
	return this.callNative('getApplicationIconBadgeNumber', arguments);
};

Notifications.popInitialNotification = function(handler) {
	this.callNative('getInitialNotification').then(function(result){
		handler(result);
	});
};

Notifications.abandonPermissions = function() {
	return this.callNative('abandonPermissions', arguments);
};

Notifications.checkPermissions = function() {
	return this.callNative('checkPermissions', arguments);
};

Notifications.registerNotificationActions = function() {
	return this.callNative('registerNotificationActions', arguments)
}

Notifications.clearAllNotifications = function() {
	// Only available for Android
	return this.callNative('clearAllNotifications', arguments)
}

module.exports = Notifications;
<|endoftext|>


<commit_before>var DeviceClient = require('upnp-device-client');
var util = require('util');
var debug = require('debug')('upnp-mediarenderer-client');
var et = require('elementtree');

var MEDIA_EVENTS = [
  'status',
  'loading',
  'playing',
  'paused',
  'stopped',
  'speedChanged'
];

function MediaRendererClient(url) {
  DeviceClient.call(this, url);
  this.instanceId = 0;

  // Subscribe / unsubscribe from AVTransport depending
  // on relevant registered / removed event listeners.
  var self = this;
  var refs = 0;
  var receivedState;

  this.addListener('newListener', function(eventName, listener) {
    if(MEDIA_EVENTS.indexOf(eventName) === -1) return;
    if(refs === 0) {
      receivedState = false;
      self.subscribe('AVTransport', onstatus);
    }
    refs++;
  });

  this.addListener('removeListener', function(eventName, listener) {
    if(MEDIA_EVENTS.indexOf(eventName) === -1) return;
    refs--;
    if(refs === 0) self.unsubscribe('AVTransport', onstatus);
  });

  function onstatus(e) {
    self.emit('status', e);

    if(!receivedState) {
      // Starting from here we only want state updates.
      // As the first received event is the full service state, we ignore it.
      receivedState = true;
      return;
    }

    if(e.hasOwnProperty('TransportState')) {
      switch(e.TransportState) {
        case 'TRANSITIONING':
          self.emit('loading');
          break;
        case 'PLAYING':
          self.emit('playing');
          break;
        case 'PAUSED_PLAYBACK':
          self.emit('paused');
          break;
        case 'STOPPED':
          self.emit('stopped');
          break;
      }
    }

    if(e.hasOwnProperty('TransportPlaySpeed')) {
      self.emit('speedChanged', Number(e.TransportPlaySpeed));
    }
  }

}

util.inherits(MediaRendererClient, DeviceClient);

MediaRendererClient.prototype.getSupportedProtocols = function(callback) {
  this.callAction('ConnectionManager', 'GetProtocolInfo', {}, function(err, result) {
    if(err) return callback(err);

    //
    // Here we leave off the `Source` field as we're hopefuly dealing with a Sink-only device.
    //
    var lines = result.Sink.split(',');

    var protocols = lines.map(function(line) {
      var tmp = line.split(':');
      return {
        protocol: tmp[0],
        network: tmp[1],
        contentFormat: tmp[2],
        additionalInfo: tmp[3]
      };
    });

    callback(null, protocols);
  });
};

MediaRendererClient.prototype.getPosition = function(callback) {
  this.callAction('AVTransport', 'GetPositionInfo', { InstanceID: this.instanceId }, function(err, result) {
    if(err) return callback(err);

    var str = result.AbsTime !== 'NOT_IMPLEMENTED'
      ? result.AbsTime
      : result.RelTime;

    callback(null, parseTime(str));
  });
};

MediaRendererClient.prototype.getDuration = function(callback) {
  this.callAction('AVTransport', 'GetMediaInfo', { InstanceID: this.instanceId }, function(err, result) {
    if(err) return callback(err);
    callback(null, parseTime(result.MediaDuration));
  });
};

MediaRendererClient.prototype.load = function(url, options, callback) {
  var self = this;
  if(typeof options === 'function') {
    callback = options;
    options = {};
  }

  var dlnaFlags = options.dlnaFlags || ':*';
  var contentType = options.contentType || 'video/mpeg'; // Default to something generic
  var protocolInfo = 'http-get:*:' + contentType + dlnaFlags;

  var metadata = options.metadata || {};
  metadata.url = url;
  metadata.protocolInfo = protocolInfo;

  var params = {
    RemoteProtocolInfo: protocolInfo,
    PeerConnectionManager: null,
    PeerConnectionID: -1,
    Direction: 'Input'
  };

  this.callAction('ConnectionManager', 'PrepareForConnection', params, function(err, result) {
    if(err) {
      if(err.code !== 'ENOACTION') {
        return callback(err);
      }
      //
      // If PrepareForConnection is not implemented, we keep the default (0) InstanceID
      //
    } else {
      self.instanceId = result.AVTransportID;
    }

    var params = {
      InstanceID: self.instanceId,
      CurrentURI: url,
      CurrentURIMetaData: buildMetadata(metadata)
    };

    self.callAction('AVTransport', 'SetAVTransportURI', params, function(err) {
      if(err) return callback(err);
      if(options.autoplay) {
        self.play(callback);
        return;
      }
      callback();
    });
  });
};

MediaRendererClient.prototype.play = function(callback) {
  var params = {
    InstanceID: this.instanceId,
    Speed: 1,
  };
  this.callAction('AVTransport', 'Play', params, callback || noop);
};

MediaRendererClient.prototype.pause = function(callback) {
  var params = {
    InstanceID: this.instanceId
  };
  this.callAction('AVTransport', 'Pause', params, callback || noop);
};

MediaRendererClient.prototype.stop = function(callback) {
  var params = {
    InstanceID: this.instanceId
  };
  this.callAction('AVTransport', 'Stop', params, callback || noop);
};

MediaRendererClient.prototype.seek = function(seconds, callback) {
  var params = {
    InstanceID: this.instanceId,
    Unit: 'REL_TIME',
    Target: formatTime(seconds)
  };
  this.callAction('AVTransport', 'Seek', params, callback || noop);
};

MediaRendererClient.prototype.getVolume = function(callback) {
  this.callAction('RenderingControl', 'GetVolume', { InstanceID: this.instanceId,Channel: 'Master'}, function(err, result) {
    if(err) return callback(err);
    callback(null, parseInt(result.CurrentVolume));
  });
};

MediaRendererClient.prototype.setVolume = function(volume, callback) {
  var params = {
    InstanceID: this.instanceId,
    Channel: 'Master',
    DesiredVolume: volume
  };
  this.callAction('RenderingControl', 'SetVolume', params, callback || noop);
};

MediaRendererClient.prototype.getTransportInfo = function(callback) {
  this.callAction('AVTransport', 'GetTransportInfo', { InstanceID: this.instanceId }, callback)
}

function formatTime(seconds) {
  var h = 0;
  var m = 0;
  var s = 0;
  h = Math.floor((seconds - (h * 0)    - (m * 0 )) / 3600);
  m = Math.floor((seconds - (h * 3600) - (m * 0 )) / 60);
  s =            (seconds - (h * 3600) - (m * 60));

  function pad(v) {
    return (v < 10) ? '0' + v : v;
  }

  return [pad(h), pad(m), pad(s)].join(':');
}

function parseTime(time) {
  var parts = time.split(':').map(Number);
  return parts[0] * 3600 + parts[1] * 60 + parts[2];
}

function buildMetadata(metadata) {
  var didl = et.Element('DIDL-Lite');
  didl.set('xmlns', 'urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/');
  didl.set('xmlns:dc', 'http://purl.org/dc/elements/1.1/');
  didl.set('xmlns:upnp', 'urn:schemas-upnp-org:metadata-1-0/upnp/');
  didl.set('xmlns:sec', 'http://www.sec.co.kr/');

  var item = et.SubElement(didl, 'item');
  item.set('id', 0);
  item.set('parentID', -1);
  item.set('restricted', false);

  var OBJECT_CLASSES = {
    'audio': 'object.item.audioItem.musicTrack',
    'video': 'object.item.videoItem.movie',
    'image': 'object.item.imageItem.photo'
  }

  if(metadata.type) {
    var klass = et.SubElement(item, 'upnp:class');
    klass.text = OBJECT_CLASSES[metadata.type];
  }

  if(metadata.title) {
    var title = et.SubElement(item, 'dc:title');
    title.text = metadata.title;
  }

  if(metadata.creator) {
    var creator = et.SubElement(item, 'dc:creator');
    creator.text = metadata.creator;
  }

  if(metadata.url && metadata.protocolInfo) {
    var res = et.SubElement(item, 'res');
    res.set('protocolInfo', metadata.protocolInfo);
    res.text = metadata.url;
  }

  if(metadata.subtitlesUrl) {
    var captionInfo = et.SubElement(item, 'sec:CaptionInfo');
    captionInfo.set('sec:type', 'srt');
    captionInfo.text = metadata.subtitlesUrl;

    var captionInfoEx = et.SubElement(item, 'sec:CaptionInfoEx');
    captionInfoEx.set('sec:type', 'srt');
    captionInfoEx.text = metadata.subtitlesUrl;

    // Create a second `res` for the subtitles
    var res = et.SubElement(item, 'res');
    res.set('protocolInfo', 'http-get:*:text/srt:*');
    res.text = metadata.subtitlesUrl;
  }

  var doc = new et.ElementTree(didl);
  var xml = doc.write({ xml_declaration: false });

  return xml;
}

function noop() {}

module.exports = MediaRendererClient;
<commit_msg>dlnaFeatures<commit_after>var DeviceClient = require('upnp-device-client');
var util = require('util');
var debug = require('debug')('upnp-mediarenderer-client');
var et = require('elementtree');

var MEDIA_EVENTS = [
  'status',
  'loading',
  'playing',
  'paused',
  'stopped',
  'speedChanged'
];

function MediaRendererClient(url) {
  DeviceClient.call(this, url);
  this.instanceId = 0;

  // Subscribe / unsubscribe from AVTransport depending
  // on relevant registered / removed event listeners.
  var self = this;
  var refs = 0;
  var receivedState;

  this.addListener('newListener', function(eventName, listener) {
    if(MEDIA_EVENTS.indexOf(eventName) === -1) return;
    if(refs === 0) {
      receivedState = false;
      self.subscribe('AVTransport', onstatus);
    }
    refs++;
  });

  this.addListener('removeListener', function(eventName, listener) {
    if(MEDIA_EVENTS.indexOf(eventName) === -1) return;
    refs--;
    if(refs === 0) self.unsubscribe('AVTransport', onstatus);
  });

  function onstatus(e) {
    self.emit('status', e);

    if(!receivedState) {
      // Starting from here we only want state updates.
      // As the first received event is the full service state, we ignore it.
      receivedState = true;
      return;
    }

    if(e.hasOwnProperty('TransportState')) {
      switch(e.TransportState) {
        case 'TRANSITIONING':
          self.emit('loading');
          break;
        case 'PLAYING':
          self.emit('playing');
          break;
        case 'PAUSED_PLAYBACK':
          self.emit('paused');
          break;
        case 'STOPPED':
          self.emit('stopped');
          break;
      }
    }

    if(e.hasOwnProperty('TransportPlaySpeed')) {
      self.emit('speedChanged', Number(e.TransportPlaySpeed));
    }
  }

}

util.inherits(MediaRendererClient, DeviceClient);

MediaRendererClient.prototype.getSupportedProtocols = function(callback) {
  this.callAction('ConnectionManager', 'GetProtocolInfo', {}, function(err, result) {
    if(err) return callback(err);

    //
    // Here we leave off the `Source` field as we're hopefuly dealing with a Sink-only device.
    //
    var lines = result.Sink.split(',');

    var protocols = lines.map(function(line) {
      var tmp = line.split(':');
      return {
        protocol: tmp[0],
        network: tmp[1],
        contentFormat: tmp[2],
        additionalInfo: tmp[3]
      };
    });

    callback(null, protocols);
  });
};

MediaRendererClient.prototype.getPosition = function(callback) {
  this.callAction('AVTransport', 'GetPositionInfo', { InstanceID: this.instanceId }, function(err, result) {
    if(err) return callback(err);

    var str = result.AbsTime !== 'NOT_IMPLEMENTED'
      ? result.AbsTime
      : result.RelTime;

    callback(null, parseTime(str));
  });
};

MediaRendererClient.prototype.getDuration = function(callback) {
  this.callAction('AVTransport', 'GetMediaInfo', { InstanceID: this.instanceId }, function(err, result) {
    if(err) return callback(err);
    callback(null, parseTime(result.MediaDuration));
  });
};

MediaRendererClient.prototype.load = function(url, options, callback) {
  var self = this;
  if(typeof options === 'function') {
    callback = options;
    options = {};
  }

  var dlnaFeatures = options.dlnaFeatures || '*';
  var contentType = options.contentType || 'video/mpeg'; // Default to something generic
  var protocolInfo = 'http-get:*:' + contentType + ':' + dlnaFeatures;

  var metadata = options.metadata || {};
  metadata.url = url;
  metadata.protocolInfo = protocolInfo;

  var params = {
    RemoteProtocolInfo: protocolInfo,
    PeerConnectionManager: null,
    PeerConnectionID: -1,
    Direction: 'Input'
  };

  this.callAction('ConnectionManager', 'PrepareForConnection', params, function(err, result) {
    if(err) {
      if(err.code !== 'ENOACTION') {
        return callback(err);
      }
      //
      // If PrepareForConnection is not implemented, we keep the default (0) InstanceID
      //
    } else {
      self.instanceId = result.AVTransportID;
    }

    var params = {
      InstanceID: self.instanceId,
      CurrentURI: url,
      CurrentURIMetaData: buildMetadata(metadata)
    };

    self.callAction('AVTransport', 'SetAVTransportURI', params, function(err) {
      if(err) return callback(err);
      if(options.autoplay) {
        self.play(callback);
        return;
      }
      callback();
    });
  });
};

MediaRendererClient.prototype.play = function(callback) {
  var params = {
    InstanceID: this.instanceId,
    Speed: 1,
  };
  this.callAction('AVTransport', 'Play', params, callback || noop);
};

MediaRendererClient.prototype.pause = function(callback) {
  var params = {
    InstanceID: this.instanceId
  };
  this.callAction('AVTransport', 'Pause', params, callback || noop);
};

MediaRendererClient.prototype.stop = function(callback) {
  var params = {
    InstanceID: this.instanceId
  };
  this.callAction('AVTransport', 'Stop', params, callback || noop);
};

MediaRendererClient.prototype.seek = function(seconds, callback) {
  var params = {
    InstanceID: this.instanceId,
    Unit: 'REL_TIME',
    Target: formatTime(seconds)
  };
  this.callAction('AVTransport', 'Seek', params, callback || noop);
};

MediaRendererClient.prototype.getVolume = function(callback) {
  this.callAction('RenderingControl', 'GetVolume', { InstanceID: this.instanceId,Channel: 'Master'}, function(err, result) {
    if(err) return callback(err);
    callback(null, parseInt(result.CurrentVolume));
  });
};

MediaRendererClient.prototype.setVolume = function(volume, callback) {
  var params = {
    InstanceID: this.instanceId,
    Channel: 'Master',
    DesiredVolume: volume
  };
  this.callAction('RenderingControl', 'SetVolume', params, callback || noop);
};

MediaRendererClient.prototype.getTransportInfo = function(callback) {
  this.callAction('AVTransport', 'GetTransportInfo', { InstanceID: this.instanceId }, callback)
}

function formatTime(seconds) {
  var h = 0;
  var m = 0;
  var s = 0;
  h = Math.floor((seconds - (h * 0)    - (m * 0 )) / 3600);
  m = Math.floor((seconds - (h * 3600) - (m * 0 )) / 60);
  s =            (seconds - (h * 3600) - (m * 60));

  function pad(v) {
    return (v < 10) ? '0' + v : v;
  }

  return [pad(h), pad(m), pad(s)].join(':');
}

function parseTime(time) {
  var parts = time.split(':').map(Number);
  return parts[0] * 3600 + parts[1] * 60 + parts[2];
}

function buildMetadata(metadata) {
  var didl = et.Element('DIDL-Lite');
  didl.set('xmlns', 'urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/');
  didl.set('xmlns:dc', 'http://purl.org/dc/elements/1.1/');
  didl.set('xmlns:upnp', 'urn:schemas-upnp-org:metadata-1-0/upnp/');
  didl.set('xmlns:sec', 'http://www.sec.co.kr/');

  var item = et.SubElement(didl, 'item');
  item.set('id', 0);
  item.set('parentID', -1);
  item.set('restricted', false);

  var OBJECT_CLASSES = {
    'audio': 'object.item.audioItem.musicTrack',
    'video': 'object.item.videoItem.movie',
    'image': 'object.item.imageItem.photo'
  }

  if(metadata.type) {
    var klass = et.SubElement(item, 'upnp:class');
    klass.text = OBJECT_CLASSES[metadata.type];
  }

  if(metadata.title) {
    var title = et.SubElement(item, 'dc:title');
    title.text = metadata.title;
  }

  if(metadata.creator) {
    var creator = et.SubElement(item, 'dc:creator');
    creator.text = metadata.creator;
  }

  if(metadata.url && metadata.protocolInfo) {
    var res = et.SubElement(item, 'res');
    res.set('protocolInfo', metadata.protocolInfo);
    res.text = metadata.url;
  }

  if(metadata.subtitlesUrl) {
    var captionInfo = et.SubElement(item, 'sec:CaptionInfo');
    captionInfo.set('sec:type', 'srt');
    captionInfo.text = metadata.subtitlesUrl;

    var captionInfoEx = et.SubElement(item, 'sec:CaptionInfoEx');
    captionInfoEx.set('sec:type', 'srt');
    captionInfoEx.text = metadata.subtitlesUrl;

    // Create a second `res` for the subtitles
    var res = et.SubElement(item, 'res');
    res.set('protocolInfo', 'http-get:*:text/srt:*');
    res.text = metadata.subtitlesUrl;
  }

  var doc = new et.ElementTree(didl);
  var xml = doc.write({ xml_declaration: false });

  return xml;
}

function noop() {}

module.exports = MediaRendererClient;
<|endoftext|>


<commit_before>

// load dependencies
var fs = require('fs'),						//filesystem
	path = require('path'),					//paths
	recursive = require('recursive-readdir'),
	escape = require('escape-html'),
	console = require('better-console'),
	helpers = require('./helpers');

module.exports = function(){

	// *************************************
	//  COMPONENT LIBRARY GENERATOR
	//
	//	...
	//
	// *************************************

	// -------------------------------------
	//  Settings
	// -------------------------------------
	var srcpath = 'src/components';
	var allowedFileExtensions = ['md', 'json', 'Nunjucks'];

	// -------------------------------------
	//  Helper functions
	// -------------------------------------
	helpers.init();
	var objTypeString = Object.prototype.toString; // http://blog.niftysnippets.org/2010/09/say-what.html

	/**
	 * Initialize calls 3 basic functions
	 * a) save treeToJSON
	 * b) generateNavigation
	 * c) generateContent
	 */
	function initialization(){

		// -------------------------------------
		//  save fileTree to JSON
		// -------------------------------------
		console.info("Save Directory Tree in JSON");
		console.time("Timer");
		var fileTree = treeToJSON(srcpath, allowedFileExtensions);
		// console.table(fileTree);
		// console.log(fileTree);
		console.timeEnd("Timer");

		// -------------------------------------
		//  generate navigation
		// -------------------------------------
		// console.info("Generate navigation");
		// console.time("Timer");
		// generateNavigation(fileTree);
		// // console.table(fileTree);
		// console.timeEnd("Timer");

		// -------------------------------------
		//  generate content
		// -------------------------------------
		// console.info("Generate content");
		// console.time("Timer");
		var content = generateContent(fileTree);
		// // console.table(fileTree);
		// console.timeEnd("Timer");

		// -------------------------------------
		//  filling template
		// -------------------------------------
		var placeholder = {
			styleguide: /\[\[\[styleguide-placeholder]]]/g
		},
			pageTemplate 	= 'src/styleguide/source/page.template',
			resultUrl 	 	= 'src/styleguide/index.nunjucks',
			pageSource 		= fs.readFileSync(pageTemplate).toString(),
			newSource	 	= pageSource
								.replace(placeholder.styleguide, content);
								// .replace(placeholder.url, tmpl[index].replace(/\\/g,"/"))
		// console.log(content);
		// console.log(newSource);
		fs.writeFileSync(resultUrl, newSource);
	}

	/**
	 * Recursive function which walks the file-tree and saves it in to the JSON
	 * @param  {string} dir - recursion starting directory
	 * @param  {array} allowedFileExtensions - array of allowedFileExtensions file types (otherwise, ignore is more complicated)
	 * @return {JSON}
	 */
	function treeToJSON(dir, allowedFileExtensions){
		var output = [];				// variable which later will be returned.
		var re = /(?:\.([^.]+))?$/; 	// get extension regexp
		allowedFileExtensions.toLowerCase();   		// convert allowedFileExtensions filetypes to lowercase

		// Produce this JSON structure:
		// [{
		// 	"name": "january",
		// 	"path": "winter/january",
		// 	"type": "directory",
		// 	"children": [
		// 		{
		// 			"path": "winter/january/ski.png",
		// 			"name": "ski.png",
		// 			"type": "png"
		// 		},
		// 		{
		// 			"path": "winter/january/snowboard.jpg",
		// 			"name": "snowboard.jpg",
		// 			"type": "jpg"
		// 		}
		// 	]
		// }]
		fs.readdirSync(dir).filter(function(file) {
			var whitelisted = false;
			var item = {};
			item.name = file;
			item.path = path.join(dir, file);
			item.type = (re.exec(file)[1] == undefined)? 'folder': re.exec(file)[1].toLowerCase();

			var isDirectory = fs.statSync(item.path).isDirectory();
			if(isDirectory){
				// if directory call function on new path
				item.children = treeToJSON(item.path, allowedFileExtensions);
				// console.table(item.children);
				whitelisted = true;
			}
			else{
				// if file, make sure it's whitelisted
				if(objTypeString.call(allowedFileExtensions) == "[object Array]"){
					if(allowedFileExtensions.indexOf(item.type) > -1){
						whitelisted = true;
					}
				}
				else if(objTypeString.call(allowedFileExtensions) == "[object String]"){
					if(allowedFileExtensions==item.type){
						whitelisted = true;
					}
				}
			}
			if(whitelisted) output.push(item);
		});
		return output;
	}

	function generateNavigation(tree){

	}

	function generateContent(tree){
		var folderTemplate 	= 'src/styleguide/source/folder.template';
		var templateTemplate 	= 'src/styleguide/source/template.template';
		var outputUrl 			= 'src/styleguide/index.nunjucks';
		var content 			= 'There are no components in components folder';
		var placeholder = {
			url: /\[\[\[template-url]]]/g,
			data: /\[\[\[template-data]]]/g,
			info: /\[\[\[folder-info]]]/g,
			tmpl: /\[\[\[folder-templates]]]/g
		};

		// -------------------------------------
		//  get array of components
		// -------------------------------------
		var components = JSONtoFile(tree);
		//console.table(components);
		// console.log(components.length);

		// -------------------------------------
		//  read template
		// -------------------------------------
		var folderContents = fs.readFileSync(folderTemplate).toString();
		var templateContents = fs.readFileSync(templateTemplate).toString();

		// -------------------------------------
		//  walk the tree
		// -------------------------------------
		if(components.length>0) content = "";
		for (var key in components) {
			if (components.hasOwnProperty(key)) {
				console.info("Building a folder");
				var tmplContent = '';
				var tmpl = components[key].tmpl;
				var info = (objTypeString.call(components[key].info[0]) == "[object String]") ?
					fs.readFileSync(components[key].info[0]).toString() : "";
				var data = (objTypeString.call(components[key].data[0]) == "[object String]") ?
					fs.readFileSync(components[key].data[0]).toString() : "";

				for (var index in tmpl) {
					if (tmpl.hasOwnProperty(index)) {
						// replace template & data placeholders
						tmplContent += templateContents
							.replace(placeholder.url, tmpl[index].replace(/\\/g,"/"))
							.replace(placeholder.data, data);
					}
				}

				// replace README & looping content placeholders
				content += folderContents
					.replace(placeholder.info, info)
					.replace(placeholder.tmpl, tmplContent);
			}
		}

		//console.log(content);
		return content;
	}

	/**
	 * Recursive function which walks the JSON tree and
	 * returns an array of objects (components).
	 * Object consist of - template, data and info files.
	 * @param {json} items - file structure in JSON format
	 */
	function JSONtoFile(items){
		// console.info("----------- calling JSONtoFile -----------");
		var results = [],
			folder  = {},
			tmplExt = ['nunjucks', 'nj'],
			dataExt = ['json'],
			infoExt = ['markdown','md'];
		folder.tmpl = [];
		folder.data = [];
		folder.info = [];

		//http://stackoverflow.com/questions/684672/loop-through-javascript-object
		for (var key in items) {
			if (items.hasOwnProperty(key)) {
				if(items[key].type==='folder'){
					// console.info(objTypeString.call(items[key].children));
					if(objTypeString.call(items[key].children) != "[object Undefined]"){ //if has children
						var output = JSONtoFile(items[key].children);
						if(objTypeString.call(output)!="[object Undefined]"){
							// console.table(output);
							results = results.concat(output);
						}
					}
				}
				else{
					if(tmplExt.indexOf(items[key].type) > -1){
						// console.info(items[key].type+" is a template!");
						folder.tmpl.push(items[key].path);
					}
					else if(dataExt.indexOf(items[key].type) > -1){
						// console.info(items[key].type+" is a data!");
						folder.data.push(items[key].path);
					}
					else if(infoExt.indexOf(items[key].type) > -1){
						// console.info(items[key].type+" is a documentation!");
						folder.info.push(items[key].path);
					}
				}
			}
		}

		// merge arrays
		if(folder.tmpl.length > 0 || folder.data.length > 0 || folder.info.length > 0){
			// console.log(folder);
			results.push(folder)
		}

		// console.table(results);
		if (results.length > 0) return results;
	}

	initialization();
}
<commit_msg>Include (with tmpl engine) .md files. The compile time when making changes to README files will be reduced.<commit_after>

// load dependencies
var fs = require('fs'),						//filesystem
	path = require('path'),					//paths
	recursive = require('recursive-readdir'),
	escape = require('escape-html'),
	console = require('better-console'),
	helpers = require('./helpers');

module.exports = function(){

	// *************************************
	//  COMPONENT LIBRARY GENERATOR
	//
	//	...
	//
	// *************************************

	// -------------------------------------
	//  Settings
	// -------------------------------------
	var srcpath = 'src/components';
	var allowedFileExtensions = ['md', 'json', 'Nunjucks'];

	// -------------------------------------
	//  Helper functions
	// -------------------------------------
	helpers.init();
	var objTypeString = Object.prototype.toString; // http://blog.niftysnippets.org/2010/09/say-what.html

	/**
	 * Initialize calls 3 basic functions
	 * a) save treeToJSON
	 * b) generateNavigation
	 * c) generateContent
	 */
	function initialization(){

		// -------------------------------------
		//  save fileTree to JSON
		// -------------------------------------
		console.info("Save Directory Tree in JSON");
		console.time("Timer");
		var fileTree = treeToJSON(srcpath, allowedFileExtensions);
		// console.table(fileTree);
		// console.log(fileTree);
		console.timeEnd("Timer");

		// -------------------------------------
		//  generate navigation
		// -------------------------------------
		// console.info("Generate navigation");
		// console.time("Timer");
		// generateNavigation(fileTree);
		// // console.table(fileTree);
		// console.timeEnd("Timer");

		// -------------------------------------
		//  generate content
		// -------------------------------------
		// console.info("Generate content");
		// console.time("Timer");
		var content = generateContent(fileTree);
		// // console.table(fileTree);
		// console.timeEnd("Timer");

		// -------------------------------------
		//  filling template
		// -------------------------------------
		var placeholder = {
			styleguide: /\[\[\[styleguide-placeholder]]]/g
		},
			pageTemplate 	= 'src/styleguide/source/page.template',
			resultUrl 	 	= 'src/styleguide/index.nunjucks',
			pageSource 		= fs.readFileSync(pageTemplate).toString(),
			newSource	 	= pageSource
								.replace(placeholder.styleguide, content);
								// .replace(placeholder.url, tmpl[index].replace(/\\/g,"/"))
		// console.log(content);
		// console.log(newSource);
		fs.writeFileSync(resultUrl, newSource);
	}

	/**
	 * Recursive function which walks the file-tree and saves it in to the JSON
	 * @param  {string} dir - recursion starting directory
	 * @param  {array} allowedFileExtensions - array of allowedFileExtensions file types (otherwise, ignore is more complicated)
	 * @return {JSON}
	 */
	function treeToJSON(dir, allowedFileExtensions){
		var output = [];				// variable which later will be returned.
		var re = /(?:\.([^.]+))?$/; 	// get extension regexp
		allowedFileExtensions.toLowerCase();   		// convert allowedFileExtensions filetypes to lowercase

		// Produce this JSON structure:
		// [{
		// 	"name": "january",
		// 	"path": "winter/january",
		// 	"type": "directory",
		// 	"children": [
		// 		{
		// 			"path": "winter/january/ski.png",
		// 			"name": "ski.png",
		// 			"type": "png"
		// 		},
		// 		{
		// 			"path": "winter/january/snowboard.jpg",
		// 			"name": "snowboard.jpg",
		// 			"type": "jpg"
		// 		}
		// 	]
		// }]
		fs.readdirSync(dir).filter(function(file) {
			var whitelisted = false;
			var item = {};
			item.name = file;
			item.path = path.join(dir, file);
			item.type = (re.exec(file)[1] == undefined)? 'folder': re.exec(file)[1].toLowerCase();

			var isDirectory = fs.statSync(item.path).isDirectory();
			if(isDirectory){
				// if directory call function on new path
				item.children = treeToJSON(item.path, allowedFileExtensions);
				// console.table(item.children);
				whitelisted = true;
			}
			else{
				// if file, make sure it's whitelisted
				if(objTypeString.call(allowedFileExtensions) == "[object Array]"){
					if(allowedFileExtensions.indexOf(item.type) > -1){
						whitelisted = true;
					}
				}
				else if(objTypeString.call(allowedFileExtensions) == "[object String]"){
					if(allowedFileExtensions==item.type){
						whitelisted = true;
					}
				}
			}
			if(whitelisted) output.push(item);
		});
		return output;
	}

	function generateNavigation(tree){

	}

	function generateContent(tree){
		var folderTemplate 	= 'src/styleguide/source/folder.template';
		var templateTemplate 	= 'src/styleguide/source/template.template';
		var outputUrl 			= 'src/styleguide/index.nunjucks';
		var content 			= 'There are no components in components folder';
		var placeholder = {
			url: /\[\[\[template-url]]]/g,
			data: /\[\[\[template-data]]]/g,
			info: /\[\[\[folder-info]]]/g,
			tmpl: /\[\[\[folder-templates]]]/g
		};

		// -------------------------------------
		//  get array of components
		// -------------------------------------
		var components = JSONtoFile(tree);
		//console.table(components);
		// console.log(components.length);

		// -------------------------------------
		//  read template
		// -------------------------------------
		var folderContents = fs.readFileSync(folderTemplate).toString();
		var templateContents = fs.readFileSync(templateTemplate).toString();

		// -------------------------------------
		//  walk the tree
		// -------------------------------------
		if(components.length>0) content = "";
		for (var key in components) {
			if (components.hasOwnProperty(key)) {
				console.info("Building a folder");
				var tmplContent = '';
				var tmpl = components[key].tmpl;
				// var info = (objTypeString.call(components[key].info[0]) == "[object String]") ?
				// 	fs.readFileSync(components[key].info[0]).toString() : "";
				var info = (objTypeString.call(components[key].info[0]) == "[object String]") ?
					components[key].info[0] : "";
				var data = (objTypeString.call(components[key].data[0]) == "[object String]") ?
					fs.readFileSync(components[key].data[0]).toString() : "''";

				for (var index in tmpl) {
					if (tmpl.hasOwnProperty(index)) {
						// replace template & data placeholders
						tmplContent += templateContents
							.replace(placeholder.url, tmpl[index].replace(/\\/g,"/"))
							.replace(placeholder.data, data);
					}
				}

				// replace README & looping content placeholders
				content += folderContents
					.replace(placeholder.info, info.replace(/\\/g,"/"))
					.replace(placeholder.tmpl, tmplContent);
			}
		}

		//console.log(content);
		return content;
	}

	/**
	 * Recursive function which walks the JSON tree and
	 * returns an array of objects (components).
	 * Object consist of - template, data and info files.
	 * @param {json} items - file structure in JSON format
	 */
	function JSONtoFile(items){
		// console.info("----------- calling JSONtoFile -----------");
		var results = [],
			folder  = {},
			tmplExt = ['nunjucks', 'nj'],
			dataExt = ['json'],
			infoExt = ['markdown','md'];
		folder.tmpl = [];
		folder.data = [];
		folder.info = [];

		//http://stackoverflow.com/questions/684672/loop-through-javascript-object
		for (var key in items) {
			if (items.hasOwnProperty(key)) {
				if(items[key].type==='folder'){
					// console.info(objTypeString.call(items[key].children));
					if(objTypeString.call(items[key].children) != "[object Undefined]"){ //if has children
						var output = JSONtoFile(items[key].children);
						if(objTypeString.call(output)!="[object Undefined]"){
							// console.table(output);
							results = results.concat(output);
						}
					}
				}
				else{
					if(tmplExt.indexOf(items[key].type) > -1){
						// console.info(items[key].type+" is a template!");
						folder.tmpl.push(items[key].path);
					}
					else if(dataExt.indexOf(items[key].type) > -1){
						// console.info(items[key].type+" is a data!");
						folder.data.push(items[key].path);
					}
					else if(infoExt.indexOf(items[key].type) > -1){
						// console.info(items[key].type+" is a documentation!");
						folder.info.push(items[key].path);
					}
				}
			}
		}

		// merge arrays
		if(folder.tmpl.length > 0 || folder.data.length > 0 || folder.info.length > 0){
			// console.log(folder);
			results.push(folder)
		}

		// console.table(results);
		if (results.length > 0) return results;
	}

	initialization();
}
<|endoftext|>