<commit_before>import * as _ from 'lodash';

const path = Plugin.path;
const fs = Plugin.fs;
const sass = Npm.require('node-sass');
const Future = Npm.require('fibers/future');
const files = Plugin.files;

let _includePaths;

Plugin.registerCompiler({
  extensions: ['scss', 'sass'],
  archMatching: 'web'
}, () => new SassCompiler());

var toPosixPath = function (p, partialPath) {
  // Sometimes, you can have a path like \Users\IEUser on windows, and this
  // actually means you want C:\Users\IEUser
  if (p[0] === "\\" && (! partialPath)) {
    p = process.env.SystemDrive + p;
  }

  p = p.replace(/\\/g, '/');
  if (p[1] === ':' && ! partialPath) {
    // transform "C:/bla/bla" to "/c/bla/bla"
    p = '/' + p[0] + p.slice(2);
  }

  return p;
};

var convertToStandardPath = function (osPath, partialPath) {
  if (process.platform === "win32") {
    return toPosixPath(osPath, partialPath);
  }

  return osPath;
}

// CompileResult is {css, sourceMap}.
class SassCompiler extends MultiFileCachingCompiler {
  constructor() {
    super({
      compilerName: 'sass',
      defaultCacheSize: 1024*1024*10
    });
  }

  getCacheKey(inputFile) {
    return inputFile.getSourceHash();
  }

  compileResultSize(compileResult) {
    return compileResult.css.length +
      this.sourceMapSize(compileResult.sourceMap);
  }

  // The heuristic is that a file is an import (ie, is not itself processed as a
  // root) if it matches _*.sass, _*.scss
  // This can be overridden in either direction via an explicit
  // `isImport` file option in api.addFiles.
  isRoot(inputFile) {
    const fileOptions = inputFile.getFileOptions();
    if (fileOptions.hasOwnProperty('isImport')) {
      return !fileOptions.isImport;
    }

    const pathInPackage = inputFile.getPathInPackage();
    return !this.hasUnderscore(pathInPackage);
  }

  hasUnderscore(file){
    return path.basename(file)[0] === '_';
  }

  compileOneFile(inputFile, allFiles) {

    const referencedImportPaths = [];

    const self = this;

    var totalImportPath = [];
    var sourceMapPaths = ['.'+inputFile.getDisplayPath()];

    function addUnderscore(file){
      if(!self.hasUnderscore(file)){
        file = path.join(path.dirname(file),'_'+path.basename(file));
      }
      return file;
    }

    const getRealImportPath = function(importPath){
      const rawImportPath = importPath;
      var isAbsolute = false;

      if(importPath[0] === '/'){
        isAbsolute = true;
      }

      //SASS has a whole range of possible import files from one import statement, try each of them
      const possibleFiles = [];

      //If the referenced file has no extension, try possible extensions, starting with extension of the parent file.
      let possibleExtensions = ['scss','sass','css'];

      if(! importPath.match(/\.s?(a|c)ss$/)){
        possibleExtensions = [inputFile.getExtension()].concat(_.without(possibleExtensions,inputFile.getExtension()));
        for(const extension of possibleExtensions){
          possibleFiles.push(importPath+'.'+extension);
        }
      }else{
        possibleFiles.push(importPath);
      }

      //Try files prefixed with underscore
      for(const possibleFile of possibleFiles){
        if(! self.hasUnderscore(possibleFile)){
          possibleFiles.push(addUnderscore(possibleFile));
        }
      }

      //Try if one of the possible files exists
      for(const possibleFile of possibleFiles){
        if((isAbsolute && fileExists(possibleFile)) || (!isAbsolute && allFiles.has(possibleFile))){
            return {absolute:isAbsolute,path:possibleFile};
        }
      }

      //Nothing found...
      return null;

    };

    //Handle import statements found by the sass compiler, used to handle cross-package imports
    const importer = function(url,prev,done){

      if(!totalImportPath.length){
        totalImportPath.push(prev);
      }

      if(totalImportPath[totalImportPath.length] !== prev){
        //backtracked, splice of part we don't need anymore
        // (XXX: this might give problems when multiple parts of the path have the same name)
        totalImportPath.splice(totalImportPath.indexOf(prev)+1,totalImportPath.length);
      }

      var importPath = url;
      for(var i = totalImportPath.length-1; i >= 0; i--){
        if(importPath[0] === '/' || importPath[0] === '{'){
          break;
        }
        importPath = path.join(path.dirname(totalImportPath[i]),importPath);
      }
      totalImportPath.push(url);

      let accPosition = importPath.indexOf('{');
      if(accPosition > -1){
        importPath = importPath.substr(accPosition,importPath.length);
      }

      try{
        let parsed = getRealImportPath(importPath);

        if (!parsed) {
          parsed = _getRealImportPathFromIncludes(url, getRealImportPath);
        }
        if (!parsed) {
          //Nothing found...
          throw new Error(`File to import: ${url} not found in file: ${totalImportPath[totalImportPath.length-2]}`);
        }

        if (parsed.absolute) {
          sourceMapPaths.push(parsed.path);
          done({ contents: fs.readFileSync(parsed.path, 'utf8')});
        }else{
          referencedImportPaths.push(parsed.path);
          sourceMapPaths.push(decodeFilePath(parsed.path));
          done({ contents: allFiles.get(parsed.path).getContentsAsString()});
        }
      }catch(e){
        return done(e);
      }

    }

    //Start compile sass (async)
    const f = new Future;

    const options = {
      sourceMap:         true,
      sourceMapContents: true,
      sourceMapEmbed:    false,
      sourceComments:    false,
      omitSourceMapUrl:  true,
      sourceMapRoot: '.',
      indentedSyntax : inputFile.getExtension() === 'sass',
      outFile: '.'+inputFile.getBasename(),
      importer: importer,
      includePaths:      []
    };

    options.file  =  this.getAbsoluteImportPath(inputFile);

    options.data = inputFile.getContentsAsBuffer().toString('utf8');

    //If the file is empty, options.data is an empty string
    // In that case options.file will be used by node-sass,
    // which it can not read since it will contain a meteor package or app reference '{}'
    // This is one workaround, another one would be to not set options.file, in which case the importer 'prev' will be 'stdin'
    // However, this would result in problems if a file named stdn.scss would exist.
    // Not the most elegant of solutions, but it works.
    if(!options.data.trim()){
      options.data = "$fakevariable : blue;"
    }

    let output;
    try {
      sass.render(options,f.resolver());
      output = f.wait();
    } catch (e) {
      inputFile.error({
        message: `Scss compiler error: ${e.message}\n`,
        sourcePath: inputFile.getDisplayPath()
      });
      return null;
    }
    //End compile sass

    //Start fix sourcemap references
    if (output.map) {
      const map = JSON.parse(output.map.toString('utf-8'));
      map.sources = sourceMapPaths;
      output.map = map;
    }
    //End fix sourcemap references

    const compileResult = {css: output.css.toString('utf-8'), sourceMap: output.map};
    return {compileResult,referencedImportPaths};
  }

  addCompileResult(inputFile,  compileResult) {
    inputFile.addStylesheet({
      data:  compileResult.css,
      path: inputFile.getPathInPackage() + '.css',
      sourceMap:  compileResult.sourceMap
    });
  }
}

function _getRealImportPathFromIncludes(importPath, getRealImportPathFn){

  _prepareIncludePaths();

  let possibleFilePath, foundFile;

  for (let includePath of _includePaths) {
    possibleFilePath = path.join(includePath, importPath);
    foundFile = getRealImportPathFn(possibleFilePath);

    if (foundFile) {
      return foundFile;
    }
  }

  return null;
}

/**
 * If not loaded yet, load configuration and includePaths.
 * @private
 */
function _prepareIncludePaths() {
  if (typeof _includePaths === 'undefined') {
    const config = _loadConfigurationFile();

    _loadIncludePaths(config);
  }
}

/**
 * Extract the 'includePaths' key from specified configuration, if any, and
 * store it into _includePaths.
 * @param config
 * @private
 */
function _loadIncludePaths(config) {
  // Extract includePaths, if any
  const includePaths = config['includePaths'];

  if (includePaths && _.isArray(includePaths)) {
    _includePaths = includePaths;
  } else {
    _includePaths = [];
  }
}

/**
 * Read the content of 'scss-config.json' file (if any)
 * @returns {{}}
 * @private
 */
function _loadConfigurationFile() {
  return _getConfig('scss-config.json') || {};
}

/**
 * Build a path from current process working directory (i.e. meteor project
 * root) and specified file name, try to get the file and parse its content.
 * @param configFileName
 * @returns {{}}
 * @private
 */
function _getConfig(configFileName) {
  const appdir = process.env.PWD || process.cwd();
  const custom_config_filename = path.join(appdir, configFileName);
  let userConfig = {};

  if (fileExists(custom_config_filename)) {
    userConfig = fs.readFileSync(custom_config_filename, {
      encoding: 'utf8'
    });
    userConfig = JSON.parse(userConfig);
  } else {
    console.warn('Could not find configuration file at ' + custom_config_filename);
  }
  return userConfig;
}

function decodeFilePath (filePath) {
  const match = filePath.match(/{(.*)}\/(.*)$/);
  if (! match)
    throw new Error('Failed to decode Less path: ' + filePath);

  if (match[1] === '') {
    // app
    return match[2];
  }

  return 'packages/' + match[1] + '/' + match[2];
}

//Handle deprecation of fs.existsSYnc
//XXX: remove when meteor is fully on node 4+
function fileExists(file){
  if(fs.statSync){
    try{
      fs.statSync(file);
    }catch(e){
      return false;
    }
    return true;
  }else{
    return fs.existsSync(file);
  }
}
<commit_msg>Fix _.without not found<commit_after>import sass from 'node-sass';
import Future from 'fibers/future';
import _ from 'meteor/underscore';

const files = Plugin.files;
const path = Plugin.path;
const fs = Plugin.fs;

let _includePaths;

Plugin.registerCompiler({
  extensions: ['scss', 'sass'],
  archMatching: 'web'
}, () => new SassCompiler());

var toPosixPath = function (p, partialPath) {
  // Sometimes, you can have a path like \Users\IEUser on windows, and this
  // actually means you want C:\Users\IEUser
  if (p[0] === "\\" && (! partialPath)) {
    p = process.env.SystemDrive + p;
  }

  p = p.replace(/\\/g, '/');
  if (p[1] === ':' && ! partialPath) {
    // transform "C:/bla/bla" to "/c/bla/bla"
    p = '/' + p[0] + p.slice(2);
  }

  return p;
};

var convertToStandardPath = function (osPath, partialPath) {
  if (process.platform === "win32") {
    return toPosixPath(osPath, partialPath);
  }

  return osPath;
}

// CompileResult is {css, sourceMap}.
class SassCompiler extends MultiFileCachingCompiler {
  constructor() {
    super({
      compilerName: 'sass',
      defaultCacheSize: 1024*1024*10
    });
  }

  getCacheKey(inputFile) {
    return inputFile.getSourceHash();
  }

  compileResultSize(compileResult) {
    return compileResult.css.length +
      this.sourceMapSize(compileResult.sourceMap);
  }

  // The heuristic is that a file is an import (ie, is not itself processed as a
  // root) if it matches _*.sass, _*.scss
  // This can be overridden in either direction via an explicit
  // `isImport` file option in api.addFiles.
  isRoot(inputFile) {
    const fileOptions = inputFile.getFileOptions();
    if (fileOptions.hasOwnProperty('isImport')) {
      return !fileOptions.isImport;
    }

    const pathInPackage = inputFile.getPathInPackage();
    return !this.hasUnderscore(pathInPackage);
  }

  hasUnderscore(file){
    return path.basename(file)[0] === '_';
  }

  compileOneFile(inputFile, allFiles) {

    const referencedImportPaths = [];

    const self = this;

    var totalImportPath = [];
    var sourceMapPaths = ['.'+inputFile.getDisplayPath()];

    function addUnderscore(file){
      if(!self.hasUnderscore(file)){
        file = path.join(path.dirname(file),'_'+path.basename(file));
      }
      return file;
    }

    const getRealImportPath = function(importPath){
      const rawImportPath = importPath;
      var isAbsolute = false;

      if(importPath[0] === '/'){
        isAbsolute = true;
      }

      //SASS has a whole range of possible import files from one import statement, try each of them
      const possibleFiles = [];

      //If the referenced file has no extension, try possible extensions, starting with extension of the parent file.
      let possibleExtensions = ['scss','sass','css'];

      if(! importPath.match(/\.s?(a|c)ss$/)){
        possibleExtensions = [inputFile.getExtension()].concat(_.without(possibleExtensions,inputFile.getExtension()));
        for(const extension of possibleExtensions){
          possibleFiles.push(importPath+'.'+extension);
        }
      }else{
        possibleFiles.push(importPath);
      }

      //Try files prefixed with underscore
      for(const possibleFile of possibleFiles){
        if(! self.hasUnderscore(possibleFile)){
          possibleFiles.push(addUnderscore(possibleFile));
        }
      }

      //Try if one of the possible files exists
      for(const possibleFile of possibleFiles){
        if((isAbsolute && fileExists(possibleFile)) || (!isAbsolute && allFiles.has(possibleFile))){
            return {absolute:isAbsolute,path:possibleFile};
        }
      }

      //Nothing found...
      return null;

    };

    //Handle import statements found by the sass compiler, used to handle cross-package imports
    const importer = function(url,prev,done){

      if(!totalImportPath.length){
        totalImportPath.push(prev);
      }

      if(totalImportPath[totalImportPath.length] !== prev){
        //backtracked, splice of part we don't need anymore
        // (XXX: this might give problems when multiple parts of the path have the same name)
        totalImportPath.splice(totalImportPath.indexOf(prev)+1,totalImportPath.length);
      }

      var importPath = url;
      for(var i = totalImportPath.length-1; i >= 0; i--){
        if(importPath[0] === '/' || importPath[0] === '{'){
          break;
        }
        importPath = path.join(path.dirname(totalImportPath[i]),importPath);
      }
      totalImportPath.push(url);

      let accPosition = importPath.indexOf('{');
      if(accPosition > -1){
        importPath = importPath.substr(accPosition,importPath.length);
      }

      try{
        let parsed = getRealImportPath(importPath);

        if (!parsed) {
          parsed = _getRealImportPathFromIncludes(url, getRealImportPath);
        }
        if (!parsed) {
          //Nothing found...
          throw new Error(`File to import: ${url} not found in file: ${totalImportPath[totalImportPath.length-2]}`);
        }

        if (parsed.absolute) {
          sourceMapPaths.push(parsed.path);
          done({ contents: fs.readFileSync(parsed.path, 'utf8')});
        }else{
          referencedImportPaths.push(parsed.path);
          sourceMapPaths.push(decodeFilePath(parsed.path));
          done({ contents: allFiles.get(parsed.path).getContentsAsString()});
        }
      }catch(e){
        return done(e);
      }

    }

    //Start compile sass (async)
    const f = new Future;

    const options = {
      sourceMap:         true,
      sourceMapContents: true,
      sourceMapEmbed:    false,
      sourceComments:    false,
      omitSourceMapUrl:  true,
      sourceMapRoot: '.',
      indentedSyntax : inputFile.getExtension() === 'sass',
      outFile: '.'+inputFile.getBasename(),
      importer: importer,
      includePaths:      []
    };

    options.file  =  this.getAbsoluteImportPath(inputFile);

    options.data = inputFile.getContentsAsBuffer().toString('utf8');

    //If the file is empty, options.data is an empty string
    // In that case options.file will be used by node-sass,
    // which it can not read since it will contain a meteor package or app reference '{}'
    // This is one workaround, another one would be to not set options.file, in which case the importer 'prev' will be 'stdin'
    // However, this would result in problems if a file named stdín.scss would exist.
    // Not the most elegant of solutions, but it works.
    if(!options.data.trim()){
      options.data = "$fakevariable : blue;"
    }

    let output;
    try {
      sass.render(options,f.resolver());
      output = f.wait();
    } catch (e) {
      inputFile.error({
        message: `Scss compiler error: ${e.message}\n`,
        sourcePath: inputFile.getDisplayPath()
      });
      return null;
    }
    //End compile sass

    //Start fix sourcemap references
    if (output.map) {
      const map = JSON.parse(output.map.toString('utf-8'));
      map.sources = sourceMapPaths;
      output.map = map;
    }
    //End fix sourcemap references

    const compileResult = {css: output.css.toString('utf-8'), sourceMap: output.map};
    return {compileResult,referencedImportPaths};
  }

  addCompileResult(inputFile,  compileResult) {
    inputFile.addStylesheet({
      data:  compileResult.css,
      path: inputFile.getPathInPackage() + '.css',
      sourceMap:  compileResult.sourceMap
    });
  }
}

function _getRealImportPathFromIncludes(importPath, getRealImportPathFn){

  _prepareIncludePaths();

  let possibleFilePath, foundFile;

  for (let includePath of _includePaths) {
    possibleFilePath = path.join(includePath, importPath);
    foundFile = getRealImportPathFn(possibleFilePath);

    if (foundFile) {
      return foundFile;
    }
  }

  return null;
}

/**
 * If not loaded yet, load configuration and includePaths.
 * @private
 */
function _prepareIncludePaths() {
  if (typeof _includePaths === 'undefined') {
    const config = _loadConfigurationFile();

    _loadIncludePaths(config);
  }
}

/**
 * Extract the 'includePaths' key from specified configuration, if any, and
 * store it into _includePaths.
 * @param config
 * @private
 */
function _loadIncludePaths(config) {
  // Extract includePaths, if any
  const includePaths = config['includePaths'];

  if (includePaths && _.isArray(includePaths)) {
    _includePaths = includePaths;
  } else {
    _includePaths = [];
  }
}

/**
 * Read the content of 'scss-config.json' file (if any)
 * @returns {{}}
 * @private
 */
function _loadConfigurationFile() {
  return _getConfig('scss-config.json') || {};
}

/**
 * Build a path from current process working directory (i.e. meteor project
 * root) and specified file name, try to get the file and parse its content.
 * @param configFileName
 * @returns {{}}
 * @private
 */
function _getConfig(configFileName) {
  const appdir = process.env.PWD || process.cwd();
  const custom_config_filename = path.join(appdir, configFileName);
  let userConfig = {};

  if (fileExists(custom_config_filename)) {
    userConfig = fs.readFileSync(custom_config_filename, {
      encoding: 'utf8'
    });
    userConfig = JSON.parse(userConfig);
  } else {
    console.warn('Could not find configuration file at ' + custom_config_filename);
  }
  return userConfig;
}

function decodeFilePath (filePath) {
  const match = filePath.match(/{(.*)}\/(.*)$/);
  if (! match)
    throw new Error('Failed to decode Less path: ' + filePath);

  if (match[1] === '') {
    // app
    return match[2];
  }

  return 'packages/' + match[1] + '/' + match[2];
}

//Handle deprecation of fs.existsSYnc
//XXX: remove when meteor is fully on node 4+
function fileExists(file){
  if(fs.statSync){
    try{
      fs.statSync(file);
    }catch(e){
      return false;
    }
    return true;
  }else{
    return fs.existsSync(file);
  }
}
<|endoftext|>


<commit_before>var miio = require('./lib');

const crypto = require('crypto');
// var vacuum = python.import('mirobot/protocol');

class MiHome {

  constructor (name, ip, port, token, model) {
    this.ip = ip;
    this.port = port
    this.token = token;
    this.name = name
    this.model = model
    this.device = null;
  }

  load () {
    var that = this;

    return that.loading = new Promise(function(resolve, reject){

      if (that.model == 'vacuum') {
        that.device = miio.createDevice({
          address: that.ip,
          token: that.token,
          model: 'rockrobo.vacuum.v1'
        });

        that.device.init()
        .then(() => {
          console.log(that.model + " init success");
          resolve(that);
        })
        .catch(err => {
          console.error(that.model + " init error: " + err);
          reject(err);
        });
      }
      else {

        if (that.token == null) {
          var para = { address: that.ip };
        }
        else {
          var para = { address: that.ip, token: that.token };
        };

        miio.device(para)
          .then(device => {
              that.device = device;
              console.log(that.name + " " + that.device.type + " init success");
              resolve(that);
            })
          .catch(err => {
          console.error(that.name + " init error: " + err);
          reject(err);
        });
      }
    });
  }

  exec(cmd) {
    var that = this;

    if (cmd == 'Stop') {
      cmd = 'Charge';
    }

    switch (cmd) {
      case 'Start':

        if (this.device.type == 'vacuum') {
          this.device.start()
            .then(res => console.log(this.name + " start result : " + res))
            .catch(err => console.error(this.name + " start error: " + err));
        }

        break;

      case 'Charge':

        if (this.device.type == 'vacuum') {
          this.device.charge()
            .then(res => console.log(this.name + " charge result : " + res))
            .catch(err => console.error(this.name + " charge error: " + err));
        }

        break;

      case 'Pause':

        if (this.device.type == 'vacuum') {
          this.device.pause()
            .then(res => console.log(this.name + " pause result : " + res))
            .catch(err => console.error(this.name + " pause error: " + err));
        }

        break;

      case 'List':
        if (this.device.type == 'gateway') {
          this.list();
        }

        break;

      case 'PowerOff':
        if (this.device.type == 'light') {
          this.device.setPower(false)
            .then(res => console.log(this.name + " PowerOff result : " + res))
            .catch(err => console.error(this.name + " PowerOff error: " + err));
        }
        else {
          this.powerSet(false);
        }

        break;
      case 'PowerOn':

        if (this.device.type == 'light') {
          this.device.setPower(true)
            .then(res => console.log(this.name + " PowerOn result : " + res))
            .catch(err => console.error(this.name + " PowerOn error: " + err));
        }
        else {
          this.powerSet(true);
        }
        break;

      case 'PowerStatus':
        this.powerStatus();
        break;

      case 'LightStatus':
        if (this.device.type == 'light') {
          console.log(this.name + " Color Temp: " + this.device.colorTemperature);
        }
        break;

      default:
        console.log('Invalid type:' + cmd);
    }

    return;

  }

  powerOn() {
    var that = this;

    if(this.device.hasCapability('power')) {
      this.device.setPower(true)
        .then(console.log(this.name + ":Power On"))
        .catch(console.error(this.name + "Failed to Power On"));
    }

  }

  _deviecPowerSet(inputDevice, cmd) {
    var that = this;

    for (var i=0; i<inputDevice.powerChannels.length; i++) {
      console.log("Chan " + i);
      inputDevice.setPower(i, cmd)
      .then(power => console.log(inputDevice.type + " Power " + power))
      .catch(err => console.error(inputDevice.type + " Failed: " + err));
    }
  }

  _deviecPowerStatus(inputDevice) {
    var that = this;

    for (var i=0; i<inputDevice.powerChannels.length; i++) {
      console.log(inputDevice.type + " power " + inputDevice.power(JSON.stringify(i)));
    }
  }

  powerSet(cmd) {
    var that = this;

    if(that.device.type == 'gateway') {
      that.device.on('deviceAvailable', subDevice => {
        if (subDevice.type == 'power-plug') {

          that._deviecPowerSet(subDevice,cmd);
        }

        return;
      });
    }
    else {
      that._deviecPowerSet(that.device,cmd);
    }
  }

  powerStatus() {
    var that = this;

    if(that.device.type == 'gateway') {
      that.device.on('deviceAvailable', subDevice => {
        if (subDevice.type == 'power-plug') {

          that._deviecPowerStatus(subDevice);
        }

        return;
      });
    }
    else {
      that._deviecPowerStatus(that.device);
    }
  }

  encryption (key, iv, data) {
    var cipher = crypto.createCipheriv('aes-128-cbc', key, iv);
    var crypted = cipher.update(data, 'utf8', 'binary');
    crypted += cipher.final('binary');
    crypted = new Buffer(crypted, 'binary');
    return crypted;
  }

  list() {
    var that = this;
    console.log('All SubDevice:');

    that.device.on('deviceAvailable', subDevice => {
      console.log('ID: ' + subDevice.id + ' Model:' + subDevice.model + ' Type:' + subDevice.type );

      if (subDevice.model == 'lumi.switch') {
          console.log('Actions: ' + subDevice.actions );
          debugger;

          var key = that.device._developerKey;
          var token = that.device._lastToken;

          console.log('key ' + key + ' token ' + token)

          console.log('tokenkey ' + that.device.packet._tokenKey + ' IV ' + that.device.packet._tokenIV)

          var base64str = that.encryption(that.device.packet._tokenKey, that.device.packet._tokenIV, token.toString());

          console.log(' ciphertext: ' + base64str.toString('hex'));

          var finalKey = base64str.toString('hex');

          var data = {"cmd":"write",
                "model":"switch",
                "sid":subDevice.id,
                "short_id":2014,
                "data":"{\"status\":\"click\",\"key\":\"" + finalKey + "\"}" }
          that.device.devApi.send(data);
                 // 0: data = {"cmd":"read_ack","model":"switch","sid":"158d000155e11d","short_id":2014,"data":"{\"voltage\":3062}"}

        }

      return;

    });

    if(that.device.devices) {

      that.device.devices.forEach(function(item){
        console.log('-' + item);
      })
    }

  }

};

module.exports = MiHome;<commit_msg>update test<commit_after>var miio = require('./lib');

const crypto = require('crypto');
// var vacuum = python.import('mirobot/protocol');

class MiHome {

  constructor (name, ip, port, token, model) {
    this.ip = ip;
    this.port = port
    this.token = token;
    this.name = name
    this.model = model
    this.device = null;
  }

  load () {
    var that = this;

    return that.loading = new Promise(function(resolve, reject){

      if (that.model == 'vacuum') {
        that.device = miio.createDevice({
          address: that.ip,
          token: that.token,
          model: 'rockrobo.vacuum.v1'
        });

        that.device.init()
        .then(() => {
          console.log(that.model + " init success");
          resolve(that);
        })
        .catch(err => {
          console.error(that.model + " init error: " + err);
          reject(err);
        });
      }
      else {

        if (that.token == null) {
          var para = { address: that.ip };
        }
        else {
          var para = { address: that.ip, token: that.token };
        };

        miio.device(para)
          .then(device => {
              that.device = device;
              console.log(that.name + " " + that.device.type + " init success");
              resolve(that);
            })
          .catch(err => {
          console.error(that.name + " init error: " + err);
          reject(err);
        });
      }
    });
  }

  exec(cmd) {
    var that = this;

    if (cmd == 'Stop') {
      cmd = 'Charge';
    }

    switch (cmd) {
      case 'Start':

        if (this.device.type == 'vacuum') {
          this.device.start()
            .then(res => console.log(this.name + " start result : " + res))
            .catch(err => console.error(this.name + " start error: " + err));
        }

        break;

      case 'Charge':

        if (this.device.type == 'vacuum') {
          this.device.charge()
            .then(res => console.log(this.name + " charge result : " + res))
            .catch(err => console.error(this.name + " charge error: " + err));
        }

        break;

      case 'Pause':

        if (this.device.type == 'vacuum') {
          this.device.pause()
            .then(res => console.log(this.name + " pause result : " + res))
            .catch(err => console.error(this.name + " pause error: " + err));
        }

        break;

      case 'List':
        if (this.device.type == 'gateway') {
          this.list();
        }

        break;

      case 'PowerOff':
        if (this.device.type == 'light') {
          this.device.setPower(false)
            .then(res => console.log(this.name + " PowerOff result : " + res))
            .catch(err => console.error(this.name + " PowerOff error: " + err));
        }
        else {
          this.powerSet(false);
        }

        break;
      case 'PowerOn':

        if (this.device.type == 'light') {
          this.device.setPower(true)
            .then(res => console.log(this.name + " PowerOn result : " + res))
            .catch(err => console.error(this.name + " PowerOn error: " + err));
        }
        else {
          this.powerSet(true);
        }
        break;

      case 'PowerStatus':
        this.powerStatus();
        break;

      case 'LightStatus':
        if (this.device.type == 'light') {
          console.log(this.name + " Color Temp: " + this.device.colorTemperature);
        }
        break;

      default:
        console.log('Invalid type:' + cmd);
    }

    return;

  }

  powerOn() {
    var that = this;

    if(this.device.hasCapability('power')) {
      this.device.setPower(true)
        .then(console.log(this.name + ":Power On"))
        .catch(console.error(this.name + "Failed to Power On"));
    }

  }

  _deviecPowerSet(inputDevice, cmd) {
    var that = this;

    for (var i=0; i<inputDevice.powerChannels.length; i++) {
      console.log("Chan " + i);
      inputDevice.setPower(i, cmd)
      .then(power => console.log(inputDevice.type + " Power " + power))
      .catch(err => console.error(inputDevice.type + " Failed: " + err));
    }
  }

  _deviecPowerStatus(inputDevice) {
    var that = this;

    for (var i=0; i<inputDevice.powerChannels.length; i++) {
      console.log(inputDevice.type + " power " + inputDevice.power(JSON.stringify(i)));
    }
  }

  powerSet(cmd) {
    var that = this;

    if(that.device.type == 'gateway') {
      that.device.on('deviceAvailable', subDevice => {
        if (subDevice.type == 'power-plug') {

          that._deviecPowerSet(subDevice,cmd);
        }

        return;
      });
    }
    else {
      that._deviecPowerSet(that.device,cmd);
    }
  }

  powerStatus() {
    var that = this;

    if(that.device.type == 'gateway') {
      that.device.on('deviceAvailable', subDevice => {
        if (subDevice.type == 'power-plug') {

          that._deviecPowerStatus(subDevice);
        }

        return;
      });
    }
    else {
      that._deviecPowerStatus(that.device);
    }
  }

  encryption (key, iv, data) {
    var cipher = crypto.createCipheriv('aes-128-cbc', key, iv);
    var crypted = cipher.update(data, 'utf8', 'binary');
    crypted += cipher.final('binary');
    crypted = new Buffer(crypted, 'binary');
    return crypted;
  }

  list() {
    var that = this;
    console.log('All SubDevice:');

    that.device.on('deviceAvailable', subDevice => {
      console.log('ID: ' + subDevice.id + ' Model:' + subDevice.model + ' Type:' + subDevice.type );

      if (subDevice.model == 'lumi.switch') {
          console.log('Actions: ' + subDevice.actions );
          debugger;

          var key = that.device._developerKey;
          var token = subDevice._lastToken;

          console.log('key ' + key + ' token ' + token)

          console.log('tokenkey ' + that.device.packet._tokenKey + ' IV ' + that.device.packet._tokenIV)

          var base64str = that.encryption(that.device.packet._tokenKey, that.device.packet._tokenIV, token.toString());

          console.log(' ciphertext: ' + base64str.toString('hex'));

          var finalKey = base64str.toString('hex');

          var data = {"cmd":"write",
                "model":"switch",
                "sid":subDevice.id,
                "short_id":2014,
                "data":"{\"status\":\"click\",\"key\":\"" + finalKey + "\"}" }
          that.device.devApi.send(data);
                 // 0: data = {"cmd":"read_ack","model":"switch","sid":"158d000155e11d","short_id":2014,"data":"{\"voltage\":3062}"}

        }

      return;

    });

    if(that.device.devices) {

      that.device.devices.forEach(function(item){
        console.log('-' + item);
      })
    }

  }

};

module.exports = MiHome;<|endoftext|>


<commit_before>/**
 * jq.web.carousel - a carousel library for html5 mobile apps
 * @copyright AppMobi 2011 - AppMobi
 *
 */
(function($) {
    var cache = [];
    $.fn.carousel = function(opts) {
        if (opts === undefined && this.length > 0)
        {
            return cache[this[0].id] ? cache[this[0].id] : null;
        }
        var tmp;
        for (var i = 0; i < this.length; i++) {
            tmp = new carousel(this[i], opts);
            if (this[i].id)
                cache[this[i].id] = tmp;
        }
        return this.length === 1 ? tmp : this;
    };

    var carousel = (function() {
        if (!window.WebKitCSSMatrix) {
            return;
        }
        var translateOpen = 'm11' in new WebKitCSSMatrix() ? "3d(" : "(";
        var translateClose = 'm11' in new WebKitCSSMatrix() ? ",0)" : ")";

        var carousel = function(containerEl, opts) {
            if (typeof containerEl === "string" || containerEl instanceof String) {
                this.container = document.getElementById(containerEl);
            } else {
                this.container = containerEl;
            }
            if (!this.container) {
                alert("Error finding container for carousel " + containerEl);
                return;
            }
            if (this instanceof carousel) {
                for (var j in opts) {
                    if (opts.hasOwnProperty(j)) {
                        this[j] = opts[j];
                    }
                }
            } else {

                return new carousel(containerEl, opts);
            }

                var that = this;
                this.pagingDiv = this.pagingDiv ? document.getElementById(this.pagingDiv) : null;

                // initial setup
                this.container.style.overflow = "hidden";
                this.container.style['-webkit-box-orient'] = "vertical";
                this.container.style['display'] = "-webkit-box";
                this.container.style['-webkit-box-orient'] = "vertical";
                if (this.vertical) {
                    this.horizontal = false;
                }

                var el = document.createElement("div");
                this.container.appendChild(el);
                var $el=$(el);
                var $container=$(this.container);
                var data = Array.prototype.slice.call(this.container.childNodes);
                while(data.length>0)
                {
                    var myEl=data.splice(0,1);
                    myEl=$container.find(myEl)
                    if(myEl.get()==el)
                       continue;
                    $el.append(myEl.get());
                }
                if (this.horizontal) {
                    el.style.display = "-webkit-box";
                    el.style['-webkit-box-flex'] = 1;
                }
                else {
                    el.style.display = "block";
                }

                this.el = el;
                this.refreshItems();

                el.addEventListener('touchmove', function(e) {
                    that.touchMove(e);
                }, false);
                el.addEventListener('touchend', function(e) {
                    that.touchEnd(e);
                }, false);
                el.addEventListener('touchstart', function(e) {
                    that.touchStart(e);
                }, false);
                var that = this;
                window.addEventListener("orientationchange", function() {
                    that.onMoveIndex(that.carouselIndex,0);
                }, false);

        };

        carousel.prototype = {
            startX: 0,
            startY: 0,
            dx: 0,
            dy: 0,
            myDivWidth: 0,
            myDivHeight: 0,
            cssMoveStart: 0,
            childrenCount: 0,
            carouselIndex: 0,
            vertical: false,
            horizontal: true,
            el: null,
            movingElement: false,
            container: null,
            pagingDiv: null,
            pagingCssName: "carousel_paging",
            pagingCssNameSelected: "carousel_paging_selected",
            pagingFunction: null,
            lockMove:false,
            // handle the moving function
            touchStart: function(e) {
                this.myDivWidth = numOnly(this.container.clientWidth);
                this.myDivHeight = numOnly(this.container.clientHeight);
                this.lockMove=false;
                if (event.touches[0].target && event.touches[0].target.type !== undefined) {
                    var tagname = event.touches[0].target.tagName.toLowerCase();
                    if (tagname === "select" || tagname === "input" || tagname === "button")  // stuff we need to allow
                    {
                        return;
                    }
                }
                if (e.touches.length === 1) {

                    this.movingElement = true;
                    this.startY = e.touches[0].pageY;
                    this.startX = e.touches[0].pageX;
                    if (this.vertical) {
                        try {
                            this.cssMoveStart = numOnly(new WebKitCSSMatrix(window.getComputedStyle(this.el, null).webkitTransform).f);
                        } catch (ex1) {
                            this.cssMoveStart = 0;
                        }
                    } else {
                        try {
                            this.cssMoveStart = numOnly(new WebKitCSSMatrix(window.getComputedStyle(this.el, null).webkitTransform).e);
                        } catch (ex1) {
                            this.cssMoveStart = 0;
                        }
                    }
                }
            },
            touchMove: function(e) {
                if(!this.movingElement)
                   return;
                if (e.touches.length > 1) {
                    return this.touchEnd(e);
                }

                var rawDelta = {
                    x: e.touches[0].pageX - this.startX,
                    y: e.touches[0].pageY - this.startY
                };

                if (this.vertical) {
                    var movePos = { x: 0, y: 0 };
                    this.dy = e.touches[0].pageY - this.startY;
                    this.dy += this.cssMoveStart;
                    movePos.y = this.dy;
                    e.preventDefault();
                    e.stopPropagation();
                } else {
                    if (!this.lockMove&&isHorizontalSwipe(rawDelta.x, rawDelta.y)) {

                        var movePos = {x: 0,y: 0};
                        this.dx = e.touches[0].pageX - this.startX;
                        this.dx += this.cssMoveStart;
                        e.preventDefault();
                        e.stopPropagation();
                        movePos.x = this.dx;
                    }
                    else
                       return this.lockMove=true;
                }

                var totalMoved = this.vertical ? ((this.dy % this.myDivHeight) / this.myDivHeight * 100) * -1 : ((this.dx % this.myDivWidth) / this.myDivWidth * 100) * -1; // get a percentage of movement.
                if(movePos)
                    this.moveCSS3(this.el, movePos);
            },
            touchEnd: function(e) {
                if (!this.movingElement) {
                    return;
                }
                // e.preventDefault();
                // e.stopPropagation();
                var runFinal = false;
                try {
                    var endPos = this.vertical ? numOnly(new WebKitCSSMatrix(window.getComputedStyle(this.el, null).webkitTransform).f) : numOnly(new WebKitCSSMatrix(window.getComputedStyle(this.el, null).webkitTransform).e);
                    if (endPos > 0) {
                        this.moveCSS3(this.el, {
                            x: 0,
                            y: 0
                        }, "300");
                    } else {
                        var totalMoved = this.vertical ? ((this.dy % this.myDivHeight) / this.myDivHeight * 100) * -1 : ((this.dx % this.myDivWidth) / this.myDivWidth * 100) * -1; // get a percentage of movement.
                        // Only need
                        // to drag 3% to trigger an event
                        var currInd = this.carouselIndex;
                        if (endPos < this.cssMoveStart && totalMoved > 3) {
                            currInd++; // move right/down
                        } else if ((endPos > this.cssMoveStart && totalMoved < 97)) {
                            currInd--; // move left/up
                        }
                        if (currInd > (this.childrenCount - 1)) {
                            currInd = this.childrenCount - 1;
                        }
                        if (currInd < 0) {
                            currInd = 0;
                        }
                        var movePos = {
                            x: 0,
                            y: 0
                        };
                        if (this.vertical) {
                            movePos.y = (currInd * this.myDivHeight * -1);
                        }
                        else {
                            movePos.x = (currInd * this.myDivWidth * -1);
                        }

                        this.moveCSS3(this.el, movePos, "150");

                        if (this.pagingDiv && this.carouselIndex !== currInd) {
                            document.getElementById(this.container.id + "_" + this.carouselIndex).className = this.pagingCssName;
                            document.getElementById(this.container.id + "_" + currInd).className = this.pagingCssNameSelected;
                        }
                        if (this.carouselIndex != currInd)
                            runFinal = true;
                        this.carouselIndex = currInd;
                    }
                } catch (e) {
                    console.log(e);
                }
                this.dx = 0;
                this.movingElement = false;
                this.startX = 0;
                this.dy = 0;
                this.startY = 0;
                if (runFinal && this.pagingFunction && typeof this.pagingFunction == "function")
                    this.pagingFunction(this.carouselIndex);
            },
            onMoveIndex: function(newInd,transitionTime) {

                this.myDivWidth = numOnly(this.container.clientWidth);
                this.myDivHeight = numOnly(this.container.clientHeight);
                var runFinal = false;

                    if(document.getElementById(this.container.id + "_" + this.carouselIndex))
                        document.getElementById(this.container.id + "_" + this.carouselIndex).className = this.pagingCssName;
                    var newTime = Math.abs(newInd - this.carouselIndex);

                    var ind = newInd;
                    if (ind < 0)
                        ind = 0;
                    if (ind > this.childrenCount - 1) {
                        ind = this.childrenCount - 1;
                    }
                    var movePos = {
                        x: 0,
                        y: 0
                    };
                    if (this.vertical) {
                        movePos.y = (ind * this.myDivHeight * -1);
                    }
                    else {
                        movePos.x = (ind * this.myDivWidth * -1);
                    }

                    var time =transitionTime?transitionTime: 50 + parseInt((newTime * 20));
                    this.moveCSS3(this.el, movePos, time);
                    if (this.carouselIndex != ind)
                        runFinal = true;
                    this.carouselIndex = ind;
                    if (this.pagingDiv) {

                        document.getElementById(this.container.id + "_" + this.carouselIndex).className = this.pagingCssNameSelected;
                    }

                if (runFinal && this.pagingFunction && typeof this.pagingFunction == "function")
                    this.pagingFunction(currInd);
            },

            moveCSS3: function(el, distanceToMove, time, timingFunction) {
                if (!time)
                    time = 0;
                else
                    time = parseInt(time);
                if (!timingFunction)
                    timingFunction = "linear";

                el.style.webkitTransform = "translate" + translateOpen + distanceToMove.x + "px," + distanceToMove.y + "px" + translateClose;
                el.style.webkitTransitionDuration = time + "ms";
                el.style.webkitBackfaceVisiblity = "hidden";
                el.style.webkitTransformStyle = "preserve-3d";
                el.style.webkitTransitionTimingFunction = timingFunction;
            },

            addItem: function(el) {
                if (el && el.nodeType) {

                    this.container.childNodes[0].appendChild(el);
                    this.refreshItems();
                }
            },
            refreshItems: function() {
                var childrenCounter = 0;
                var that = this;
                var el = this.el;
                n = el.childNodes[0];
                var widthParam;
                var heightParam = "100%";
                var elems = [];
                for (; n; n = n.nextSibling) {
                    if (n.nodeType === 1) {
                        elems.push(n);
                        childrenCounter++;
                    }
                }
                var param = (100 / childrenCounter) + "%";
                this.childrenCount = childrenCounter;
                widthParam = parseFloat(100 / this.childrenCount) + "%";
                for (var i = 0; i < elems.length; i++) {
                    if (this.horizontal) {
                        elems[i].style.width = widthParam;
                        elems[i].style.height = "100%";
                    }
                    else {
                        elems[i].style.height = widthParam;
                        elems[i].style.width = "100%";
                        elems[i].style.display = "block";
                    }
                }
                this.moveCSS3(el, {
                    x: 0,
                    y: 0
                });
                if (this.horizontal) {
                    el.style.width = Math.ceil((this.childrenCount) * 100) + "%";
                    el.style.height = "100%";
                    el.style['min-height'] = "100%"
                }
                else {
                    el.style.width = "100%";
                    el.style.height = Math.ceil((this.childrenCount) * 100) + "%";
                    el.style['min-height'] = Math.ceil((this.childrenCount) * 100) + "%";
                }
                // Create the paging dots
                if (this.pagingDiv) {
                    this.pagingDiv.innerHTML = ""
                    for (i = 0; i < this.childrenCount; i++) {

                        var pagingEl = document.createElement("div");
                        pagingEl.id = this.container.id + "_" + i;
                        pagingEl.pageId = i;
                        if (i !== 0) {
                            pagingEl.className = this.pagingCssName;
                        }
                        else {
                            pagingEl.className = this.pagingCssNameSelected;
                        }
                        pagingEl.onclick = function() {
                            that.onMoveIndex(this.pageId);
                        };
                        var spacerEl = document.createElement("div");

                        spacerEl.style.width = "20px";
                        if(this.horizontal){
                            spacerEl.style.cssFloat = "left";
                            spacerEl.innerHTML = " ";
                        }
                        else{
                           spacerEl.innerHTML=" ";
                           spacerEl.style.display="block";
                        }

                        this.pagingDiv.appendChild(pagingEl);
                        if (i + 1 < (this.childrenCount))
                            this.pagingDiv.appendChild(spacerEl);
                        pagingEl = null;
                        spacerEl = null;
                    }
                    this.pagingDiv.style.width = (this.childrenCount) * 50 + "px";
                    this.pagingDiv.style.height = "25px";
                }
                this.onMoveIndex(this.carouselIndex);

            }

        };
        return carousel;
    })();

    function isHorizontalSwipe(xAxis, yAxis) {
                var X = xAxis;
                var Y = yAxis;
                var Z = Math.round(Math.sqrt(Math.pow(X,2)+Math.pow(Y,2))); //the distance - rounded - in pixels
                var r = Math.atan2(Y,X); //angle in radians
                var swipeAngle = Math.round(r*180/Math.PI); //angle in degrees
                if ( swipeAngle < 0 ) { swipeAngle =  360 - Math.abs(swipeAngle); } // for negative degree values
                if (((swipeAngle <= 215) && (swipeAngle >= 155)) || ((swipeAngle <= 45) && (swipeAngle >= 0)) || ((swipeAngle <= 360) && (swipeAngle >= 315))) // horizontal angles with threshold
                {return true; }
                else {return false}
    }

})(jq);<commit_msg>carousel caching system<commit_after>/**
 * jq.web.carousel - a carousel library for html5 mobile apps
 * @copyright AppMobi 2011 - AppMobi
 *
 */
(function($) {
    var cache = [];
	var objId=function(obj){
		if(!obj.jqmCarouselId) obj.jqmCarouselId=$.uuid();
		return obj.jqmCarouselId;
	}
    $.fn.carousel = function(opts) {
        var tmp, id;
        for (var i = 0; i < this.length; i++) {
			//cache system
			id = objId(this[i]);
			if(!cache[id]){
				tmp = new carousel(this[i], opts);
				cache[id] = tmp;
			} else {
				tmp = cache[id];
			}
        }
        return this.length == 1 ? tmp : this;
    };

    var carousel = (function() {
        if (!window.WebKitCSSMatrix) {
            return;
        }
        var translateOpen = 'm11' in new WebKitCSSMatrix() ? "3d(" : "(";
        var translateClose = 'm11' in new WebKitCSSMatrix() ? ",0)" : ")";

        var carousel = function(containerEl, opts) {
            if (typeof containerEl === "string" || containerEl instanceof String) {
                this.container = document.getElementById(containerEl);
            } else {
                this.container = containerEl;
            }
            if (!this.container) {
                alert("Error finding container for carousel " + containerEl);
                return;
            }
            if (this instanceof carousel) {
                for (var j in opts) {
                    if (opts.hasOwnProperty(j)) {
                        this[j] = opts[j];
                    }
                }
            } else {

                return new carousel(containerEl, opts);
            }

                var that = this;
                this.pagingDiv = this.pagingDiv ? document.getElementById(this.pagingDiv) : null;

                // initial setup
                this.container.style.overflow = "hidden";
                this.container.style['-webkit-box-orient'] = "vertical";
                this.container.style['display'] = "-webkit-box";
                this.container.style['-webkit-box-orient'] = "vertical";
                if (this.vertical) {
                    this.horizontal = false;
                }

                var el = document.createElement("div");
                this.container.appendChild(el);
                var $el=$(el);
                var $container=$(this.container);
                var data = Array.prototype.slice.call(this.container.childNodes);
                while(data.length>0)
                {
                    var myEl=data.splice(0,1);
                    myEl=$container.find(myEl)
                    if(myEl.get()==el)
                       continue;
                    $el.append(myEl.get());
                }
                if (this.horizontal) {
                    el.style.display = "-webkit-box";
                    el.style['-webkit-box-flex'] = 1;
                }
                else {
                    el.style.display = "block";
                }

                this.el = el;
                this.refreshItems();

                el.addEventListener('touchmove', function(e) {
                    that.touchMove(e);
                }, false);
                el.addEventListener('touchend', function(e) {
                    that.touchEnd(e);
                }, false);
                el.addEventListener('touchstart', function(e) {
                    that.touchStart(e);
                }, false);
                var that = this;
                window.addEventListener("orientationchange", function() {
                    that.onMoveIndex(that.carouselIndex,0);
                }, false);

        };

        carousel.prototype = {
            startX: 0,
            startY: 0,
            dx: 0,
            dy: 0,
            myDivWidth: 0,
            myDivHeight: 0,
            cssMoveStart: 0,
            childrenCount: 0,
            carouselIndex: 0,
            vertical: false,
            horizontal: true,
            el: null,
            movingElement: false,
            container: null,
            pagingDiv: null,
            pagingCssName: "carousel_paging",
            pagingCssNameSelected: "carousel_paging_selected",
            pagingFunction: null,
            lockMove:false,
            // handle the moving function
            touchStart: function(e) {
                this.myDivWidth = numOnly(this.container.clientWidth);
                this.myDivHeight = numOnly(this.container.clientHeight);
                this.lockMove=false;
                if (event.touches[0].target && event.touches[0].target.type !== undefined) {
                    var tagname = event.touches[0].target.tagName.toLowerCase();
                    if (tagname === "select" || tagname === "input" || tagname === "button")  // stuff we need to allow
                    {
                        return;
                    }
                }
                if (e.touches.length === 1) {

                    this.movingElement = true;
                    this.startY = e.touches[0].pageY;
                    this.startX = e.touches[0].pageX;
                    if (this.vertical) {
                        try {
                            this.cssMoveStart = numOnly(new WebKitCSSMatrix(window.getComputedStyle(this.el, null).webkitTransform).f);
                        } catch (ex1) {
                            this.cssMoveStart = 0;
                        }
                    } else {
                        try {
                            this.cssMoveStart = numOnly(new WebKitCSSMatrix(window.getComputedStyle(this.el, null).webkitTransform).e);
                        } catch (ex1) {
                            this.cssMoveStart = 0;
                        }
                    }
                }
            },
            touchMove: function(e) {
                if(!this.movingElement)
                   return;
                if (e.touches.length > 1) {
                    return this.touchEnd(e);
                }

                var rawDelta = {
                    x: e.touches[0].pageX - this.startX,
                    y: e.touches[0].pageY - this.startY
                };

                if (this.vertical) {
                    var movePos = { x: 0, y: 0 };
                    this.dy = e.touches[0].pageY - this.startY;
                    this.dy += this.cssMoveStart;
                    movePos.y = this.dy;
                    e.preventDefault();
                    e.stopPropagation();
                } else {
                    if (!this.lockMove&&isHorizontalSwipe(rawDelta.x, rawDelta.y)) {

                        var movePos = {x: 0,y: 0};
                        this.dx = e.touches[0].pageX - this.startX;
                        this.dx += this.cssMoveStart;
                        e.preventDefault();
                        e.stopPropagation();
                        movePos.x = this.dx;
                    }
                    else
                       return this.lockMove=true;
                }

                var totalMoved = this.vertical ? ((this.dy % this.myDivHeight) / this.myDivHeight * 100) * -1 : ((this.dx % this.myDivWidth) / this.myDivWidth * 100) * -1; // get a percentage of movement.
                if(movePos)
                    this.moveCSS3(this.el, movePos);
            },
            touchEnd: function(e) {
                if (!this.movingElement) {
                    return;
                }
                // e.preventDefault();
                // e.stopPropagation();
                var runFinal = false;
                try {
                    var endPos = this.vertical ? numOnly(new WebKitCSSMatrix(window.getComputedStyle(this.el, null).webkitTransform).f) : numOnly(new WebKitCSSMatrix(window.getComputedStyle(this.el, null).webkitTransform).e);
                    if (endPos > 0) {
                        this.moveCSS3(this.el, {
                            x: 0,
                            y: 0
                        }, "300");
                    } else {
                        var totalMoved = this.vertical ? ((this.dy % this.myDivHeight) / this.myDivHeight * 100) * -1 : ((this.dx % this.myDivWidth) / this.myDivWidth * 100) * -1; // get a percentage of movement.
                        // Only need
                        // to drag 3% to trigger an event
                        var currInd = this.carouselIndex;
                        if (endPos < this.cssMoveStart && totalMoved > 3) {
                            currInd++; // move right/down
                        } else if ((endPos > this.cssMoveStart && totalMoved < 97)) {
                            currInd--; // move left/up
                        }
                        if (currInd > (this.childrenCount - 1)) {
                            currInd = this.childrenCount - 1;
                        }
                        if (currInd < 0) {
                            currInd = 0;
                        }
                        var movePos = {
                            x: 0,
                            y: 0
                        };
                        if (this.vertical) {
                            movePos.y = (currInd * this.myDivHeight * -1);
                        }
                        else {
                            movePos.x = (currInd * this.myDivWidth * -1);
                        }

                        this.moveCSS3(this.el, movePos, "150");

                        if (this.pagingDiv && this.carouselIndex !== currInd) {
                            document.getElementById(this.container.id + "_" + this.carouselIndex).className = this.pagingCssName;
                            document.getElementById(this.container.id + "_" + currInd).className = this.pagingCssNameSelected;
                        }
                        if (this.carouselIndex != currInd)
                            runFinal = true;
                        this.carouselIndex = currInd;
                    }
                } catch (e) {
                    console.log(e);
                }
                this.dx = 0;
                this.movingElement = false;
                this.startX = 0;
                this.dy = 0;
                this.startY = 0;
                if (runFinal && this.pagingFunction && typeof this.pagingFunction == "function")
                    this.pagingFunction(this.carouselIndex);
            },
            onMoveIndex: function(newInd,transitionTime) {

                this.myDivWidth = numOnly(this.container.clientWidth);
                this.myDivHeight = numOnly(this.container.clientHeight);
                var runFinal = false;

                    if(document.getElementById(this.container.id + "_" + this.carouselIndex))
                        document.getElementById(this.container.id + "_" + this.carouselIndex).className = this.pagingCssName;
                    var newTime = Math.abs(newInd - this.carouselIndex);

                    var ind = newInd;
                    if (ind < 0)
                        ind = 0;
                    if (ind > this.childrenCount - 1) {
                        ind = this.childrenCount - 1;
                    }
                    var movePos = {
                        x: 0,
                        y: 0
                    };
                    if (this.vertical) {
                        movePos.y = (ind * this.myDivHeight * -1);
                    }
                    else {
                        movePos.x = (ind * this.myDivWidth * -1);
                    }

                    var time =transitionTime?transitionTime: 50 + parseInt((newTime * 20));
                    this.moveCSS3(this.el, movePos, time);
                    if (this.carouselIndex != ind)
                        runFinal = true;
                    this.carouselIndex = ind;
                    if (this.pagingDiv) {

                        document.getElementById(this.container.id + "_" + this.carouselIndex).className = this.pagingCssNameSelected;
                    }

                if (runFinal && this.pagingFunction && typeof this.pagingFunction == "function")
                    this.pagingFunction(currInd);
            },

            moveCSS3: function(el, distanceToMove, time, timingFunction) {
                if (!time)
                    time = 0;
                else
                    time = parseInt(time);
                if (!timingFunction)
                    timingFunction = "linear";

                el.style.webkitTransform = "translate" + translateOpen + distanceToMove.x + "px," + distanceToMove.y + "px" + translateClose;
                el.style.webkitTransitionDuration = time + "ms";
                el.style.webkitBackfaceVisiblity = "hidden";
                el.style.webkitTransformStyle = "preserve-3d";
                el.style.webkitTransitionTimingFunction = timingFunction;
            },

            addItem: function(el) {
                if (el && el.nodeType) {

                    this.container.childNodes[0].appendChild(el);
                    this.refreshItems();
                }
            },
            refreshItems: function() {
                var childrenCounter = 0;
                var that = this;
                var el = this.el;
                n = el.childNodes[0];
                var widthParam;
                var heightParam = "100%";
                var elems = [];
                for (; n; n = n.nextSibling) {
                    if (n.nodeType === 1) {
                        elems.push(n);
                        childrenCounter++;
                    }
                }
                var param = (100 / childrenCounter) + "%";
                this.childrenCount = childrenCounter;
                widthParam = parseFloat(100 / this.childrenCount) + "%";
                for (var i = 0; i < elems.length; i++) {
                    if (this.horizontal) {
                        elems[i].style.width = widthParam;
                        elems[i].style.height = "100%";
                    }
                    else {
                        elems[i].style.height = widthParam;
                        elems[i].style.width = "100%";
                        elems[i].style.display = "block";
                    }
                }
                this.moveCSS3(el, {
                    x: 0,
                    y: 0
                });
                if (this.horizontal) {
                    el.style.width = Math.ceil((this.childrenCount) * 100) + "%";
                    el.style.height = "100%";
                    el.style['min-height'] = "100%"
                }
                else {
                    el.style.width = "100%";
                    el.style.height = Math.ceil((this.childrenCount) * 100) + "%";
                    el.style['min-height'] = Math.ceil((this.childrenCount) * 100) + "%";
                }
                // Create the paging dots
                if (this.pagingDiv) {
                    this.pagingDiv.innerHTML = ""
                    for (i = 0; i < this.childrenCount; i++) {

                        var pagingEl = document.createElement("div");
                        pagingEl.id = this.container.id + "_" + i;
                        pagingEl.pageId = i;
                        if (i !== 0) {
                            pagingEl.className = this.pagingCssName;
                        }
                        else {
                            pagingEl.className = this.pagingCssNameSelected;
                        }
                        pagingEl.onclick = function() {
                            that.onMoveIndex(this.pageId);
                        };
                        var spacerEl = document.createElement("div");

                        spacerEl.style.width = "20px";
                        if(this.horizontal){
                            spacerEl.style.cssFloat = "left";
                            spacerEl.innerHTML = " ";
                        }
                        else{
                           spacerEl.innerHTML=" ";
                           spacerEl.style.display="block";
                        }

                        this.pagingDiv.appendChild(pagingEl);
                        if (i + 1 < (this.childrenCount))
                            this.pagingDiv.appendChild(spacerEl);
                        pagingEl = null;
                        spacerEl = null;
                    }
                    this.pagingDiv.style.width = (this.childrenCount) * 50 + "px";
                    this.pagingDiv.style.height = "25px";
                }
                this.onMoveIndex(this.carouselIndex);

            }

        };
        return carousel;
    })();

    function isHorizontalSwipe(xAxis, yAxis) {
                var X = xAxis;
                var Y = yAxis;
                var Z = Math.round(Math.sqrt(Math.pow(X,2)+Math.pow(Y,2))); //the distance - rounded - in pixels
                var r = Math.atan2(Y,X); //angle in radians
                var swipeAngle = Math.round(r*180/Math.PI); //angle in degrees
                if ( swipeAngle < 0 ) { swipeAngle =  360 - Math.abs(swipeAngle); } // for negative degree values
                if (((swipeAngle <= 215) && (swipeAngle >= 155)) || ((swipeAngle <= 45) && (swipeAngle >= 0)) || ((swipeAngle <= 360) && (swipeAngle >= 315))) // horizontal angles with threshold
                {return true; }
                else {return false}
    }

})(jq);<|endoftext|>


<commit_before>// Business process dedicated list manager
// (used to handle business processes table in official roles)

'use strict';

var toArray        = require('es5-ext/object/to-array')
  , ensureCallable = require('es5-ext/object/valid-callable')
  , d              = require('d')
  , memoize        = require('memoizee/plain')
  , db             = require('mano').db
  , getData        = require('mano/lib/client/xhr-driver').get
  , ListManager    = require('../objects-table/manager')

  , defineProperties = Object.defineProperties, User = db.User;

require('memoizee/ext/max-age');

var getViewData = memoize(function (query) {
	return getData('/get-users-view/', query).aside(function (result) {
		if (!result.data) return;
		result.data.forEach(function (eventStr) { db.unserializeEvent(eventStr, 'server-temporary'); });
	});
}, {
	normalizer: function (args) { return String(toArray(args[0], null, null, true)); },
	maxAge: 10 * 1000
});

var UsersManager = module.exports = function (conf) {
	var getOrderIndex = ensureCallable(conf.getOrderIndex);

	defineProperties(this, {
		_view: d(db.views.usersAdmin),
		_getItemOrderIndex: d(getOrderIndex),
		_queryExternal: d(getViewData)
	});
};

UsersManager.prototype = Object.create(ListManager.prototype, {
	constructor: d(UsersManager),
	_type: d(User),

	// Characterics that needs to be provided per system/user:
	_view: d(null),
	_getItemOrderIndex: d(null),

	_isExternalQuery: d(function (query) {
		// If it's not about first page, it's only server that knows
		return (query.page > 1);
	}),
	_modifiers: d([{
		process: function (ignore, query) { return this._resolveList(this._view, query); }
	}])
});
<commit_msg>Fix modifier configuration<commit_after>// Business process dedicated list manager
// (used to handle business processes table in official roles)

'use strict';

var toArray        = require('es5-ext/object/to-array')
  , ensureCallable = require('es5-ext/object/valid-callable')
  , d              = require('d')
  , memoize        = require('memoizee/plain')
  , db             = require('mano').db
  , getData        = require('mano/lib/client/xhr-driver').get
  , ListManager    = require('../objects-table/manager')

  , defineProperties = Object.defineProperties, User = db.User;

require('memoizee/ext/max-age');

var getViewData = memoize(function (query) {
	return getData('/get-users-view/', query).aside(function (result) {
		if (!result.data) return;
		result.data.forEach(function (eventStr) { db.unserializeEvent(eventStr, 'server-temporary'); });
	});
}, {
	normalizer: function (args) { return String(toArray(args[0], null, null, true)); },
	maxAge: 10 * 1000
});

var UsersManager = module.exports = function (conf) {
	var getOrderIndex = ensureCallable(conf.getOrderIndex);

	defineProperties(this, {
		_view: d(db.views.usersAdmin),
		_getItemOrderIndex: d(getOrderIndex),
		_queryExternal: d(getViewData)
	});
};

UsersManager.prototype = Object.create(ListManager.prototype, {
	constructor: d(UsersManager),
	_type: d(User),

	// Characterics that needs to be provided per system/user:
	_view: d(null),
	_getItemOrderIndex: d(null),

	_isExternalQuery: d(function (query) {
		// If it's not about first page, it's only server that knows
		return (query.page > 1);
	}),
	_modifiers: d([{
		process: function (ignore, query) {
			var list = this._resolveList(this._view, query);
			return { list: list, size: this._view.totalSize };
		}
	}])
});
<|endoftext|>


<commit_before>function xhrSuccess () { this.callback.apply(this, this.arguments); }

function xhrError () { console.error(this.statusText); }

function loadFile (sURL, fCallback /*, argumentToPass1, argumentToPass2, etc. */) {
  var oReq = new XMLHttpRequest();
  oReq.callback = fCallback;
  oReq.arguments = Array.prototype.slice.call(arguments, 2);
  oReq.onload = xhrSuccess;
  oReq.onerror = xhrError;
  oReq.open("get", sURL, true);
  oReq.send(null);
}

function showMessage (sMsg) {
  alert(sMsg + this.responseText);
}

loadFile("b.txt", showMessage, "New message!\n\n");

//chrome.exe --disable-web-security

var value =  sessionStorage.getItem('fileName');
value += "\n\n Some example code \n if ( true ) { then awesome; }";
var editor = CodeMirror(document.body.getElementsByTagName("article")[0], {
    value: value,
    lineNumbers: true,
    mode: "javascript",
    keyMap: "sublime",
    autoCloseBrackets: true,
    matchBrackets: true,
    showCursorWhenSelecting: true,
    theme: "monokai",
    tabSize: 2
});

function readAllCode(){
	// the code after editing...
	console.log(editor.getValue());
}<commit_msg>remove code format file<commit_after><|endoftext|>


<commit_before>
/**
 *  Proxify
 *  Event "proxying" (or: element "puppetizer")
 *  Manipulation of targets on any event type
 *
 *  @example
 *      HTML : ...
 *      JS : $('[data-proxy]').proxify();
 *
 *  @example with custom callback & custom data-attr for storing JSON
 *      HTML : ...
 *      JS :
 *      $('[data-proxy]').proxify({callback:my_custom_fn, attr:"data-my-custom-attr"});
 *      function my_custom_fn(properties)
 *      {
 *          //  Access any properties like :
 *          properties.source : $(this) - in this ex. it corresponds to
 *          properties.targets : in this ex. it's $(".bg-overlay")
 *          properties.options : in this ex. it's {callback:my_custom_fn}
 *      }
 *
 *  @requires jquery 1.7+
 */
jQuery.fn.proxify = function(options)
{
	$ = jQuery;
	$(this).each(function()
	{
        var $this = $(this),
            json_str = options.attr ? $this.attr(options.attr) : $this.attr('data-proxy');

        //      Support not using JSON in a data-attribute
        //      ex: $('.my-selector').proxify({properties:{evt_types:"mouseenter mouseleave focus blur", target:".bg-overlay"}});
        if (options.properties) {
            properties = options.properties;
        }
        else
        {
            try {
                var properties = JSON && JSON.parse(json_str) || $.parseJSON(json_str);
            }
            catch (err)
            {
                //      debug invalid JSON
                //console.log(err);
                return false;
            }
        }

        //      We need :
        //      • event type(s) : one or more space-separated event types and optional namespaces, such as "click" or "keydown.myPlugin".
        //      • target(s) : CSS selectors
        //      • [optional] filter : CSS selectors to filter the descendants of the selected elements that trigger the event.
        //      • [optional] callback : a callable function(properties)
        //      • [optional] target toggle class (default : "is-active") : a class that will be added/removed on the target
        //      Limitation : same targets for all event types
        //      @evol introduce variants - ex: using one() To attach an event that runs only once and then removes itself
        if (properties.evt_types && properties.target)
        {
            var $target = $(properties.target);
            if ($target.length)
            {
                //      Init defaults
                if (!properties.toggle_class) {
                    properties.toggle_class = "is-active";
                }

                //      Keep stuff for later use
                properties.targets = $target;
                properties.source = $this;
                properties.options = options;

                /**
                 *  Attach event listener
                 *  @see http://api.jquery.com/on/
                 */
                $this.on(
                    properties.evt_types,
                    properties.filter,
                    properties,
                    function(e)
                    {
                        e.data.targets.toggleClass(e.data.toggle_class);
                        if (e.data.options.callback && typeof e.data.options.callback === 'function') {
                            e.data.options.callback(e.data);
                        }
                    }
                );
            }
        }
    });
};
<commit_msg>[minor] Woops : fix committed wrong version of Front-end\Javascript\jQuery\proxify.js<commit_after>
/**
 *  Proxify
 *  Event "proxying" (or: element "puppetizer")
 *  Manipulation of targets on any event type
 *
 *  @example
 *      HTML : ...
 *      JS : $('[data-proxy]').proxify();
 *
 *  @example with custom callback & custom data-attr for storing JSON
 *      HTML : ...
 *      JS :
 *      $('[data-proxy]').proxify({callback:my_custom_fn, attr:"data-my-custom-attr"});
 *      function my_custom_fn(properties)
 *      {
 *          //  Access any properties like :
 *          properties.source : $(this) - in this ex. it corresponds to
 *          properties.targets : in this ex. it's $(".bg-overlay")
 *          properties.options : in this ex. it's {callback:my_custom_fn}
 *      }
 *
 *  @requires jquery 1.7+
 */
jQuery.fn.proxify = function(options)
{
	$ = jQuery;
	$(this).each(function()
	{
        var $this = $(this),
            json_str = (options && options.attr) ? $this.attr(options.attr) : $this.attr('data-proxy');

        //      Support not using JSON in a data-attribute
        //      ex: $('.my-selector').proxify({properties:{evt_types:"mouseenter mouseleave focus blur", target:".bg-overlay"}});
        if (options && options.properties) {
            properties = options.properties;
        }
        else
        {
            try {
                var properties = JSON && JSON.parse(json_str) || $.parseJSON(json_str);
            }
            catch (err)
            {
                //      debug invalid JSON
                //console.log(err);
                return false;
            }
        }

        //      We need :
        //      • event type(s) : one or more space-separated event types and optional namespaces, such as "click" or "keydown.myPlugin".
        //      • target(s) : CSS selectors
        //      • [optional] filter : CSS selectors to filter the descendants of the selected elements that trigger the event.
        //      • [optional] callback : a callable function(properties)
        //      • [optional] target toggle class (default : "is-active") : a class that will be added/removed on the target
        //      Limitation : same targets for all event types
        //      @evol introduce variants - ex: using one() To attach an event that runs only once and then removes itself
        if (properties.evt_types && properties.target)
        {
            var $target = $(properties.target);
            if ($target.length)
            {
                //      Init defaults
                if (!properties.toggle_class) {
                    properties.toggle_class = "is-active";
                }

                //      Keep stuff for later use
                properties.targets = $target;
                properties.source = $this;
                properties.options = options;

                /**
                 *  Attach event listener
                 *  @see http://api.jquery.com/on/
                 */
                $this.on(
                    properties.evt_types,
                    properties.filter,
                    properties,
                    function(e)
                    {
                        e.data.targets.toggleClass(e.data.toggle_class);
                        if (e.data.options.callback && typeof e.data.options.callback === 'function') {
                            e.data.options.callback(e.data);
                        }
                    }
                );
            }
        }
    });
};
<|endoftext|>


<commit_before>// ==UserScript==
// @name        Geocache Height
// @namespace   http://inge.org.uk/userscripts
// @description Works out the height of a geocache in metres above sea level and adds it alongside the coordinates on the geocache listing.
// @include     https://www.geocaching.com/geocache/GC*
// @author      JRI
// @oujs:author JRI
// @copyright   2010-16, James Inge (http://geo.inge.org.uk/)
// @license     MIT License; http://www.opensource.org/licenses/mit-license.php
// @version     1.1.0
// @grant       GM_xmlhttpRequest
// @icon        https://raw.githubusercontent.com/JRInge/userscripts/master/GeocacheHeight/images/height48.png
// @icon64      https://raw.githubusercontent.com/JRInge/userscripts/master/GeocacheHeight/images/height64.png
// @updateURL   http://geo.inge.org.uk/userscripts/Geocache_Height.meta.js
// @downloadURL https://openuserjs.org/install/JRI/Geocache_Height.user.js
// ==/UserScript==

//
//  Works out the height of a geocache.
//
//  v0.0.1  First attempt!
//  v0.0.2  Updated for changes to geocaching.com layout.
//  v0.0.3  Updated for more changes to geocaching.com layout.
//  v1.0.4  Updated for changes to geocaching.com cache page URLS.
//  v1.1.0  Update and re-write for changes to geocaching.com.
//

/*jslint browser, devel */
/*global mapLatLng, window, GM_xmlhttpRequest */

(function () {
    "use strict";
    function getCoords(uriId) {
        /* Looks for coordinates in a URI and returns them as a URI string fragment. Returns null on failure */
        var target = document.getElementById(uriId);
        var pattern = /lat=([\-0-9\.]+)&lng=([\-0-9\.]+)/;
        var matched;

        if (target === null || target.href === undefined) {
            return null;
        }

        matched = target.href.match(pattern);
        if (matched.length === 3) {
            return [matched[1], matched[2]].join();
        }
        return null;
    }

    function formatHeight(height) {
        var heightElement = document.createElement("span");
        heightElement.id = "jriCacheHeight";
        heightElement.innerHTML = (height >= 0) ? " +" : " ";
        heightElement.innerHTML += Math.round(height) + "m";
        return heightElement;
    }

    function isPMOnly() {
        var form = document.getElementById("aspnetForm");
        if (form && /cache_pmo\.aspx/.test(form.action)) {
            return true;
        }
        return false;
    }

    function parseHeight(jsonString) {
        var json;

        try {
            json = JSON.parse(jsonString);
        } catch(e) {
            console.error(e + "Geocache Height didn't get valid JSON data from Google");
            return false;
        }

        if (typeof json.results[0].elevation !== "number") {
            console.error("Geocache Height didn't get the data format it expected from Google");
            return false;
        }

        return json.results[0].elevation;
    }

    var coords = getCoords("ctl00_ContentBody_uxViewLargerMap");
    var target = document.getElementById("uxLatLon");
    var scriptId = "Geocache Height v1.1.0 ";

    //don't run on frames or iframes
    if (window.top !== window.self) {
        return;
    }

    if (isPMOnly()) {
        console.warn(scriptId + "run on Premium Member cache - coordinates not available");
        return;
    }

    if (target === null) {
        console.error(scriptId + "couldn't find where to display height on the cache page");
        return;
    }

    if (coords === null) {
        console.error(scriptId + "couldn't work out coordinates for cache");
        return;
    }

    console.info(scriptId);

    GM_xmlhttpRequest({
        method: 'GET',
        url: "https://maps.googleapis.com/maps/api/elevation/json?sensor=false&locations=" + coords,
        onload: function(responseDetails) {
            var height = parseHeight(responseDetails.responseText);
            if (height !== false) {
                target.parentElement.appendChild(formatHeight(height));
            }
        }
    });
}());<commit_msg>Remove BOM<commit_after>// ==UserScript==
// @name        Geocache Height
// @namespace   http://inge.org.uk/userscripts
// @description Works out the height of a geocache in metres above sea level and adds it alongside the coordinates on the geocache listing.
// @include     https://www.geocaching.com/geocache/GC*
// @author      JRI
// @oujs:author JRI
// @copyright   2010-16, James Inge (http://geo.inge.org.uk/)
// @license     MIT License; http://www.opensource.org/licenses/mit-license.php
// @version     1.1.0
// @grant       GM_xmlhttpRequest
// @icon        https://raw.githubusercontent.com/JRInge/userscripts/master/GeocacheHeight/images/height48.png
// @icon64      https://raw.githubusercontent.com/JRInge/userscripts/master/GeocacheHeight/images/height64.png
// @updateURL   http://geo.inge.org.uk/userscripts/Geocache_Height.meta.js
// @downloadURL https://openuserjs.org/install/JRI/Geocache_Height.user.js
// ==/UserScript==

//
//  Works out the height of a geocache.
//
//  v0.0.1  First attempt!
//  v0.0.2  Updated for changes to geocaching.com layout.
//  v0.0.3  Updated for more changes to geocaching.com layout.
//  v1.0.4  Updated for changes to geocaching.com cache page URLS.
//  v1.1.0  Update and re-write for changes to geocaching.com.
//

/*jslint browser, devel */
/*global mapLatLng, window, GM_xmlhttpRequest */

(function () {
    "use strict";
    function getCoords(uriId) {
        /* Looks for coordinates in a URI and returns them as a URI string fragment. Returns null on failure */
        var target = document.getElementById(uriId);
        var pattern = /lat=([\-0-9\.]+)&lng=([\-0-9\.]+)/;
        var matched;

        if (target === null || target.href === undefined) {
            return null;
        }

        matched = target.href.match(pattern);
        if (matched.length === 3) {
            return [matched[1], matched[2]].join();
        }
        return null;
    }

    function formatHeight(height) {
        var heightElement = document.createElement("span");
        heightElement.id = "jriCacheHeight";
        heightElement.innerHTML = (height >= 0) ? " +" : " ";
        heightElement.innerHTML += Math.round(height) + "m";
        return heightElement;
    }

    function isPMOnly() {
        var form = document.getElementById("aspnetForm");
        if (form && /cache_pmo\.aspx/.test(form.action)) {
            return true;
        }
        return false;
    }

    function parseHeight(jsonString) {
        var json;

        try {
            json = JSON.parse(jsonString);
        } catch(e) {
            console.error(e + "Geocache Height didn't get valid JSON data from Google");
            return false;
        }

        if (typeof json.results[0].elevation !== "number") {
            console.error("Geocache Height didn't get the data format it expected from Google");
            return false;
        }

        return json.results[0].elevation;
    }

    var coords = getCoords("ctl00_ContentBody_uxViewLargerMap");
    var target = document.getElementById("uxLatLon");
    var scriptId = "Geocache Height v1.1.0 ";

    //don't run on frames or iframes
    if (window.top !== window.self) {
        return;
    }

    if (isPMOnly()) {
        console.warn(scriptId + "run on Premium Member cache - coordinates not available");
        return;
    }

    if (target === null) {
        console.error(scriptId + "couldn't find where to display height on the cache page");
        return;
    }

    if (coords === null) {
        console.error(scriptId + "couldn't work out coordinates for cache");
        return;
    }

    console.info(scriptId);

    GM_xmlhttpRequest({
        method: 'GET',
        url: "https://maps.googleapis.com/maps/api/elevation/json?sensor=false&locations=" + coords,
        onload: function(responseDetails) {
            var height = parseHeight(responseDetails.responseText);
            if (height !== false) {
                target.parentElement.appendChild(formatHeight(height));
            }
        }
    });
}());
<|endoftext|>