<commit_before>/*
 * Copyright 2019 The Closure Compiler Authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * @fileoverview Definitions for console debugging facilities implemented in
 *     various browsers but not part of https://console.spec.whatwg.org/.
 * @externs
 */

/**
 * @constructor
 * @see https://cs.chromium.org/search/?q=%22interface+MemoryInfo%22+file:idl+file:WebKit+package:chromium&type=cs
 */
function MemoryInfo() {};

/** @type {number} */
MemoryInfo.prototype.totalJSHeapSize;

/** @type {number} */
MemoryInfo.prototype.usedJSHeapSize;

/** @type {number} */
MemoryInfo.prototype.jsHeapSizeLimit;

/**
 * @param {*} value
 * @return {undefined}
 */
Console.prototype.markTimeline = function(value) {};

/**
 * @param {string=} title
 * @return {undefined}
 */
Console.prototype.profile = function(title) {};

/** @type {Array<ScriptProfile>} */
Console.prototype.profiles;

/**
 * @param {string=} title
 * @return {undefined}
 */
Console.prototype.profileEnd = function(title) {};

/**
 * @param {*} value
 * @return {undefined}
 */
Console.prototype.timeStamp = function(value) {};

/** @record */
class ConsoleTask {
  /**
   * @param {function(): T} fn
   * @return {T}
   * @template T
   */
  run(fn) {}
}

/**
 * https://developer.chrome.com/docs/devtools/console/api/#createtask
 * @param {string} name
 * @return {!ConsoleTask}
 */
Console.prototype.createTask = function(name) {};

/** @type {MemoryInfo} */
Console.prototype.memory;
<commit_msg>Create an extern for Console.prototype.createTask<commit_after>/*
 * Copyright 2019 The Closure Compiler Authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * @fileoverview Definitions for console debugging facilities implemented in
 *     various browsers but not part of https://console.spec.whatwg.org/.
 * @externs
 */

/**
 * @constructor
 * @see https://cs.chromium.org/search/?q=%22interface+MemoryInfo%22+file:idl+file:WebKit+package:chromium&type=cs
 */
function MemoryInfo() {};

/** @type {number} */
MemoryInfo.prototype.totalJSHeapSize;

/** @type {number} */
MemoryInfo.prototype.usedJSHeapSize;

/** @type {number} */
MemoryInfo.prototype.jsHeapSizeLimit;

/**
 * @param {*} value
 * @return {undefined}
 */
Console.prototype.markTimeline = function(value) {};

/**
 * @param {string=} title
 * @return {undefined}
 */
Console.prototype.profile = function(title) {};

/** @type {Array<ScriptProfile>} */
Console.prototype.profiles;

/**
 * @param {string=} title
 * @return {undefined}
 */
Console.prototype.profileEnd = function(title) {};

/**
 * @param {*} value
 * @return {undefined}
 */
Console.prototype.timeStamp = function(value) {};

/** @type {MemoryInfo} */
Console.prototype.memory;
<|endoftext|>


<commit_before>const fs = require('fs-plus')
const {CompositeDisposable, Disposable} = require('atom')
const {dontSaveIf, shouldSave} = require('./controls')

module.exports = {
  subscriptions: null,

  provideService () {
    return {dontSaveIf}
  },

  activate () {
    this.subscriptions = new CompositeDisposable()

    const handleBeforeUnload = this.autosaveAllPaneItems.bind(this)

    window.addEventListener('beforeunload', handleBeforeUnload, true)
    this.subscriptions.add(new Disposable(function () { return window.removeEventListener('beforeunload', handleBeforeUnload, true) }))

    const handleBlur = event => {
      if (event.target === window) {
        this.autosaveAllPaneItems()
      // TODO: We can remove the check for the editor not containing the related target once 1.18 reaches stable
      } else if (event.target.matches('atom-text-editor:not(mini)') && !event.target.contains(event.relatedTarget)) {
        return this.autosavePaneItem(event.target.getModel())
      }
    }

    window.addEventListener('blur', handleBlur, true)
    this.subscriptions.add(new Disposable(() => window.removeEventListener('blur', handleBlur, true)))

    this.subscriptions.add(atom.workspace.onDidAddPaneItem(({item}) => this.autosavePaneItem(item, true)))
    this.subscriptions.add(atom.workspace.onWillDestroyPaneItem(({item}) => this.autosavePaneItem(item)))
  },

  deactivate () {
    this.subscriptions.dispose()
  },

  autosavePaneItem (paneItem, create = false) {
    if (!atom.config.get('autosave.enabled')) return
    if (!paneItem) return
    if (typeof paneItem.getURI !== 'function' || !paneItem.getURI()) return
    if (!create && (typeof paneItem.isModified !== 'function' || !paneItem.isModified())) return
    if (typeof paneItem.getPath !== 'function' || !paneItem.getPath()) return
    if (!shouldSave(paneItem)) return

    try {
      const stats = fs.statSync(paneItem.getPath())
      if (!stats.isFile()) return
    } catch (e) {
      if (e.code !== 'ENOENT') return
      if (!create) return
    }

    const pane = atom.workspace.paneForItem(paneItem)
    if (pane) {
      return pane.saveItem(paneItem)
    } else if (typeof paneItem.save === 'function') {
      return paneItem.save()
    } else {
      return Promise.resolve()
    }
  },

  autosaveAllPaneItems () {
    return atom.workspace.getPaneItems().map((paneItem) => this.autosavePaneItem(paneItem))
  }
}
<commit_msg>Return a Promise that resolves after all pending saves complete<commit_after>const fs = require('fs-plus')
const {CompositeDisposable, Disposable} = require('atom')
const {dontSaveIf, shouldSave} = require('./controls')

module.exports = {
  subscriptions: null,
  pendingSaves: 0,
  resolvePendingSaves: () => {},

  provideService () {
    return {dontSaveIf}
  },

  activate () {
    this.subscriptions = new CompositeDisposable()

    const handleBeforeUnload = this.autosaveAllPaneItems.bind(this)

    window.addEventListener('beforeunload', handleBeforeUnload, true)
    this.subscriptions.add(new Disposable(function () { return window.removeEventListener('beforeunload', handleBeforeUnload, true) }))

    const handleBlur = event => {
      if (event.target === window) {
        this.autosaveAllPaneItems()
      // TODO: We can remove the check for the editor not containing the related target once 1.18 reaches stable
      } else if (event.target.matches('atom-text-editor:not(mini)') && !event.target.contains(event.relatedTarget)) {
        return this.autosavePaneItem(event.target.getModel())
      }
    }

    window.addEventListener('blur', handleBlur, true)
    this.subscriptions.add(new Disposable(() => window.removeEventListener('blur', handleBlur, true)))

    this.subscriptions.add(atom.workspace.onDidAddPaneItem(({item}) => this.autosavePaneItem(item, true)))
    this.subscriptions.add(atom.workspace.onWillDestroyPaneItem(({item}) => this.autosavePaneItem(item)))
  },

  deactivate () {
    this.subscriptions.dispose()
    return new Promise(resolve => {
      if (this.pendingSaves === 0) {
        resolve()
      } else {
        this.resolvePendingSaves = resolve
      }
    })
  },

  autosavePaneItem (paneItem, create = false) {
    if (!atom.config.get('autosave.enabled')) return
    if (!paneItem) return
    if (typeof paneItem.getURI !== 'function' || !paneItem.getURI()) return
    if (!create && (typeof paneItem.isModified !== 'function' || !paneItem.isModified())) return
    if (typeof paneItem.getPath !== 'function' || !paneItem.getPath()) return
    if (!shouldSave(paneItem)) return

    try {
      const stats = fs.statSync(paneItem.getPath())
      if (!stats.isFile()) return
    } catch (e) {
      if (e.code !== 'ENOENT') return
      if (!create) return
    }

    this.pendingSaves++
    const saveComplete = () => {
      this.pendingSaves--
      if (this.pendingSaves === 0) {
        this.resolvePendingSaves()
      }
    }

    const pane = atom.workspace.paneForItem(paneItem)
    let promise = Promise.resolve()
    if (pane) {
      promise = pane.saveItem(paneItem)
    } else if (typeof paneItem.save === 'function') {
      promise = paneItem.save()
    }
    return promise.then(saveComplete, saveComplete)
  },

  autosaveAllPaneItems () {
    return atom.workspace.getPaneItems().map((paneItem) => this.autosavePaneItem(paneItem))
  }
}
<|endoftext|>


<commit_before>import Field from '../Field';
import React from 'react';
import { FormRow, FormField, FormInput } from 'elemental';

module.exports = Field.create({

	displayName: 'GeopointField',
	statics: {
		type: 'Geopoint',
	},

	focusTargetRef: 'lat',

	valueChanged (which, event) {
		this.props.value[which] = event.target.value;
		this.props.onChange({
			path: this.props.path,
			value: this.props.value,
		});
	},

	renderValue () {
		if (this.props.value[1] && this.props.value[0]) {
			return <FormInput noedit>{this.props.value[1]}, {this.props.value[0]}</FormInput>; // eslint-disable-line comma-spacing
		}
		return <FormInput noedit>(not set)</FormInput>;
	},

	renderField () {
		return (
			<FormRow>
				<FormField width="one-half">
					<FormInput name={this.props.path + '[1]'} placeholder="Latitude" ref="lat" value={this.props.value[1]} onChange={this.valueChanged.bind(this, 1)} autoComplete="off" />
				</FormField>
				<FormField width="one-half">
					<FormInput name={this.props.path + '[0]'} placeholder="Longitude" ref="lng" value={this.props.value[0]} onChange={this.valueChanged.bind(this, 0)} autoComplete="off" />
				</FormField>
			</FormRow>
		);
	},

});
<commit_msg>GeoPoint: Fix null derefs and value prop overwrite<commit_after>import Field from '../Field';
import React from 'react';
import { FormRow, FormField, FormInput } from 'elemental';

module.exports = Field.create({

	displayName: 'GeopointField',
	statics: {
		type: 'Geopoint',
	},

	focusTargetRef: 'lat',

	handleLat (event) {
		const { value = [], path, onChange } = this.props;
		const newVal = event.target.value;
		onChange({
			path,
			value: [value[0], newVal],
		});
	},

	handleLong (event) {
		const { value = [], path, onChange } = this.props;
		const newVal = event.target.value;
		onChange({
			path,
			value: [newVal, value[1]],
		});
	},

	renderValue () {
		const { value } = this.props;
		if (value && value[1] && value[0]) {
			return <FormInput noedit>{value[1]}, {value[0]}</FormInput>; // eslint-disable-line comma-spacing
		}
		return <FormInput noedit>(not set)</FormInput>;
	},

	renderField () {
		const { value = [], path } = this.props;
		return (
			<FormRow>
				<FormField width="one-half">
					<FormInput name={path + '[1]'} placeholder="Latitude" ref="lat" value={value[1]} onChange={this.handleLat} autoComplete="off" />
				</FormField>
				<FormField width="one-half">
					<FormInput name={path + '[0]'} placeholder="Longitude" ref="lng" value={value[0]} onChange={this.handleLong} autoComplete="off" />
				</FormField>
			</FormRow>
		);
	},

});
<|endoftext|>


<commit_before>const chokidar = require('chokidar');
const rollup = require('rollup');
const postcss = require('rollup-plugin-postcss');
const url = require('rollup-plugin-url');
const buble = require('rollup-plugin-buble');
const nodeResolve = require('rollup-plugin-node-resolve');
const replace = require('rollup-plugin-replace');
const commonjs = require('rollup-plugin-commonjs');
const uglify = require('rollup-plugin-uglify');
const uglifyLib = require('uglify-js');

const startServer = require('./server');

let cache;

const urlPluginDevelopment = url({
  limit: 20 * 1024, // inline files < 20k, copy files > 20k
  include: [
    '**/*.svg',
    '**/*.png',
    '**/*.jpg',
    '**/*.gif',
    '**/*.json',
  ], // defaults to .svg, .png, .jpg and .gif files
  publicPath: `/bundle/`
});

const basePlugins = (config) => ([
  nodeResolve({
    module: true, // Default: true
    jsnext: true,  // Default: false
    main: true,  // Default: true
    browser: true,
    extensions: ['.js'],  // Default: ['.js']
    preferBuiltins: false,
  }),
  commonjs(),
  postcss({
    extensions: ['.css'],
  }),
  buble({
    jsx: config.jsx
  }),
]);

function development(config) {
  const writeOptions = {
    format: 'iife',
    dest: `dev/bundle.js`
  };

  const generate = () => {
    console.log('Rebundling');
    return rollup.rollup({
      entry: 'src/entry.js',
      plugins: [
        ...basePlugins(config),
        urlPluginDevelopment,
        replace({
          ENVIROMENT: JSON.stringify('development'),
          'process.env.NODE_ENV': JSON.stringify('development')
        }),
      ],
      cache: cache
    })
    .then(bundle => {
      return bundle.write(writeOptions)
        .then(() => {
          console.log('Done!')
        })
    })
    .then(() => urlPluginDevelopment.write(writeOptions))
    .catch(err => {
      console.log(err);
    });
  }

  const watcher = chokidar.watch('src', {
    ignored: /[\/\\]\./,
    persistent: true
  });

  watcher.on('change', () => {
    generate();
  });

  generate();

  startServer();
}

function dist(config, publicPath) {
  const writeOptions = {
    format: 'iife',
    dest: 'dist/bundle.js'
  };

  const urlPluginProduction = url({
    limit: 20 * 1024, // inline files < 20k, copy files > 20k
    include: [
      '**/*.svg',
      '**/*.png',
      '**/*.jpg',
      '**/*.gif',
      '**/*.json',
    ], // defaults to .svg, .png, .jpg and .gif files
    publicPath: `https://interactives.theprate.com/${publicPath}`,
  });

  return rollup.rollup({
    entry: 'src/entry.js',
    plugins: [
      ...basePlugins(config),
      urlPluginProduction,
      uglify({}, uglifyLib.minify),
      replace({
        ENVIROMENT: JSON.stringify('production'),
        'process.env.NODE_ENV': JSON.stringify('production')
      }),
    ],
    cache: cache
  })
  .then(bundle => {
    return bundle.write(writeOptions)
  })
  .then(() => {
    return urlPluginProduction.write(writeOptions)
  });
}

exports.development = development;
exports.dist = dist;
<commit_msg>Allow dangerous tagged templating, used via gg<commit_after>const chokidar = require('chokidar');
const rollup = require('rollup');
const postcss = require('rollup-plugin-postcss');
const url = require('rollup-plugin-url');
const buble = require('rollup-plugin-buble');
const nodeResolve = require('rollup-plugin-node-resolve');
const replace = require('rollup-plugin-replace');
const commonjs = require('rollup-plugin-commonjs');
const uglify = require('rollup-plugin-uglify');
const uglifyLib = require('uglify-js');

const startServer = require('./server');

let cache;

const urlPluginDevelopment = url({
  limit: 20 * 1024, // inline files < 20k, copy files > 20k
  include: [
    '**/*.svg',
    '**/*.png',
    '**/*.jpg',
    '**/*.gif',
    '**/*.json',
  ], // defaults to .svg, .png, .jpg and .gif files
  publicPath: `/bundle/`
});

const basePlugins = (config) => ([
  nodeResolve({
    module: true, // Default: true
    jsnext: true,  // Default: false
    main: true,  // Default: true
    browser: true,
    extensions: ['.js'],  // Default: ['.js']
    preferBuiltins: false,
  }),
  commonjs(),
  postcss({
    extensions: ['.css'],
  }),
  buble({
    jsx: config.jsx,
    transforms: {
      dangerousTaggedTemplateString: true,
    }
  }),
]);

function development(config) {
  const writeOptions = {
    format: 'iife',
    dest: `dev/bundle.js`
  };

  const generate = () => {
    console.log('Rebundling');
    return rollup.rollup({
      entry: 'src/entry.js',
      plugins: [
        ...basePlugins(config),
        urlPluginDevelopment,
        replace({
          ENVIROMENT: JSON.stringify('development'),
          'process.env.NODE_ENV': JSON.stringify('development')
        }),
      ],
      cache: cache
    })
    .then(bundle => {
      return bundle.write(writeOptions)
        .then(() => {
          console.log('Done!')
        })
    })
    .then(() => urlPluginDevelopment.write(writeOptions))
    .catch(err => {
      console.log(err);
    });
  }

  const watcher = chokidar.watch('src', {
    ignored: /[\/\\]\./,
    persistent: true
  });

  watcher.on('change', () => {
    generate();
  });

  generate();

  startServer();
}

function dist(config, publicPath) {
  const writeOptions = {
    format: 'iife',
    dest: 'dist/bundle.js'
  };

  const urlPluginProduction = url({
    limit: 20 * 1024, // inline files < 20k, copy files > 20k
    include: [
      '**/*.svg',
      '**/*.png',
      '**/*.jpg',
      '**/*.gif',
      '**/*.json',
    ], // defaults to .svg, .png, .jpg and .gif files
    publicPath: `https://interactives.theprate.com/${publicPath}`,
  });

  return rollup.rollup({
    entry: 'src/entry.js',
    plugins: [
      ...basePlugins(config),
      urlPluginProduction,
      uglify({}, uglifyLib.minify),
      replace({
        ENVIROMENT: JSON.stringify('production'),
        'process.env.NODE_ENV': JSON.stringify('production')
      }),
    ],
    cache: cache
  })
  .then(bundle => {
    return bundle.write(writeOptions)
  })
  .then(() => {
    return urlPluginProduction.write(writeOptions)
  });
}

exports.development = development;
exports.dist = dist;
<|endoftext|>


<commit_before>const caller = require('callers-path');
const path   = require('path');

// Object of modules for use below in default case statement
const modules = {
	ABG  : ABG,  AHL  : AHL,  ANZV : ANZV, ASC  : ASC,  ASST : ASST,
	BMBT : BMBT, CCM  : CCM,  CDC  : CDC,  CDCD : CDCD, CID  : CID,
	CSU  : CSU,  CVM  : CVM,  DIA  : DIA,  DME  : DME,  DMEK : DMEK,
	DSP  : DSP,  DSPC : DSPC, EGS  : EGS,  EHC  : EHC,  EKM  : EKM,
	EKP  : EKP,  EWS  : EWS,  FBZV : FBZV, FHK  : FHK,  FID  : FID,
	FMBT : FMBT, GM   : GM,   GR   : GR,   GT   : GT,   GTF  : GTF,
	HAC  : HAC,  HKM  : HKM,  IHKA : IHKA, IKE  : IKE,  IRIS : IRIS,
	LCM  : LCM,  LWS  : LWS,  MFL  : MFL,  MID  : MID,
	MM3  : MM3,  MML  : MML,  MMR  : MMR,  NAV  : NAV,  NAVC : NAVC,
	NAVE : NAVE, NAVJ : NAVJ, PDC  : PDC,  PIC  : PIC,  RAD  : RAD,
	RCC  : RCC,  RCSC : RCSC, RDC  : RDC,  RLS  : RLS,  SDRS : SDRS,
	SES  : SES,  SHD  : SHD,  SM   : SM,   SMAD : SMAD, SOR  : SOR,
	STH  : STH,  TCU  : TCU,  TEL  : TEL,  VID  : VID,

	// CANBUS
	ASC1   : ASC1,
	CON1   : CON1,
	DME1   : DME1,
	// KOMBI1 : KOMBI1,
	// FRM1   : FRM1,

	// facepalm
};

// Determine destination network by module name
function dst_detect(data) {
	// If data.bus is defined, use it
	if (typeof data.bus !== 'undefined' && data.bus !== null) {
		return data.bus;
	}

	// No dst? Must be CANBUS.. and I must be lazy.
	if (typeof data.dst === 'undefined' || data.dst === null) {
		return 'can1';
	}

	switch (data.dst) {
		case 'ANZV' : return 'ibus'; // IBUS
		case 'BMBT' : return 'ibus';
		case 'CCM'  : return 'ibus';
		case 'CDC'  : return 'ibus';
		case 'DSP'  : return 'ibus';
		case 'DSPC' : return 'ibus';
		case 'GT'   : return 'ibus';
		case 'IKE'  : return 'ibus';
		case 'LCM'  : return 'ibus';
		case 'LOC'  : return 'ibus';
		case 'MFL'  : return 'ibus';
		case 'MID'  : return 'ibus';
		case 'NAV'  : return 'ibus';
		case 'PDC'  : return 'ibus';
		case 'RAD'  : return 'ibus';
		case 'SES'  : return 'ibus';
		case 'TEL'  : return 'ibus';
		case 'VID'  : return 'ibus';

		case 'ABG'  : return 'kbus'; // KBUS
		case 'EWS'  : return 'kbus';
		case 'GLO'  : return 'kbus'; // IKE will mirror packets with dst GLO from IBUS<->KBUS networks
		case 'GM'   : return 'kbus';
		case 'HAC'  : return 'kbus';
		case 'IHKA' : return 'kbus';
		case 'RLS'  : return 'kbus';
		case 'SHD'  : return 'kbus';
		case 'STH'  : return 'kbus';

		// case 'DIA' : console.log('[node:SEND] [%s->%s] command:', data.src, data.dst, data.msg); return;
		case 'DIA': return false;

		// Dunno? Welp, try to send to both KBUS and IBUS
		default : return 'both-low';
	}
}

function parse_canbus(data) {
	// console.dir(data, { showHidden: true, depth: null, colors: true});

	// Locked-case module acronyms
	data.src.lower = data.src.name.toLowerCase();
	data.src.upper = data.src.name.toUpperCase();

	// Workaround for zeroMQ AMP encoding
	data.msg = data.msg.data;

	// If a defined module for the message source exists,
	// pass the data to it
	if (modules[data.src.upper] != null) {
		modules[data.src.upper].parse_out(data);
		return;
	}
}

// Parse messages from IBUS/KBUS
function parse_low(data) {
	// Parse received data only from actual modules
	if (data.src.name == 'GLO' || data.src.name == 'LOC') return;

	// Locked-case module acronyms
	data.dst.lower = data.dst.name.toLowerCase();
	data.dst.upper = data.dst.name.toUpperCase();
	data.src.lower = data.src.name.toLowerCase();
	data.src.upper = data.src.name.toUpperCase();

	// Non-module-specifc commands
	// Message command types go in this file if the are sent from more than one module
	switch (data.msg[0]) {
		case 0x01: // Request: Device status
			data.command = 'req';
			data.value   = 'device status';
			// Send reply if configured to do so
			bus.cmds.send_device_status(data.dst.name);
			break;

		case 0x02: // Broadcast: Device status
			data.command = 'sta';
			data.value   = 'ready';

			// Set device status ready var
			status[data.src.lower].ready = true;

			if (data.msg[1] === 0x01) {
				// Set device status reset var
				status[data.src.lower].reset = false;
				data.value += ' after reset';
				break;
			}

			// Attempt to send audio power button
			if (data.src.name == 'DSP' || data.src.name == 'RAD') {
				BMBT.toggle_power_if_ready();
				MID.toggle_power_if_ready();
			}
			break;

		case 0x03: // Request: Bus status
			data.command = 'req';
			data.value   = 'bus status';
			break;

		case 0x04: // Broadcast: Bus status
			data.command = 'bro';
			data.value   = 'bus status';
			break;

		case 0x10: // Request: Ignition status
			data.command = 'req';
			data.value   = 'ignition status';
			break;

		case 0x12: // Request: IKE sensor status
			data.command = 'req';
			data.value   = 'IKE sensor status';
			break;

		case 0x14: // Request: Country coding data request
			data.command = 'req';
			data.value   = 'country coding data';
			break;

		case 0x16: // Request: Odometer
			data.command = 'req';
			data.value   = 'odometer';
			break;

		case 0x1B: // ACK text message
			data.command = 'ack';
			data.value   = parseFloat(data.msg[1])+' text messages';
			break;

		case 0x1D: // Request: Temperature values
			data.command = 'req';
			data.value   = 'temperature values';
			break;

		case 0x21: // Update: Menu text
			data.command = 'upd';
			data.value   = 'menu text: '+hex.h2s(data.msg);
			break;

		case 0x22: // ACK text message
			data.command = 'ack';
			data.value   = parseFloat(data.msg[1])+' text messages';
			break;

		case 0x23: { // Update: Display text
			data.command = 'upd';
			let layout;

			// data.msg[1] - Layout
			switch (data.msg[1]) {
				case 0x00 : layout = 'phone';        break;
				case 0x24 : layout = 'checkcontrol'; break;
				case 0x40 : layout = 'display';      break;
				case 0x50 : layout = 'cluster';      return;
				case 0x62 : layout = 'radio';        break;
				default   : layout = 'unknown '+data.msg[1]; break;
			}

			// data.msg[2] - flags
			let flags = [];
			if (bitmask.test(data.msg[2], 0x20)) { flags.push('CLS');       }
			if (bitmask.test(data.msg[2], 0x40)) { flags.push('PartTx');    }
			if (bitmask.test(data.msg[2], 0x80)) { flags.push('SetCursor'); }

			data.value = 'display text, F/L: '+flags+'/'+layout+', \''+hex.h2s(data.msg)+'\'';
			break;
		}

		case 0x32: { // Broadcast: Volume control
			// data.msg[1] -
			// -1 : 10
			// -2 : 20
			// -3 : 30
			// -4 : 40
			// -5 : 50
			// +1 : 11
			// +2 : 21
			// +3 : 31
			// +4 : 41
			// +5 : 51

			data.command = 'con';
			let volume = parseFloat(data.msg[1]);

			// Determine volume change direction
			let direction  = bitmask.test(volume, 0x01) && '+' || '-';
			let volume_inc = Math.floor(volume/0x10);

			data.value = 'volume '+direction+volume_inc+' ('+volume+')';

			if (config.media.kodi.enable && config.media.kodi.mfl_volume) {
				let volume_target;

				if (status.kodi.volume.level === null) status.kodi.volume.level = 25;

				switch (direction) {
					case '+' : volume_target = status.kodi.volume.level+volume_inc; break;
					case '-' : volume_target = status.kodi.volume.level-volume_inc;
				}

				if (volume_target < 1  ) volume_target = 0;
				if (volume_target > 100) volume_target = 100;

				kodi.volume(volume_target);
			}
			break;
		}

		case 0x41: // Request: OBC value
			data.command = 'req';
			data.value   = 'OBC value '+obc_values.h2n(data.msg[1]);
			break;

		case 0x50: // Request: Check-control sensor status
			data.command = 'req';
			data.value   = 'check control sensor status';
			break;

		case 0x53: // Request: Vehicle data
			data.command = 'req';
			data.value   = 'vehicle data';
			break;

		case 0x5A: // Request: Light status
			data.command = 'req';
			data.value   = 'light status';
			break;

		case 0x5D: // Request: Light dimmer status
			data.command = 'req';
			data.value   = 'light dimmer status';
			break;

		case 0x71: // Request: Rain sensor status
			data.command = 'req';
			data.value   = 'rain sensor status';
			break;

		case 0x73: // Request: Immobiliser status
			data.command = 'req';
			data.value   = 'immobiliser status';
			break;

		case 0x75: // Request: Wiper status
			data.command = 'req';
			data.value   = 'wiper status';
			break;

		case 0x79: // Request: Door/flap status
			data.command = 'req';
			data.value   = 'door/flap status';
			break;

		case 0xA2: // Reply: Diagnostic command rejected
			data.command = 'rep';
			data.value   = 'diagnostic command rejected';
			break;

		case 0xFF: // Reply: Diagnostic command not acknowledged
			data.command = 'rep';
			data.value   = 'diagnostic command not acknowledged';
			break;
	}

	// If the global parser didn't handle it, try a module-specifc one
	if (data.command) {
		log.bus(data);
	}
	else if (modules[data.src.upper]) {
		modules[data.src.upper].parse_out(data);
	}
	else {
		data.command = 'unk';
		data.value   = Buffer.from(data.msg);
		log.bus(data);
	}

	// Parse sent data to emulated modules
	if (config.emulate[data.dst.lower] === true) modules[data.dst.upper].parse_in(data);
}

// Send vehicle bus data to bmwd
function send(data) {
	// Get the name of the calling module if the data destination hasn't been specified
	if (typeof data.dst === 'undefined' || data.dst === null) {
		data.dst = path.parse(caller()).name;
	}

	socket.bus_tx(dst_detect(data), data);
}

// Data handler
function recv(data) {
	// console.dir(data, { showHidden: true, depth: null, colors: true});

	// No dst? Must be CANBUS
	if (typeof data.dst === 'undefined' || data.dst === null) {
		parse_canbus(data);
		return;
	}

	parse_low(data);
}

module.exports = {
	send    : (data) => { return send(data); },
	receive : (data) => { return recv(data); },
};
<commit_msg>Debugging<commit_after>const caller = require('callers-path');
const path   = require('path');

// Object of modules for use below in default case statement
const modules = {
	ABG  : ABG,  AHL  : AHL,  ANZV : ANZV, ASC  : ASC,  ASST : ASST,
	BMBT : BMBT, CCM  : CCM,  CDC  : CDC,  CDCD : CDCD, CID  : CID,
	CSU  : CSU,  CVM  : CVM,  DIA  : DIA,  DME  : DME,  DMEK : DMEK,
	DSP  : DSP,  DSPC : DSPC, EGS  : EGS,  EHC  : EHC,  EKM  : EKM,
	EKP  : EKP,  EWS  : EWS,  FBZV : FBZV, FHK  : FHK,  FID  : FID,
	FMBT : FMBT, GM   : GM,   GR   : GR,   GT   : GT,   GTF  : GTF,
	HAC  : HAC,  HKM  : HKM,  IHKA : IHKA, IKE  : IKE,  IRIS : IRIS,
	LCM  : LCM,  LWS  : LWS,  MFL  : MFL,  MID  : MID,
	MM3  : MM3,  MML  : MML,  MMR  : MMR,  NAV  : NAV,  NAVC : NAVC,
	NAVE : NAVE, NAVJ : NAVJ, PDC  : PDC,  PIC  : PIC,  RAD  : RAD,
	RCC  : RCC,  RCSC : RCSC, RDC  : RDC,  RLS  : RLS,  SDRS : SDRS,
	SES  : SES,  SHD  : SHD,  SM   : SM,   SMAD : SMAD, SOR  : SOR,
	STH  : STH,  TCU  : TCU,  TEL  : TEL,  VID  : VID,

	// CANBUS
	ASC1   : ASC1,
	CON1   : CON1,
	DME1   : DME1,
	// KOMBI1 : KOMBI1,
	// FRM1   : FRM1,

	// facepalm
};

// Determine destination network by module name
function dst_detect(data) {
	// If data.bus is defined, use it
	if (typeof data.bus !== 'undefined' && data.bus !== null) {
		return data.bus;
	}

	// No dst? Must be CANBUS.. and I must be lazy.
	if (typeof data.dst === 'undefined' || data.dst === null) {
		return 'can1';
	}

	switch (data.dst) {
		case 'ANZV' : return 'ibus'; // IBUS
		case 'BMBT' : return 'ibus';
		case 'CCM'  : return 'ibus';
		case 'CDC'  : return 'ibus';
		case 'DSP'  : return 'ibus';
		case 'DSPC' : return 'ibus';
		case 'GT'   : return 'ibus';
		case 'IKE'  : return 'ibus';
		case 'LCM'  : return 'ibus';
		case 'LOC'  : return 'ibus';
		case 'MFL'  : return 'ibus';
		case 'MID'  : return 'ibus';
		case 'NAV'  : return 'ibus';
		case 'PDC'  : return 'ibus';
		case 'RAD'  : return 'ibus';
		case 'SES'  : return 'ibus';
		case 'TEL'  : return 'ibus';
		case 'VID'  : return 'ibus';

		case 'ABG'  : return 'kbus'; // KBUS
		case 'EWS'  : return 'kbus';
		case 'GLO'  : return 'kbus'; // IKE will mirror packets with dst GLO from IBUS<->KBUS networks
		case 'GM'   : return 'kbus';
		case 'HAC'  : return 'kbus';
		case 'IHKA' : return 'kbus';
		case 'RLS'  : return 'kbus';
		case 'SHD'  : return 'kbus';
		case 'STH'  : return 'kbus';

		// case 'DIA' : console.log('[node:SEND] [%s->%s] command:', data.src, data.dst, data.msg); return;
		case 'DIA': return false;

		// Dunno? Welp, try to send to both KBUS and IBUS
		default : return 'both-low';
	}
}

function parse_canbus(data) {
	// console.dir(data, { showHidden: true, depth: null, colors: true});

	// Locked-case module acronyms
	data.src.lower = data.src.name.toLowerCase();
	data.src.upper = data.src.name.toUpperCase();

	// Workaround for zeroMQ AMP encoding
	data.msg = data.msg.data;

	// If a defined module for the message source exists,
	// pass the data to it
	if (modules[data.src.upper] != null) {
		modules[data.src.upper].parse_out(data);
		return;
	}
}

// Parse messages from IBUS/KBUS
function parse_low(data) {
	// Parse received data only from actual modules
	if (data.src.name == 'GLO' || data.src.name == 'LOC') return;

	// Locked-case module acronyms
	data.dst.lower = data.dst.name.toLowerCase();
	data.dst.upper = data.dst.name.toUpperCase();
	data.src.lower = data.src.name.toLowerCase();
	data.src.upper = data.src.name.toUpperCase();

	// Non-module-specifc commands
	// Message command types go in this file if the are sent from more than one module
	switch (data.msg[0]) {
		case 0x01: // Request: Device status
			data.command = 'req';
			data.value   = 'device status';
			// Send reply if configured to do so
			bus.cmds.send_device_status(data.dst.name);
			break;

		case 0x02: // Broadcast: Device status
			data.command = 'sta';
			data.value   = 'ready';

			// Set device status ready var
			status[data.src.lower].ready = true;

			if (data.msg[1] === 0x01) {
				// Set device status reset var
				status[data.src.lower].reset = false;
				data.value += ' after reset';
				break;
			}

			// Attempt to send audio power button
			if (data.src.name == 'DSP' || data.src.name == 'RAD') {
				BMBT.toggle_power_if_ready();
				MID.toggle_power_if_ready();
			}
			break;

		case 0x03: // Request: Bus status
			data.command = 'req';
			data.value   = 'bus status';
			break;

		case 0x04: // Broadcast: Bus status
			data.command = 'bro';
			data.value   = 'bus status';
			break;

		case 0x10: // Request: Ignition status
			data.command = 'req';
			data.value   = 'ignition status';
			break;

		case 0x12: // Request: IKE sensor status
			data.command = 'req';
			data.value   = 'IKE sensor status';
			break;

		case 0x14: // Request: Country coding data request
			data.command = 'req';
			data.value   = 'country coding data';
			break;

		case 0x16: // Request: Odometer
			data.command = 'req';
			data.value   = 'odometer';
			break;

		case 0x1B: // ACK text message
			data.command = 'ack';
			data.value   = parseFloat(data.msg[1])+' text messages';
			break;

		case 0x1D: // Request: Temperature values
			data.command = 'req';
			data.value   = 'temperature values';
			break;

		case 0x21: // Update: Menu text
			data.command = 'upd';
			data.value   = 'menu text: '+hex.h2s(data.msg);
			break;

		case 0x22: // ACK text message
			data.command = 'ack';
			data.value   = parseFloat(data.msg[1])+' text messages';
			break;

		case 0x23: { // Update: Display text
			data.command = 'upd';
			let layout;

			// data.msg[1] - Layout
			switch (data.msg[1]) {
				case 0x00 : layout = 'phone';        break;
				case 0x24 : layout = 'checkcontrol'; break;
				case 0x40 : layout = 'display';      break;
				case 0x50 : layout = 'cluster';      return;
				case 0x62 : layout = 'radio';        break;
				default   : layout = 'unknown '+data.msg[1]; break;
			}

			// data.msg[2] - flags
			let flags = [];
			if (bitmask.test(data.msg[2], 0x20)) { flags.push('CLS');       }
			if (bitmask.test(data.msg[2], 0x40)) { flags.push('PartTx');    }
			if (bitmask.test(data.msg[2], 0x80)) { flags.push('SetCursor'); }

			data.value = 'display text, F/L: '+flags+'/'+layout+', \''+hex.h2s(data.msg)+'\'';
			break;
		}

		case 0x32: { // Broadcast: Volume control
			// data.msg[1] -
			// -1 : 10
			// -2 : 20
			// -3 : 30
			// -4 : 40
			// -5 : 50
			// +1 : 11
			// +2 : 21
			// +3 : 31
			// +4 : 41
			// +5 : 51

			data.command = 'con';
			let volume = parseFloat(data.msg[1]);

			// Determine volume change direction
			let direction  = bitmask.test(volume, 0x01) && '+' || '-';
			let volume_inc = Math.floor(volume/0x10);

			data.value = 'volume '+direction+volume_inc+' ('+volume+')';

			if (config.media.kodi.enable && config.media.kodi.mfl_volume) {
				let volume_target;

				if (status.kodi.volume.level === null) status.kodi.volume.level = 25;

				switch (direction) {
					case '+' : volume_target = status.kodi.volume.level+volume_inc; break;
					case '-' : volume_target = status.kodi.volume.level-volume_inc;
				}

				if (volume_target < 1  ) volume_target = 0;
				if (volume_target > 100) volume_target = 100;

				kodi.volume(volume_target);
			}
			break;
		}

		case 0x41: // Request: OBC value
			data.command = 'req';
			data.value   = 'OBC value '+obc_values.h2n(data.msg[1]);
			break;

		case 0x50: // Request: Check-control sensor status
			data.command = 'req';
			data.value   = 'check control sensor status';
			break;

		case 0x53: // Request: Vehicle data
			data.command = 'req';
			data.value   = 'vehicle data';
			break;

		case 0x5A: // Request: Light status
			data.command = 'req';
			data.value   = 'light status';
			break;

		case 0x5D: // Request: Light dimmer status
			data.command = 'req';
			data.value   = 'light dimmer status';
			break;

		case 0x71: // Request: Rain sensor status
			data.command = 'req';
			data.value   = 'rain sensor status';
			break;

		case 0x73: // Request: Immobiliser status
			data.command = 'req';
			data.value   = 'immobiliser status';
			break;

		case 0x75: // Request: Wiper status
			data.command = 'req';
			data.value   = 'wiper status';
			break;

		case 0x79: // Request: Door/flap status
			data.command = 'req';
			data.value   = 'door/flap status';
			break;

		case 0xA2: // Reply: Diagnostic command rejected
			data.command = 'rep';
			data.value   = 'diagnostic command rejected';
			break;

		case 0xFF: // Reply: Diagnostic command not acknowledged
			data.command = 'rep';
			data.value   = 'diagnostic command not acknowledged';
			break;
	}

	// If the global parser didn't handle it, try a module-specifc one
	if (data.command) {
		log.bus(data);
	}
	else if (modules[data.src.upper]) {
		modules[data.src.upper].parse_out(data);
	}
	else {
		data.command = 'unk';
		data.value   = Buffer.from(data.msg);
		log.bus(data);
	}

	// Parse sent data to emulated modules
	if (config.emulate[data.dst.lower] === true) modules[data.dst.upper].parse_in(data);
}

// Send vehicle bus data to bmwd
function send(data) {
	// Get the name of the calling module if the data destination hasn't been specified
	if (typeof data.dst === 'undefined' || data.dst === null) {
		data.dst = path.parse(caller()).name;
	}

	let dst_bus = dst_detect(data);
	console.log('sending packet to dst bus: %s', dst_bus);
	socket.bus_tx(dst_bus, data);
}

// Data handler
function recv(data) {
	// console.dir(data, { showHidden: true, depth: null, colors: true});

	// No dst? Must be CANBUS
	if (typeof data.dst === 'undefined' || data.dst === null) {
		parse_canbus(data);
		return;
	}

	parse_low(data);
}

module.exports = {
	send    : (data) => { return send(data); },
	receive : (data) => { return recv(data); },
};
<|endoftext|>


<commit_before>'use strict';

var path      = require('path'),
    crypto    = require('crypto'),
    fileUtils = require('./fileUtils');

function checksum(str, algorithm, encoding) {
  return crypto
    .createHash(algorithm || 'sha1')
    .update(str, 'utf8')
    .digest(encoding || 'hex');
}

function bindCheckSum(from, config, cb) {

  fileUtils.iterate(from, function (data) {

    var name;
    data.files
      .forEach(function (file) {
        name = path.basename(file.relative, '.js');
        if('index' !== name) {
          (config[name] || {}).checksum = checksum(file.contents.toString());
          (config[name] || {}).updateAt = file.stat.mtime;
        }
      });

    cb(attachCheckSumPerDependency(config));
  });
}

function attachCheckSumPerDependency(data) {

  Object
    .keys(data)
    .forEach(function (service) {

      data[service].dependencies = data[service]
        .dependencies
        .map(function (req) {

          if(req.name) {
            req.checksum = (data[req.name] || {}).checksum;
            req.updateAt = (data[req.name] || {}).updateAt;
          }
          return req;
        });

    });

  return data;
}

module.exports = bindCheckSum;
<commit_msg>Improve checkSum calcul<commit_after>'use strict';

var path      = require('path'),
    crypto    = require('crypto'),
    fileUtils = require('./fileUtils');

function checksum(str, algorithm, encoding) {
  return crypto
    .createHash(algorithm || 'sha1')
    .update(str, 'utf8')
    .digest(encoding || 'hex');
}

function bindCheckSum(from, config, cb) {

  fileUtils.iterate(from, function (data) {

    var name;
    data.files
      .forEach(function (file) {
        name = path.basename(file.relative, '.js');
        if('index' !== name) {
          (config[name] || {}).checksum = checksum(file.contents.toString() + file.stat.mtime);
          (config[name] || {}).updateAt = file.stat.mtime;
        }
      });

    cb(attachCheckSumPerDependency(config));
  });
}

function attachCheckSumPerDependency(data) {

  Object
    .keys(data)
    .forEach(function (service) {

      data[service].dependencies = data[service]
        .dependencies
        .map(function (req) {

          if(req.name) {
            req.checksum = (data[req.name] || {}).checksum;
            req.updateAt = (data[req.name] || {}).updateAt;
          }
          return req;
        });

    });

  return data;
}

module.exports = bindCheckSum;
<|endoftext|>


<commit_before>Function.prototype.curry = function() {
  var fun = this;
  var args = [].slice.call(arguments, 0);

  return function() {
    return fun.apply(this,
      args.concat([].slice.call(arguments, 0)));
  };
}

var chillhub = module.exports = function(Firebase, Date) {
  return {
    login: login.curry(Firebase, Date)
  };
};

function UndefinedPropertyError(property) {
  this.kind = 'error#undefined-property';
  this.property = property;
}

function FirebaseAuthWithCustomTokenError(error) {
  this.kind = 'error#firebase';
  this.method = 'authWithCustomToken';
  this.error = error;
}

function FirebaseUpdateError(error) {
  this.kind = 'error#firebase';
  this.method = 'update';
  this.error = error;
}

function login(Firebase, Date, token, callback) {
  if (callback) {
    if (token) {
      var firebase = new Firebase('https://firstbuild.firebaseio.com');

      firebase.authWithCustomToken(token, function(e, auth) {
        if (e) {
          callback(new FirebaseAuthWithCustomTokenError(e));
        }
        else {
          var uid = auth.uid;

          callback(null, {
            uid: uid,
            create: createChillHub.curry(Date, firebase, uid)
          });
        }
      });
    }
    else {
      callback(new UndefinedPropertyError('token'));
    }
  }
  else {
    throw new UndefinedPropertyError('callback');
  }
}

function createChillHub(Date, firebase, uid, uuid, hardware_version, software_version, callback) {
  if (callback) {
    if (uuid) {
      if (hardware_version) {
        if (software_version) {
          var now = new Date().toISOString();

          var value = {
            created: now,
            updated: now,
            hardware_version: hardware_version,
            software_version: software_version,
            "status": "connected"
          };

          var chillhub = firebase
            .child('users').child(uid)
            .child('devices').child('chillhubs').child(uuid);

          var setStatus = function(status) {
            var value = {
               "status" : status
            }
            chillhub.update(value, function(err) {
               if (err) {
                  console.log("Error setting status to " + status);
               }
            });
          }

          var errorGettingRoot = function(err) {
            console.log("Error getting chillhub root. " + err);
          }

          var register = function(value) {
             chillhub.update(value, function(e) {
                if (e) {
                   callback(new FirebaseUpdateError(e));
                }
                else {
                   var obj = {
                      create: createAttachment.curry(chillhub),
                      chillhub: chillhub,
                      setStatus: setStatus
                   }
                   callback(null, obj);
                }
             });
          }

          var getDevicesFromDeviceTypeSnapshot = function(types) {
             var devices = [];
             for (var i=0; i<types.length; i++) {
               types[i].forEach(function(childSnap) {
                  if (childSnap.hasChildren()) {
                     devices.push(childSnap);
                  }
               });
             }
             return devices;
          }

          var getDevicesFromRootSnapshot = function(rootSnap) {
             var deviceTypeSnaps = [];
             if (rootSnap.hasChildren()) {
                rootSnap.forEach(function(childSnap){
                   if (childSnap.hasChildren()) {
                      deviceTypeSnaps.push(childSnap);
                   }
                });
             }
             return getDevicesFromDeviceTypeSnapshot(deviceTypeSnaps);
          }

          var setDevicesToDisconnected = function(root) {
            var status = {"status": "disconnected"};
            var devices = getDevicesFromRootSnapshot(root);
            for (var i=0; i<devices.length; i++) {
               devices[i].ref().update(status);
            }
          }

          var gotRoot = function(rootSnap) {
             if (rootSnap.val() != null) {
                // This chillhub is on firebase already.
                // Use the old created and updated times.
                // Update the HW and SW versions to newest.
                value.created = rootSnap.val().created;
                value.updated = rootSnap.val().updated;
                setDevicesToDisconnected(rootSnap);
                register(value);
             } else {
                register(value);
             }
          }

          // See if this chillhub is already on firebase.
          chillhub.once("value", gotRoot, errorGettingRoot);

        }
        else {
          callback(new UndefinedPropertyError('software_version'));
        }
      }
      else {
        callback(new UndefinedPropertyError('hardware_version'));
      }
    }
    else {
      callback(new UndefinedPropertyError('uuid'));
    }
  }
  else {
    throw new UndefinedPropertyError('callback');
  }
}

var setAttachmentStatus = function(attachment, status) {
   var value = {
      "status" : status
   }
   attachment.update(value, function(err) {
      if (err) {
         console.log("Error setting status to " + status);
      }
   });
}

var createAttachment = function(root, type, uuid, callback) {
   if (callback) {
      if (uuid) {
         if (type) {
            var attachment = root.child(type).child(uuid);
            // See if the child is already registered.
            console.log("Checking to see if attachment exists in firebase.");
            var now = new Date().toISOString();
            attachment.once('value',
                  function(snap) {
                     var value = {
                        "created": now,
                        "status": "connected"
                     }
                     if (snap.val() != null) {
                        console.log("Attachment does exist.");
                        value.created = snap.val().created;
                     } else {
                        console.log("Attachment does not exist.");
                     }
                     attachment.update(value);
                     callback(null, {
                        createResource: createAttachmentResource.curry(attachment),
                        setStatus: setAttachmentStatus.curry(attachment)
                     });
                  },
                  function(err) {
                     console.log("Error getting attachment info.");
                  });
         } else {
            callback(new UndefinedPropertyError('type'));
         }
      } else {
         callback(new UndefinedPropertyError('uuid'));
      }
   } else {
      throw new UndefinedPropertyError('callback');
   }
}

var createAttachmentResource = function(attachment, key, initialValue, onChange, callback) {
   if (callback === undefined) {
      throw new UndefinedPropertyError('callback');
   } else if (initialValue === undefined) {
      callback(new UndefinedPropertyError('initialValue'));
   } else if (key === undefined) {
      callback(new UndefinedPropertyError('key'));
   } else if (onChange === undefined) {
      callback(new UndefinedPropertyError('onChange'));
   } else {
      attachment.transaction(function(currentdata) {
         if(currentdata == null) {
            return{key: initialValue};
         } else {
            currentdata[key] = initialValue;
            return currentdata;
         }

      }, function(error, committed, snap) {
         if (error) {
            console.log("Resource creation transaction failed for resource " + key);
         } else if (!committed) {
            console.log("Looks like we aborted the creation transaction for resource " + key);
         } else {
            if (onChange) {
               attachment.child(key).on("value", onChange);
            }
            callback(null, attachment);
         }
      });
      return;
   }
}
<commit_msg>Added onDisconnect for chillhub status.<commit_after>Function.prototype.curry = function() {
  var fun = this;
  var args = [].slice.call(arguments, 0);

  return function() {
    return fun.apply(this,
      args.concat([].slice.call(arguments, 0)));
  };
}

var chillhub = module.exports = function(Firebase, Date) {
  return {
    login: login.curry(Firebase, Date)
  };
};

function UndefinedPropertyError(property) {
  this.kind = 'error#undefined-property';
  this.property = property;
}

function FirebaseAuthWithCustomTokenError(error) {
  this.kind = 'error#firebase';
  this.method = 'authWithCustomToken';
  this.error = error;
}

function FirebaseUpdateError(error) {
  this.kind = 'error#firebase';
  this.method = 'update';
  this.error = error;
}

function login(Firebase, Date, token, callback) {
  if (callback) {
    if (token) {
      //var firebase = new Firebase('https://firstbuild.firebaseio.com');
      var firebase = new Firebase('https://mobius-firstbuild.firebaseio.com');

      firebase.authWithCustomToken(token, function(e, auth) {
        if (e) {
          callback(new FirebaseAuthWithCustomTokenError(e));
        }
        else {
          var uid = auth.uid;

          callback(null, {
            uid: uid,
            create: createChillHub.curry(Date, firebase, uid)
          });
        }
      });
    }
    else {
      callback(new UndefinedPropertyError('token'));
    }
  }
  else {
    throw new UndefinedPropertyError('callback');
  }
}

function createChillHub(Date, firebase, uid, uuid, hardware_version, software_version, callback) {
  if (callback) {
    if (uuid) {
      if (hardware_version) {
        if (software_version) {
          var now = new Date().toISOString();

          var value = {
            created: now,
            updated: now,
            hardware_version: hardware_version,
            software_version: software_version,
            "status": "connected"
          };

          var chillhub = firebase
            .child('users').child(uid)
            .child('devices').child('chillhubs').child(uuid);

          var setStatus = function(status) {
            var value = {
               "status" : status
            }
            chillhub.update(value, function(err) {
               if (err) {
                  console.log("Error setting status to " + status);
               }
            });
          }

          var errorGettingRoot = function(err) {
            console.log("Error getting chillhub root. " + err);
          }

          var register = function(value) {
             chillhub.update(value, function(e) {
                if (e) {
                   callback(new FirebaseUpdateError(e));
                }
                else {
                   var obj = {
                      create: createAttachment.curry(chillhub),
                      chillhub: chillhub,
                      setStatus: setStatus
                   }
                   callback(null, obj);
                }
             });
          }

          var getDevicesFromDeviceTypeSnapshot = function(types) {
             var devices = [];
             for (var i=0; i<types.length; i++) {
               types[i].forEach(function(childSnap) {
                  if (childSnap.hasChildren()) {
                     devices.push(childSnap);
                  }
               });
             }
             return devices;
          }

          var getDevicesFromRootSnapshot = function(rootSnap) {
             var deviceTypeSnaps = [];
             if (rootSnap.hasChildren()) {
                rootSnap.forEach(function(childSnap){
                   if (childSnap.hasChildren()) {
                      deviceTypeSnaps.push(childSnap);
                   }
                });
             }
             return getDevicesFromDeviceTypeSnapshot(deviceTypeSnaps);
          }

          var setDevicesToDisconnected = function(root) {
            var status = {"status": "disconnected"};
            var devices = getDevicesFromRootSnapshot(root);
            for (var i=0; i<devices.length; i++) {
               devices[i].ref().update(status);
            }
          }

          var gotRoot = function(rootSnap) {
             if (rootSnap.val() != null) {
                // This chillhub is on firebase already.
                // Use the old created and updated times.
                // Update the HW and SW versions to newest.
                value.created = rootSnap.val().created;
                value.updated = rootSnap.val().updated;
                setDevicesToDisconnected(rootSnap);
                rootSnap.ref().onDisconnect().update({"status": "disconnected"});
                register(value);
             } else {
                register(value);
             }
          }

          // See if this chillhub is already on firebase.
          chillhub.once("value", gotRoot, errorGettingRoot);

        }
        else {
          callback(new UndefinedPropertyError('software_version'));
        }
      }
      else {
        callback(new UndefinedPropertyError('hardware_version'));
      }
    }
    else {
      callback(new UndefinedPropertyError('uuid'));
    }
  }
  else {
    throw new UndefinedPropertyError('callback');
  }
}

var setAttachmentStatus = function(attachment, status) {
   var value = {
      "status" : status
   }
   attachment.update(value, function(err) {
      if (err) {
         console.log("Error setting status to " + status);
      }
   });
}

var createAttachment = function(root, type, uuid, callback) {
   if (callback) {
      if (uuid) {
         if (type) {
            var attachment = root.child(type).child(uuid);
            // See if the child is already registered.
            console.log("Checking to see if attachment exists in firebase.");
            var now = new Date().toISOString();
            attachment.once('value',
                  function(snap) {
                     var value = {
                        "created": now,
                        "status": "connected"
                     }
                     if (snap.val() != null) {
                        console.log("Attachment does exist.");
                        value.created = snap.val().created;
                     } else {
                        console.log("Attachment does not exist.");
                     }
                     attachment.update(value);
                     callback(null, {
                        createResource: createAttachmentResource.curry(attachment),
                        setStatus: setAttachmentStatus.curry(attachment)
                     });
                  },
                  function(err) {
                     console.log("Error getting attachment info.");
                  });
         } else {
            callback(new UndefinedPropertyError('type'));
         }
      } else {
         callback(new UndefinedPropertyError('uuid'));
      }
   } else {
      throw new UndefinedPropertyError('callback');
   }
}

var createAttachmentResource = function(attachment, key, initialValue, onChange, callback) {
   if (callback === undefined) {
      throw new UndefinedPropertyError('callback');
   } else if (initialValue === undefined) {
      callback(new UndefinedPropertyError('initialValue'));
   } else if (key === undefined) {
      callback(new UndefinedPropertyError('key'));
   } else if (onChange === undefined) {
      callback(new UndefinedPropertyError('onChange'));
   } else {
      attachment.transaction(function(currentdata) {
         if(currentdata == null) {
            return{key: initialValue};
         } else {
            currentdata[key] = initialValue;
            return currentdata;
         }

      }, function(error, committed, snap) {
         if (error) {
            console.log("Resource creation transaction failed for resource " + key);
         } else if (!committed) {
            console.log("Looks like we aborted the creation transaction for resource " + key);
         } else {
            if (onChange) {
               attachment.child(key).on("value", onChange);
            }
            callback(null, attachment);
         }
      });
      return;
   }
}
<|endoftext|>


<commit_before>// global module loader
var M = (function(miids, modules, wsCache, fn, win) {

    // check and initialize websockets
    if (!win.WebSocket || !win.history) {
        return alert('You have an old browser :(\n\nPlease update to the latest version\nand this site will work.');
    }

    var webSocket = new WebSocket('ws://' + win.location.host + '/');
    var head = document.getElementsByTagName('head')[0];

    // module class
    var Mono = {
        // remove the module instance cache
        die: function () {
            delete miids[this.miid];
        },
        // listen to events
        on: listen,
        one: function (event, handler) {
            listen.call(this, event, handler, true);
        },
        // remove listeners
        off: function (event, handler) {
            var events = this.mono.events;

            if (events && events[event]) {

                if (handler) {
                    var rm = [];

                    for (var i = 0; i < events[event].length; ++i) {
                        if (events[event][i] === handler) {
                            events[event][i] = null;
                            rm.push([event, i]);
                        }
                    }

                    remove(events, rm);

                } else {
                    delete events[event];
                }
            }
        },
        // emit event
        emit: function(event) {
            var events = this.mono.events;

            // mark miid as ready
            if (event === 'ready') {
                this.mono.ready = true;
            }

            if (events && events[event]) {

                // slice first argument
                var args = convertToArray(arguments).slice(1);
                var rm = [];

                for (var i = 0; i < events[event].length; ++i) {
                    if (events[event][i]) {

                        // call registered Methods
                        events[event][i].apply(this, args);

                        // remove from event buffer, if once is true
                        if (events[event][i].one) {
                            events[event][i] = null;
                            rm.push([event, i]);
                        }
                    }
                }

                remove(events, rm);
            }
        },
        // emit an event on a miid
        push: function (miid, event) {
            if (miids[miid]) {
                miids[miid].emit.apply(miids[miid], convertToArray(arguments).slice(1));
            }
        },
        // emit on all miids
        pushAll: function (event) {
            var args = convertToArray(arguments);
            for (var miid in miids) {
                miids[miid].emit.apply(miids[miid], args);
            }
        }
    };

    function convertToArray (object) {
        return Array.prototype.slice.call(object);
    }

    // clone object
    function clone (object) {
        var O = (function() {});
        O.prototype = object || {};
        return new O();
    }

    // load miid
    function load (miid, parentMiid) {

        // don't load the same miid more than once
        if (!miid || miids[miid]) {
            return;
        }

        // wait for websocket
        if (webSocket.readyState !== webSocket.OPEN) {
            return webSocket.onopen = function () {
                load(miid, parentMiid);
            };
        }

        // get miid config
        miids.M.emit('load', null, miid, function (err, config) {

            if (err) {
                return;
            }

            // load dependend modules
            if (config.modules) {
                for (var i = 0; i < config.modules.length; ++i) {
                    M(config.modules[i], miid);
                }
            }

            // add parent miid to waitFor config
            if (parentMiid) {
                config.waitFor = config.waitFor || [];
                config.waitFor.push(parentMiid);
            }

            // create module instance
            miids[miid] = clone(Mono);
            miids[miid].mono = {
                config: config,
                miid: miid,
                name: config.name
            };

            // load scripts and init module
            if (config.scripts && config.scripts.length > 0) {
                loadJS(miid, config.name, config.scripts, function (miid, moduleConstructor) {

                    // listen to send eventsip
                    if (config.events) {
                        for (var i = 0, l = config.events.length; i < l; ++i) {
                            miids[miid].on(config.events[i], send(config.events[i]));
                        }
                    }

                    initModule(miid, moduleConstructor);
                });

            } else {
                initModule(miid);
            }
        });
    }

    // add listeneres
    function listen (event, handler, once) {
        var events = this.mono.events = this.mono.events || {};

        if (typeof handler === fn) {
            // fire ready event immediately if miid is ready
            if (event === 'ready' && this.mono.ready) {
                return handler.call(this);
            }

            if (!events[event]) {
                events[event] = [];
            }

            handler.one = once;
            events[event].push(handler);
        }
    }

    // remove listeners
    function remove (events, rmObject) {
        if (rmObject.length) {
            for (i = 0; i < rmObject.length; ++i) {
                if (events[rmObject[i][0]]) {
                    events[rmObject[i][0]].splice(rmObject[i][0], 1);
                }
            }
        }
    }

    // load scripts
    function initModule (miid, init) {

        miid = miids[miid].mono;

        // call module init
        if (typeof init === fn) {

            // handle waitFor
            if (miid.config.waitFor instanceof Array) {

                var count = miid.config.waitFor.length;
                var current = 0;
                var handler = function () {
                    if (++current === count) {
                        init.call(miids[miid.miid]);
                    }
                };

                for (var i = 0; i < count; ++i) {
                    miids[miid.config.waitFor[i]].one('ready', handler);
                }
            } else {
                init.call(miids[miid.miid]);
            }
        }
    }

    // commonjs require
    function require (module) {
        return function (name) {
            if (name.indexOf('../') === 0) {

                var namePath = name.split('../');
                var stepBackLenght = namePath.length - 1;
                namePath = namePath.pop();

                name = module.base + (module.path.length === stepBackLenght ? namePath : module.path.slice(0, stepBackLenght).join('/') + '/' + namePath);

            } else if (name.indexOf('./') === 0) {
                var path = module.path.join('/');
                name = module.base + (path ? path + '/' : '') + name.substr(2);
            } else {

                if (name.split('/').length < 5 && name[name.length - 1] !== '/') {
                    name += '/';
                }

                for (var script in modules) {
                    if (script.indexOf(name) === 0) {
                        name = script;
                        break;
                    }
                }
            }

            name += name.slice(-3) !== '.js' ? '.js' : '';
            if (modules[name]) {
                return modules[name].exports;
            }
        };
    }

    // create CommonJS modules in order of the dependencies
    function createCommonJsModulesInOrder (miid, moduleSources, callback) {

        // init modules in order (desc)
        for (var i = (moduleSources.length - 1), l = 0; i >= l; --i) {

            // evaluate module script
            if (typeof modules[moduleSources[i]] === fn && !modules[moduleSources[i]]._eval) {

                    var module = {
                        id: moduleSources[i],
                        exports: {}
                    };

                    module.path = module.id.split('/');
                    module.file = module.path.pop();

                    if (module.id.indexOf('//') === 0) {

                        module.base = '//';
                        module.path = module.path.slice(2);

                    } else if (module.id[0] === '/') {

                        module.base = '/';
                        module.path = module.path.slice(1);

                    } else if (module.id.indexOf('://') > 0) {

                        module.base = module.path.slice(0,3).join('/') + '/';
                        module.path = module.path.slice(3);

                    } else {

                        module.base = module.path.slice(0,4).join('/') + '/';
                        module.path = module.path.slice(4);
                    }

                    // execute CommonJS module
                    modules[moduleSources[i]] = modules[moduleSources[i]].call(module.exports, require(module), module, module.exports);
                    modules[moduleSources[i]]._eval = true;
            }
        }

        // return first module of dependency list
        callback(miid, modules[moduleSources[0]] ? modules[moduleSources[0]].exports : null);
    }

    // random string generator
    function uid (len) {
        for (var i = 0, l = len || 23, random = ''; i < l; ++i) {
            random += '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'[0 | Math.random() * 62];
        }
        return random;
    }

    function extDepLoaded (src) {
        return function () {
            miids.M.emit(src);
        };
    }

    // load scripts (script tag)
    function loadJS (miid, moduleName, moduleSources, callback) {

        var length = moduleSources.length;
        var modDepLoaded = function () {
            if (--length === 0) {
                createCommonJsModulesInOrder(miid, moduleSources, callback);
            }
        };

        for (var i = length - 1, source, url; i >= 0; --i) {

            source = moduleSources[i];

            // ingore loading for unified code
            if (source[0] === '#') {
                // remove the control sign
                moduleSources[i] = source.indexOf('./') === 1 ? source.substr(3) : source.substr(1);
                --length;
                continue;
            }

            // load module files
            if (source.indexOf('./') === 0) {
                moduleSources[i] = source = moduleName + source.substr(2);
            }

            // when script is loaded check if it's evaluated
            miids.M.one(source, modDepLoaded);

            // emit source event for already loaded scripts
            if (modules[source] && modules[source] !== 1) {
                miids.M.emit(source);

            // load module scripts
            } else if (!modules[source]) {
                modules[source] = 1;
                var node = document.createElement('script');

                url = '/@/M/mod/' + source;

                // handle external scripts onload event
                if (source[0] === '/' || source.indexOf('://') > 0) {
                    node.onload = extDepLoaded(source);
                    url = source;
                }

                node.src = url;
                head.appendChild(node);
            }
        }
    }

    // create websocket message: ['miid:event:cbId','err','data']
    function send (event) {
        return function (err, data, callback) {

            var message = [this.mono.miid + ':' + event, err || 0];
            var cbId;

            if (data) {
                message[2] = data;
            }

            if (callback) {
                cbId = uid(5);
                message[0] += ':' + cbId;
            }

            try {
                message = JSON.stringify(message);
            } catch (parseError) {
                if (callback) {
                    callback(parseError);
                }
                return;
            }

            if (callback) {
                wsCache[cbId] = callback;
            }

            webSocket.send(message);
        };
    }

    // parse websocket messages: ['miid:event:cbId','err','data']
    webSocket.onmessage = function (message) {

        try {
            message = JSON.parse(message.data);
        } catch (error) {
            return;
        }

        message[0] = message[0].split(':');

        var miid = message[0][0];
        var event = message[0][1];
        var cbId = message[0][2];
        var err = message[1];
        var data = message[2];

        if (miid && event && miids[miid]) {

            // call callback
            if (wsCache[cbId]) {
                wsCache[cbId].call(miids[miid], err, data);
                delete wsCache[cbId];
            }

            // emit event
            miids[miid].emit(event, err, data);
        }
    };

    // create and init core module instance
    miids.M = clone(Mono);
    miids.M.mono = {miid: 'M', ready: true};
    miids.M.on('load', send('load'));

    // wrapping function
    load.wrap = function (name, module) {
        modules[name] = module;
        miids.M.emit(name);
    };

    // export some handy functions
    load.clone = clone;

    // Object.paht('Object.key.key.value'[, {'key': {'key': {'value': 123}}});
    load.path = function (path, scope) {

        path = path.split('.');
        scope = scope || win;

        // find keys in paths or return null
        for (var i = 0; i < path.length; ++i) {
            if (!(scope = scope[path[i]])) {
                return;
            }
        }

        return scope;
    };

    return load;
})({}, {}, {}, 'function', window);
<commit_msg>append empty events object while creating module instance<commit_after>// global module loader
var M = (function(miids, modules, wsCache, fn, win) {

    // check and initialize websockets
    if (!win.WebSocket || !win.history) {
        return alert('You have an old browser :(\n\nPlease update to the latest version\nand this site will work.');
    }

    var webSocket = new WebSocket('ws://' + win.location.host + '/');
    var head = document.getElementsByTagName('head')[0];

    // module class
    var Mono = {
        // remove the module instance cache
        die: function () {
            delete miids[this.miid];
        },
        // listen to events
        on: listen,
        one: function (event, handler) {
            listen.call(this, event, handler, true);
        },
        // remove listeners
        off: function (event, handler) {
            var events = this.mono.events;

            if (events[event]) {

                if (handler) {
                    var rm = [];

                    for (var i = 0; i < events[event].length; ++i) {
                        if (events[event][i] === handler) {
                            events[event][i] = null;
                            rm.push([event, i]);
                        }
                    }

                    remove(events, rm);

                } else {
                    delete events[event];
                }
            }
        },
        // emit event
        emit: function(event) {
            var events = this.mono.events;

            // mark miid as ready
            if (event === 'ready') {
                this.mono.ready = true;
            }

            if (events[event]) {

                // slice first argument
                var args = convertToArray(arguments).slice(1);
                var rm = [];

                for (var i = 0; i < events[event].length; ++i) {
                    if (events[event][i]) {

                        // call registered Methods
                        events[event][i].apply(this, args);

                        // remove from event buffer, if once is true
                        if (events[event][i].one) {
                            events[event][i] = null;
                            rm.push([event, i]);
                        }
                    }
                }

                remove(events, rm);
            }
        },
        // emit an event on a miid
        push: function (miid, event) {
            if (miids[miid]) {
                miids[miid].emit.apply(miids[miid], convertToArray(arguments).slice(1));
            }
        },
        // emit on all miids
        pushAll: function (event) {
            var args = convertToArray(arguments);
            for (var miid in miids) {
                miids[miid].emit.apply(miids[miid], args);
            }
        }
    };

    function convertToArray (object) {
        return Array.prototype.slice.call(object);
    }

    // clone object
    function clone (object) {
        var O = (function() {});
        O.prototype = object || {};
        return new O();
    }

    // load miid
    function load (miid, parentMiid) {

        // don't load the same miid more than once
        if (!miid || miids[miid]) {
            return;
        }

        // wait for websocket
        if (webSocket.readyState !== webSocket.OPEN) {
            return webSocket.onopen = function () {
                load(miid, parentMiid);
            };
        }

        // get miid config
        miids.M.emit('load', null, miid, function (err, config) {

            if (err) {
                return;
            }

            // load dependend modules
            if (config.modules) {
                for (var i = 0; i < config.modules.length; ++i) {
                    M(config.modules[i], miid);
                }
            }

            // add parent miid to waitFor config
            if (parentMiid) {
                config.waitFor = config.waitFor || [];
                config.waitFor.push(parentMiid);
            }

            // create module instance
            miids[miid] = clone(Mono);
            miids[miid].mono = {
                config: config,
                miid: miid,
                name: config.name,
                events: {}
            };

            // load scripts and init module
            if (config.scripts && config.scripts.length > 0) {
                loadJS(miid, config.name, config.scripts, function (miid, moduleConstructor) {

                    // listen to send eventsip
                    if (config.events) {
                        for (var i = 0, l = config.events.length; i < l; ++i) {
                            miids[miid].on(config.events[i], send(config.events[i]));
                        }
                    }

                    initModule(miid, moduleConstructor);
                });

            } else {
                initModule(miid);
            }
        });
    }

    // add listeneres
    function listen (event, handler, once) {
        var events = this.mono.events;

        if (typeof handler === fn) {
            // fire ready event immediately if miid is ready
            if (event === 'ready' && this.mono.ready) {
                return handler.call(this);
            }

            if (!events[event]) {
                events[event] = [];
            }

            handler.one = once;
            events[event].push(handler);
        }
    }

    // remove listeners
    function remove (events, rmObject) {
        if (rmObject.length) {
            for (i = 0; i < rmObject.length; ++i) {
                if (events[rmObject[i][0]]) {
                    events[rmObject[i][0]].splice(rmObject[i][0], 1);
                }
            }
        }
    }

    // load scripts
    function initModule (miid, init) {

        miid = miids[miid].mono;

        // call module init
        if (typeof init === fn) {

            // handle waitFor
            if (miid.config.waitFor instanceof Array) {

                var count = miid.config.waitFor.length;
                var current = 0;
                var handler = function () {
                    if (++current === count) {
                        init.call(miids[miid.miid]);
                    }
                };

                for (var i = 0; i < count; ++i) {
                    miids[miid.config.waitFor[i]].one('ready', handler);
                }
            } else {
                init.call(miids[miid.miid]);
            }
        }
    }

    // commonjs require
    function require (module) {
        return function (name) {
            if (name.indexOf('../') === 0) {

                var namePath = name.split('../');
                var stepBackLenght = namePath.length - 1;
                namePath = namePath.pop();

                name = module.base + (module.path.length === stepBackLenght ? namePath : module.path.slice(0, stepBackLenght).join('/') + '/' + namePath);

            } else if (name.indexOf('./') === 0) {
                var path = module.path.join('/');
                name = module.base + (path ? path + '/' : '') + name.substr(2);
            } else {

                if (name.split('/').length < 5 && name[name.length - 1] !== '/') {
                    name += '/';
                }

                for (var script in modules) {
                    if (script.indexOf(name) === 0) {
                        name = script;
                        break;
                    }
                }
            }

            name += name.slice(-3) !== '.js' ? '.js' : '';
            if (modules[name]) {
                return modules[name].exports;
            }
        };
    }

    // create CommonJS modules in order of the dependencies
    function createCommonJsModulesInOrder (miid, moduleSources, callback) {

        // init modules in order (desc)
        for (var i = (moduleSources.length - 1), l = 0; i >= l; --i) {

            // evaluate module script
            if (typeof modules[moduleSources[i]] === fn && !modules[moduleSources[i]]._eval) {

                    var module = {
                        id: moduleSources[i],
                        exports: {}
                    };

                    module.path = module.id.split('/');
                    module.file = module.path.pop();

                    if (module.id.indexOf('//') === 0) {

                        module.base = '//';
                        module.path = module.path.slice(2);

                    } else if (module.id[0] === '/') {

                        module.base = '/';
                        module.path = module.path.slice(1);

                    } else if (module.id.indexOf('://') > 0) {

                        module.base = module.path.slice(0,3).join('/') + '/';
                        module.path = module.path.slice(3);

                    } else {

                        module.base = module.path.slice(0,4).join('/') + '/';
                        module.path = module.path.slice(4);
                    }

                    // execute CommonJS module
                    modules[moduleSources[i]] = modules[moduleSources[i]].call(module.exports, require(module), module, module.exports);
                    modules[moduleSources[i]]._eval = true;
            }
        }

        // return first module of dependency list
        callback(miid, modules[moduleSources[0]] ? modules[moduleSources[0]].exports : null);
    }

    // random string generator
    function uid (len) {
        for (var i = 0, l = len || 23, random = ''; i < l; ++i) {
            random += '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'[0 | Math.random() * 62];
        }
        return random;
    }

    function extDepLoaded (src) {
        return function () {
            miids.M.emit(src);
        };
    }

    // load scripts (script tag)
    function loadJS (miid, moduleName, moduleSources, callback) {

        var length = moduleSources.length;
        var modDepLoaded = function () {
            if (--length === 0) {
                createCommonJsModulesInOrder(miid, moduleSources, callback);
            }
        };

        for (var i = length - 1, source, url; i >= 0; --i) {

            source = moduleSources[i];

            // ingore loading for unified code
            if (source[0] === '#') {
                // remove the control sign
                moduleSources[i] = source.indexOf('./') === 1 ? source.substr(3) : source.substr(1);
                --length;
                continue;
            }

            // load module files
            if (source.indexOf('./') === 0) {
                moduleSources[i] = source = moduleName + source.substr(2);
            }

            // when script is loaded check if it's evaluated
            miids.M.one(source, modDepLoaded);

            // emit source event for already loaded scripts
            if (modules[source] && modules[source] !== 1) {
                miids.M.emit(source);

            // load module scripts
            } else if (!modules[source]) {
                modules[source] = 1;
                var node = document.createElement('script');

                url = '/@/M/mod/' + source;

                // handle external scripts onload event
                if (source[0] === '/' || source.indexOf('://') > 0) {
                    node.onload = extDepLoaded(source);
                    url = source;
                }

                node.src = url;
                head.appendChild(node);
            }
        }
    }

    // create websocket message: ['miid:event:cbId','err','data']
    function send (event) {
        return function (err, data, callback) {

            var message = [this.mono.miid + ':' + event, err || 0];
            var cbId;

            if (data) {
                message[2] = data;
            }

            if (callback) {
                cbId = uid(5);
                message[0] += ':' + cbId;
            }

            try {
                message = JSON.stringify(message);
            } catch (parseError) {
                if (callback) {
                    callback(parseError);
                }
                return;
            }

            if (callback) {
                wsCache[cbId] = callback;
            }

            webSocket.send(message);
        };
    }

    // parse websocket messages: ['miid:event:cbId','err','data']
    webSocket.onmessage = function (message) {

        try {
            message = JSON.parse(message.data);
        } catch (error) {
            return;
        }

        message[0] = message[0].split(':');

        var miid = message[0][0];
        var event = message[0][1];
        var cbId = message[0][2];
        var err = message[1];
        var data = message[2];

        if (miid && event && miids[miid]) {

            // call callback
            if (wsCache[cbId]) {
                wsCache[cbId].call(miids[miid], err, data);
                delete wsCache[cbId];
            }

            // emit event
            miids[miid].emit(event, err, data);
        }
    };

    // create and init core module instance
    miids.M = clone(Mono);
    miids.M.mono = {miid: 'M', ready: true, events: {}};
    miids.M.on('load', send('load'));

    // wrapping function
    load.wrap = function (name, module) {
        modules[name] = module;
        miids.M.emit(name);
    };

    // export some handy functions
    load.clone = clone;

    // Object.paht('Object.key.key.value'[, {'key': {'key': {'value': 123}}});
    load.path = function (path, scope) {

        path = path.split('.');
        scope = scope || win;

        // find keys in paths or return null
        for (var i = 0; i < path.length; ++i) {
            if (!(scope = scope[path[i]])) {
                return;
            }
        }

        return scope;
    };

    return load;
})({}, {}, {}, 'function', window);
<|endoftext|>