<commit_before>/*
 * index.js: Top-level include for the drone module.
 *
 * (C) 2010, Nodejitsu Inc.
 *
 */

var fs = require('fs'),
    path = require('path'),
    http = require('http'),
    flatiron = require('flatiron'),
    hookio = require('hook.io'),
    haibu = require('../../haibu'),
    async = haibu.common.async;

//
// ### Include Exports
// Export other components in the module
//
exports.Drone        = require('./drone').Drone;
exports.Client       = require('./client').Client;
exports.started      = false;

//
// ### function startHook (options, callback)
// #### @options {Object} Options for the `hookio.Hook` instance.
// #### @callback {function} Continuation to respond to when complete.
// Starts a new `hookio.Hook` for this `haibu` process.
//
exports.startHook = function (options, callback) {
  if (!callback && typeof options === 'function') {
    callback = options;
    options = null;
  }

  options = options || { name: 'haibu' };

  var hook = new hookio.Hook(options);
  hook.listen(options, function (err) {
    haibu.running.hook = hook;
    return err ? callback(err) : callback(null, hook);
  });
};

//
// ### function autostart (server, callback)
// #### @server {http.Server} Haibu drone server to autostart drones with.
// #### @callback {function} Continuation to respond to when complete
// Autostarts drones for all applications persisted to
// `haibu.config.get('directories:autostart')`.
//
exports.autostart = function (server, callback) {
  var autostartDir = haibu.config.get('directories:autostart');

  //
  // Helper function which starts multiple drones
  // a given application.
  //
  function startDrones (pkg, done) {
    if (pkg.drones == 0) {
      return done();
    }

    var started = 0;

    async.whilst(function () {
      return started < pkg.drones;
    }, function (next) {
      started++;
      server.drone.start(pkg, next);
    }, done);
  }

  //
  // Find all drones in directory:
  //   %dir/%sanitized_name.json
  //
  fs.readdir(autostartDir, function (err, files) {
    if (err) {
      return callback(err);
    }

    async.map(files, function (file, next) {
      //
      // Read each `package.json` manifest file and start
      // the appropriate drones in this `haibu` instance.
      //
      fs.readFile(path.join(autostartDir, file), function (err, pkg) {
        if (err) {
          return callback(err);
        }

        //
        // Read the contents of the package.json manifest,
        // which should be JSON
        //
        try {
          pkg = JSON.parse(pkg.toString());
        }
        catch (ex) {
          return callback(ex);
        }

        startDrones(pkg, next);
      });
    }, callback);
  });
}

//
// ### function start (options, callback)
// #### @options {Object} Options to use when starting this module.
// #### @callback {function} Continuation to respond to when complete.
// Starts the haibu `drone` webservice with the specified options.
//
exports.start = function (options, callback) {
  if (exports.started) {
    return callback(null, haibu.running.server);
  }

  function tryAutostart (server) {
    exports.autostart(server, function (err) {
      //
      // Ignore errors from autostart and continue
      // bringing up the haibu `drone` server.
      //
      // Remark: We should report the `err` somewhere
      //
      haibu.emit('start');
      callback(null, server);
    });
  }

  function startServer (err, hook) {
    if (err) {
      return callback(err);
    }

    //
    // Create the server and add the new `http.Server`
    // and `haibu.drone.Drone` instance into the `haibu.running`
    // namespace.
    //
    var drone = new haibu.drone.Drone(options);

    //
    // Setup the `union` server through `flatiron.plugins.http`
    // and then add routes.
    //
    haibu.use(flatiron.plugins.http, options.http || {});
    require('./service').createRouter(drone);

    if (options.port) {
      haibu.listen(options.port);
    }

    haibu.running.server = haibu.server;
    haibu.running.hook   = hook;
    haibu.running.drone  = haibu.server.drone = drone;
    haibu.running.ports  = {};

    //
    // Attempt to autostart any applications and respond.
    //
    tryAutostart(haibu.server);
  }

  function startHook (err) {
    return err
      ? callback(err)
      : exports.startHook(options, startServer)
  }

  //
  // Indicate that `haibu.drone` has started
  //
  exports.started = true;

  return haibu.initialized
    ? startHook()
    : haibu.init(options, startHook);
};

//
// ### function stop (callback)
// #### @cleanup {bool} (optional) Remove all autostart files (default=true).
// #### @callback {function} Continuation to respond to when complete.
// Gracefully stops `drone` instance
//
exports.stop = function (cleanup, callback) {
  if (!callback && typeof cleanup === 'function') {
    callback = cleanup;
    cleanup = true;
  }

  if (!exports.started) {
    return callback ? callback() : null;
  }

  exports.started = false;

  haibu.running.server.close();
  haibu.running.hook.server.close();

  // Terminate drones
  haibu.running.drone.destroy(cleanup, callback || function () {});
  haibu.running = {};
};<commit_msg>[minor] Add warning for supressed exception on EPIPE. This error is thrown in node@0.4.12 when attempting to pipe a bad tarball to `haibu.config.get("tar")`<commit_after>/*
 * index.js: Top-level include for the drone module.
 *
 * (C) 2010, Nodejitsu Inc.
 *
 */

var fs = require('fs'),
    path = require('path'),
    http = require('http'),
    flatiron = require('flatiron'),
    hookio = require('hook.io'),
    haibu = require('../../haibu'),
    async = haibu.common.async;

//
// ### Include Exports
// Export other components in the module
//
exports.Drone        = require('./drone').Drone;
exports.Client       = require('./client').Client;
exports.started      = false;

//
// ### function startHook (options, callback)
// #### @options {Object} Options for the `hookio.Hook` instance.
// #### @callback {function} Continuation to respond to when complete.
// Starts a new `hookio.Hook` for this `haibu` process.
//
exports.startHook = function (options, callback) {
  if (!callback && typeof options === 'function') {
    callback = options;
    options = null;
  }

  options = options || { name: 'haibu' };

  var hook = new hookio.Hook(options);
  hook.listen(options, function (err) {
    haibu.running.hook = hook;
    return err ? callback(err) : callback(null, hook);
  });
};

//
// ### function autostart (server, callback)
// #### @server {http.Server} Haibu drone server to autostart drones with.
// #### @callback {function} Continuation to respond to when complete
// Autostarts drones for all applications persisted to
// `haibu.config.get('directories:autostart')`.
//
exports.autostart = function (server, callback) {
  var autostartDir = haibu.config.get('directories:autostart');

  //
  // Helper function which starts multiple drones
  // a given application.
  //
  function startDrones (pkg, done) {
    if (pkg.drones == 0) {
      return done();
    }

    var started = 0;

    async.whilst(function () {
      return started < pkg.drones;
    }, function (next) {
      started++;
      server.drone.start(pkg, next);
    }, done);
  }

  //
  // Find all drones in directory:
  //   %dir/%sanitized_name.json
  //
  fs.readdir(autostartDir, function (err, files) {
    if (err) {
      return callback(err);
    }

    async.map(files, function (file, next) {
      //
      // Read each `package.json` manifest file and start
      // the appropriate drones in this `haibu` instance.
      //
      fs.readFile(path.join(autostartDir, file), function (err, pkg) {
        if (err) {
          return callback(err);
        }

        //
        // Read the contents of the package.json manifest,
        // which should be JSON
        //
        try {
          pkg = JSON.parse(pkg.toString());
        }
        catch (ex) {
          return callback(ex);
        }

        startDrones(pkg, next);
      });
    }, callback);
  });
}

//
// ### function start (options, callback)
// #### @options {Object} Options to use when starting this module.
// #### @callback {function} Continuation to respond to when complete.
// Starts the haibu `drone` webservice with the specified options.
//
exports.start = function (options, callback) {
  if (exports.started) {
    return callback(null, haibu.running.server);
  }

  function tryAutostart (server) {
    exports.autostart(server, function (err) {
      //
      // Ignore errors from autostart and continue
      // bringing up the haibu `drone` server.
      //
      // Remark: We should report the `err` somewhere
      //
      haibu.emit('start');
      callback(null, server);
    });
  }

  function startServer (err, hook) {
    if (err) {
      return callback(err);
    }

    //
    // Create the server and add the new `http.Server`
    // and `haibu.drone.Drone` instance into the `haibu.running`
    // namespace.
    //
    var drone = new haibu.drone.Drone(options);

    //
    // Setup the `union` server through `flatiron.plugins.http`
    // and then add routes.
    //
    haibu.use(flatiron.plugins.http, options.http || {});
    require('./service').createRouter(drone);

    if (options.port) {
      haibu.listen(options.port);
    }

    haibu.running.server = haibu.server;
    haibu.running.hook   = hook;
    haibu.running.drone  = haibu.server.drone = drone;
    haibu.running.ports  = {};

    //
    // Attempt to autostart any applications and respond.
    //
    tryAutostart(haibu.server);
  }

  function startHook (err) {
    //
    // There is a current bug in node that throws here:
    //
    // https://github.com/joyent/node/blob/v0.4.12/lib/net.js#L159
    //
    // It will throw a broken pipe error (EPIPE) when a child process that you
    // are piping to unexpectedly exits. The write function on line 159 is
    // defined here:
    //
    // https://github.com/joyent/node/blob/v0.4.12/lib/net.js#L62
    //
    // This uncaughtExceptionHandler will catch that error,
    // and since it originated with in another sync context,
    // this section will still respond to the request.
    //
    haibu.exceptions.logger.exitOnError = function (err) {
      if (err.code === 'EPIPE') {
        console.log('expected error:');
        console.log('EPIPE -- probabaly caused by someone pushing a non gzip file.');
        console.log('"net" throws on a broken pipe, current node bug, not haibu.');
        return false;
      }

      return true;
    }

    return err
      ? callback(err)
      : exports.startHook(options, startServer)
  }

  //
  // Indicate that `haibu.drone` has started
  //
  exports.started = true;

  return haibu.initialized
    ? startHook()
    : haibu.init(options, startHook);
};

//
// ### function stop (callback)
// #### @cleanup {bool} (optional) Remove all autostart files (default=true).
// #### @callback {function} Continuation to respond to when complete.
// Gracefully stops `drone` instance
//
exports.stop = function (cleanup, callback) {
  if (!callback && typeof cleanup === 'function') {
    callback = cleanup;
    cleanup = true;
  }

  if (!exports.started) {
    return callback ? callback() : null;
  }

  exports.started = false;

  haibu.running.server.close();
  haibu.running.hook.server.close();

  // Terminate drones
  haibu.running.drone.destroy(cleanup, callback || function () {});
  haibu.running = {};
};<|endoftext|>


<commit_before>/**
 * Copyright (C) 2022 Orbeon, Inc.
 *
 * This program is free software; you can redistribute it and/or modify it under the terms of the
 * GNU Lesser General Public License as published by the Free Software Foundation; either version
 * 2.1 of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Lesser General Public License for more details.
 *
 * The full text of the license is available at http://www.gnu.org/copyleft/lesser.html
 */

ORBEON.xforms.XBL.declareCompanion("acme|map", {

    _googleMap   : null,
    _geoCoder    : null,
    _marker      : null,
    _address     : null,
    _initPromise : null,
    _marker      : null,

    init: function() {

        // If needed, dynamically load the JavaScript for the Google Maps API
        const scripts              = Array.from(document.scripts);
        const googleMapsJs         = "https://maps.googleapis.com/maps/api/js";
        const needToLoadJs         = scripts.find((script) => script.src.startsWith(googleMapsJs)) === undefined;
        if (needToLoadJs) {
            const mapElement       = this.container.querySelector('.acme-map-google');
            const googleMapsKey    = mapElement.getAttribute("data-google-maps-key");
            const googleMapsScript = document.createElement("script");
            googleMapsScript.src   = googleMapsJs + "?key=" + googleMapsKey;
            googleMapsScript.async = true;
            document.head.appendChild(googleMapsScript);
        }

        const jsLoadedPromise = new Promise((resolutionFunc) => {
            (function worker() {
                const shortDelay      = ORBEON.util.Properties.internalShortDelay.get();
                const googleMapsFound =
                    "google" in window &&
                    "maps"   in google &&
                    "Map"    in google.maps;
                if   (googleMapsFound) resolutionFunc();
                else setTimeout(worker, shortDelay);
            })();
        });

        var initPromiseResolutionFunc = null;
        this._initPromise = new Promise((resolutionFunc) => {
            initPromiseResolutionFunc = resolutionFunc;
        });

        jsLoadedPromise.then(() => {
            var mapElement = this.container.querySelector('.acme-map-google');
            var mapOptions = {
                center: { lat: 0, lng: 0},
                zoom: 1,
                scaleControl: true,
                zoomControl: true
            };
            this._googleMap = new google.maps.Map(mapElement, mapOptions);
            this._geoCoder  = new google.maps.Geocoder();
            initPromiseResolutionFunc();
        });
    },

    xformsUpdateValue: function(address) {
        var companion = this;
        this._initPromise.then(() => {
            companion._address = address;
            companion._geoCoder.geocode(
                { 'address': address },
                (results, status) => {
                    if (status == google.maps.GeocoderStatus.OK) {
                        var latLng = results[0].geometry.location;
                        companion._updateMarkerFromLatLng(latLng);
                    }
                }
            );
        });
    },

    xformsGetValue: function() {
        return this._address;
    },

    _updateMarkerFromLatLng: function(latLng) {

        // Create marker if needed
        if (this._marker == null) {
            this._marker = new google.maps.Marker({
                map       : this._googleMap
            });
        }

        // Center map and set the position of the marker
        this._googleMap.setCenter(latLng);
        this._marker.setPosition(latLng);

        // If we are still showing the whole earth, now is the time to zoom in
        if (this._googleMap.getZoom() == 1)
            this._googleMap.setZoom(14);
    }
});
<commit_msg>`xformsUpdateValue` to return promise<commit_after>/**
 * Copyright (C) 2022 Orbeon, Inc.
 *
 * This program is free software; you can redistribute it and/or modify it under the terms of the
 * GNU Lesser General Public License as published by the Free Software Foundation; either version
 * 2.1 of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Lesser General Public License for more details.
 *
 * The full text of the license is available at http://www.gnu.org/copyleft/lesser.html
 */

ORBEON.xforms.XBL.declareCompanion("acme|map", {

    _googleMap   : null,
    _geoCoder    : null,
    _marker      : null,
    _address     : null,
    _initPromise : null,
    _marker      : null,

    init: function() {

        // If needed, dynamically load the JavaScript for the Google Maps API
        const scripts              = Array.from(document.scripts);
        const googleMapsJs         = "https://maps.googleapis.com/maps/api/js";
        const needToLoadJs         = scripts.find((script) => script.src.startsWith(googleMapsJs)) === undefined;
        if (needToLoadJs) {
            const mapElement       = this.container.querySelector('.acme-map-google');
            const googleMapsKey    = mapElement.getAttribute("data-google-maps-key");
            const googleMapsScript = document.createElement("script");
            googleMapsScript.src   = googleMapsJs + "?key=" + googleMapsKey;
            googleMapsScript.async = true;
            document.head.appendChild(googleMapsScript);
        }

        const jsLoadedPromise = new Promise((resolutionFunc) => {
            (function worker() {
                const shortDelay      = ORBEON.util.Properties.internalShortDelay.get();
                const googleMapsFound =
                    "google" in window &&
                    "maps"   in google &&
                    "Map"    in google.maps;
                if   (googleMapsFound) resolutionFunc();
                else setTimeout(worker, shortDelay);
            })();
        });

        var initPromiseResolutionFunc = null;
        this._initPromise = new Promise((resolutionFunc) => {
            initPromiseResolutionFunc = resolutionFunc;
        });

        jsLoadedPromise.then(() => {
            var mapElement = this.container.querySelector('.acme-map-google');
            var mapOptions = {
                center: { lat: 0, lng: 0},
                zoom: 1,
                scaleControl: true,
                zoomControl: true
            };
            this._googleMap = new google.maps.Map(mapElement, mapOptions);
            this._geoCoder  = new google.maps.Geocoder();
            initPromiseResolutionFunc();
        });
    },

    xformsUpdateValue: function(address) {
        var companion = this;
        return this._initPromise.then(() => {
            companion._address = address;
            companion._geoCoder.geocode(
                { 'address': address },
                (results, status) => {
                    if (status == google.maps.GeocoderStatus.OK) {
                        var latLng = results[0].geometry.location;
                        companion._updateMarkerFromLatLng(latLng);
                    }
                }
            );
        });
    },

    xformsGetValue: function() {
        return this._address;
    },

    _updateMarkerFromLatLng: function(latLng) {

        // Create marker if needed
        if (this._marker == null) {
            this._marker = new google.maps.Marker({
                map       : this._googleMap
            });
        }

        // Center map and set the position of the marker
        this._googleMap.setCenter(latLng);
        this._marker.setPosition(latLng);

        // If we are still showing the whole earth, now is the time to zoom in
        if (this._googleMap.getZoom() == 1)
            this._googleMap.setZoom(14);
    }
});
<|endoftext|>


<commit_before>'use babel';

import fs from 'fs';
import path from 'path';

let adoc = {}

export function setAdoc(x) {
  adoc = x;
}

function formatAutoComplete(key, v, replacementPrefix) {
  let o = {
    text: key,
    replacementPrefix: replacementPrefix,
    type: "value",
    description: v.description
  }

  if (v.hasOwnProperty('methods')) {
    o.snippet = `${key}`;
    o.type = "class";
  }
  else {
    const params = Object.keys(v.params).map((x,i) => `\${${i+1}:${x}}`).join(", ");
    o.snippet = `${key}(${params})`;
    o.type = "function";
    o.leftLabel = v.ret.type;
  }

  return o;
}

function getPrefix(te, pos, f) {
  var row = pos.row;
  var start = pos.column;
  var end = pos.column;
  var line = te.lineTextForBufferRow(row);
  while (start > 0) {
    var s = line.substr(start-1, end-start+1)
    if (!f(s))
      break;
    start--;
  }
  return line.substr(start, end-start);
}

function depthTwoMatches(root, dir, base) {
  return [path.join(root, dir)]
    .filter(f => fs.existsSync(f) && fs.statSync(f).isDirectory())
    .map(f => fs.readdirSync(f))
    .reduce((a,b) => a.concat(b), [])
    .filter(x => x.startsWith(base))
    .map(x => path.join(dir,x))
    .map(x =>
      fs.statSync(path.join(root,x)).isDirectory()
        ? fs.readdirSync(path.join(root,x))
          .map(y => path.join(x,y))
        : [x]
      )
    .reduce((a,b) => a.concat(b), [])
    .map(x => fs.statSync(path.join(root,x)).isDirectory() ? x + "/" : x);
}

function autocompletePath(prefix) {
  let dir = path.dirname(prefix);
  let base = path.basename(prefix);
  const dirLen = dir == "." ? 0 : dir.length+1;
  const paths = atom.project.rootDirectories
    .map(rd => rd.path)
    .map(root => depthTwoMatches(root, dir, base))
    .reduce((a,b) => a.concat(b), [])
    .map(x => x.replace(/\\/g, '/'))
    .map(x => {return {
      text: x.substr(dirLen).match(/[^.]*[^/.]/)[0],
      displayText: x.substr(dirLen).match(/[^.]*/)[0],
      replacementPrefix: prefix.substr(dirLen),
      rightLabel: path.extname(x)
    }});
  return paths;
}

export var adocProvider = {
  selector: ".source.lua",

  inclusionPriority: 100,
  excludeLowerPriority: true,

  getSuggestions: ({editor, bufferPosition, scopeDescriptor, prefix, activatedManually}) => {
    const luaApiPrefix = getPrefix(editor, bufferPosition, s => s.match(/^[a-zA-Z._0-9]+$/))
    const pathPrefix = getPrefix(editor, bufferPosition, s => s.match(/^['"]?[a-zA-Z0-9_\-/]+$/))
    if (pathPrefix.length > luaApiPrefix.length && pathPrefix.match(/^['"]/)) {
      return autocompletePath(pathPrefix.substr(1));
    } else {
      const keys = luaApiPrefix.split('.');
      if (keys.length == 0) return [];
      if (keys.length == 2) {
        if (prefix == '.') {
          if (!adoc.scriptbinds[keys[0]]) return [];

          if (!adoc.scriptbinds.hasOwnProperty(keys[0])) return [];

          return Object.keys(adoc.scriptbinds[keys[0]].methods)
            .map(x => formatAutoComplete(x, adoc.scriptbinds[keys[0]].methods[x], null));
        }

        if (!adoc.scriptbinds.hasOwnProperty(keys[0])) return [];

        return Object.keys(adoc.scriptbinds[keys[0]].methods)
          .filter(x => x.startsWith(prefix))
          .map(x => formatAutoComplete(x, adoc.scriptbinds[keys[0]].methods[x], prefix));
      }

      if (adoc == null) {
        atom.notifications.addError(`No script binds documentation found`, {
          dismissable: true,
          description: `Go to Packages -> Settings View -> Manage Packages and search for hexe.
            Click on the Settings button and then change the path that is there to where the
            scriptbinds.json file is located on your computer.`
        });
      }

      if (!adoc.hasOwnProperty("scriptbinds") || prefix == '' && !activatedManually) return [];

      return Object.keys(adoc.scriptbinds)
        .filter(x => x.startsWith(prefix))
        .map(x => formatAutoComplete(x, adoc.scriptbinds[x], prefix));
    }
  }
};
<commit_msg>Forgot to add a return statement for the extra check. Issue #6 should now be closed.<commit_after>'use babel';

import fs from 'fs';
import path from 'path';

let adoc = {}

export function setAdoc(x) {
  adoc = x;
}

function formatAutoComplete(key, v, replacementPrefix) {
  let o = {
    text: key,
    replacementPrefix: replacementPrefix,
    type: "value",
    description: v.description
  }

  if (v.hasOwnProperty('methods')) {
    o.snippet = `${key}`;
    o.type = "class";
  }
  else {
    const params = Object.keys(v.params).map((x,i) => `\${${i+1}:${x}}`).join(", ");
    o.snippet = `${key}(${params})`;
    o.type = "function";
    o.leftLabel = v.ret.type;
  }

  return o;
}

function getPrefix(te, pos, f) {
  var row = pos.row;
  var start = pos.column;
  var end = pos.column;
  var line = te.lineTextForBufferRow(row);
  while (start > 0) {
    var s = line.substr(start-1, end-start+1)
    if (!f(s))
      break;
    start--;
  }
  return line.substr(start, end-start);
}

function depthTwoMatches(root, dir, base) {
  return [path.join(root, dir)]
    .filter(f => fs.existsSync(f) && fs.statSync(f).isDirectory())
    .map(f => fs.readdirSync(f))
    .reduce((a,b) => a.concat(b), [])
    .filter(x => x.startsWith(base))
    .map(x => path.join(dir,x))
    .map(x =>
      fs.statSync(path.join(root,x)).isDirectory()
        ? fs.readdirSync(path.join(root,x))
          .map(y => path.join(x,y))
        : [x]
      )
    .reduce((a,b) => a.concat(b), [])
    .map(x => fs.statSync(path.join(root,x)).isDirectory() ? x + "/" : x);
}

function autocompletePath(prefix) {
  let dir = path.dirname(prefix);
  let base = path.basename(prefix);
  const dirLen = dir == "." ? 0 : dir.length+1;
  const paths = atom.project.rootDirectories
    .map(rd => rd.path)
    .map(root => depthTwoMatches(root, dir, base))
    .reduce((a,b) => a.concat(b), [])
    .map(x => x.replace(/\\/g, '/'))
    .map(x => {return {
      text: x.substr(dirLen).match(/[^.]*[^/.]/)[0],
      displayText: x.substr(dirLen).match(/[^.]*/)[0],
      replacementPrefix: prefix.substr(dirLen),
      rightLabel: path.extname(x)
    }});
  return paths;
}

export var adocProvider = {
  selector: ".source.lua",

  inclusionPriority: 100,
  excludeLowerPriority: true,

  getSuggestions: ({editor, bufferPosition, scopeDescriptor, prefix, activatedManually}) => {
    const luaApiPrefix = getPrefix(editor, bufferPosition, s => s.match(/^[a-zA-Z._0-9]+$/))
    const pathPrefix = getPrefix(editor, bufferPosition, s => s.match(/^['"]?[a-zA-Z0-9_\-/]+$/))
    if (pathPrefix.length > luaApiPrefix.length && pathPrefix.match(/^['"]/)) {
      return autocompletePath(pathPrefix.substr(1));
    } else {
      const keys = luaApiPrefix.split('.');
      if (keys.length == 0) return [];
      if (keys.length == 2) {
        if (prefix == '.') {
          if (!adoc.scriptbinds[keys[0]]) return [];

          if (!adoc.scriptbinds.hasOwnProperty(keys[0])) return [];

          return Object.keys(adoc.scriptbinds[keys[0]].methods)
            .map(x => formatAutoComplete(x, adoc.scriptbinds[keys[0]].methods[x], null));
        }

        if (!adoc.scriptbinds.hasOwnProperty(keys[0])) return [];

        return Object.keys(adoc.scriptbinds[keys[0]].methods)
          .filter(x => x.startsWith(prefix))
          .map(x => formatAutoComplete(x, adoc.scriptbinds[keys[0]].methods[x], prefix));
      }

      if (adoc == null) {
        atom.notifications.addError(`No script binds documentation found`, {
          dismissable: true,
          description: `Go to Packages -> Settings View -> Manage Packages and search for hexe.
            Click on the Settings button and then change the path that is there to where the
            scriptbinds.json file is located on your computer.`
        });
        return [];
      }

      if (!adoc.hasOwnProperty("scriptbinds") || prefix == '' && !activatedManually) return [];

      return Object.keys(adoc.scriptbinds)
        .filter(x => x.startsWith(prefix))
        .map(x => formatAutoComplete(x, adoc.scriptbinds[x], prefix));
    }
  }
};
<|endoftext|>


<commit_before>function loadChartBuilder(pageData, onSave, chart) {
  var chart = chart;
  var pageUrl = localStorage.getItem('pageurl');
  var html = templates.chartBuilder(chart);
  var table = false;
  $('body').append(html);
  $('.chart-builder').css("display", "block");

  if (chart) {
    $('#chart-data').val(toTsv(chart));
  }

  refreshBarLineSection();
  renderChart();

  function refreshBarLineSection() {
    var data = editBarline(chart);
    var html = templates.chartEditBarlines(data);
    $('#barline').html(html);
  }

  function editBarline(chart) {
    var data = [];
    var series = chart.series;

    if (chart.type === 'barline') { // if we have a bar line we want to populate the entries for each series
      if (chart.types) { // if we have existing types use them
        var type = _.values(chart.types);
        for (var i = 0; i < chart.series.length; i += 1) {
          data.push({
            series: series[i], type: type[i],
            isChecked: (function () {
              var checked = _.indexOf(chart.groups[0], series[i]);
              if (checked < 0) {
                return checked = false;
              } else {
                return checked = true;
              }
            })()
          });
        }
      } else { // if we have no existing types, default them
        for (var i = 0; i < chart.series.length; i += 1) {
          data.push({series: series[i], type: '', isChecked: false});
        }
      }
    }
    return data;
  }

  $('#edit-chart').on('input change', ':input', function () {
    chart = buildChartObject();
    refreshBarLineSection();
    renderChart();
  });

  $('.btn-chart-builder-cancel').on('click', function () {
    $('.chart-builder').stop().fadeOut(200).remove();
  });

  $('.btn-chart-builder-create').on('click', function () {

    if (!pageData.charts) {
      pageData.charts = []
    } else {
      if (_.find(pageData.charts, function (existingChart) {
          return existingChart.filename === chart.filename
        })) {
        alert("A chart with this name already exists.");
        return;
      }
    }

    var uriUploadJSON = pageUrl + "/" + chart.filename + ".json";
    $.ajax({
      url: "/zebedee/content/" + Florence.collection.id + "?uri=" + uriUploadJSON,
      type: "POST",
      data: JSON.stringify(buildChartObject()),
      processData: false,
      contentType: false,
      success: function (res) {
        if (!table) {
          generatePng();
        }

        pageData.charts.push({title: chart.title, filename: chart.filename});
        if (onSave) {
          onSave(chart.filename, '<ons-chart path="' + getPathName() + '/' + chart.filename + '" />');
        }
        $('.chart-builder').stop().fadeOut(200).remove();
      }
    });
  });

  // Builds, parses, and renders our chart in the chart editor
  function renderChart() {
    chart = buildChartObject();
    if (table) {
      $('#preview-chart').empty();
      $('#preview-chart').html('

');
      drawTable(chart);
    }

    var preview = $('#preview-chart');

//      preview.empty();
    preview.html('

');

    var chartHeight = preview.width() * chart.aspectRatio;
    var chartWidth = preview.width();

    if (chartHeight > preview.height()) {
      chartHeight = preview.height();
      chartWidth = preview.height() / chart.aspectRatio;
    }

    renderChartObject('#chart', chart, chartHeight, chartWidth);
  }

  function buildChartObject() {
    var json = $('#chart-data').val();
    if (!chart) {
      chart = {};
    }

    chart.title = $('#chart-title').val();
    chart.filename = chart.title.replace(/[^A-Z0-9]+/ig, "").toLowerCase();
    chart.subtitle = $('#chart-subtitle').val();
    chart.unit = $('#chart-unit').val();
    chart.source = $('#chart-source').val();
    chart.legend = $('#chart-legend').val();
    chart.hideLegend = (chart.legend === 'false') ? true : false;

    if (chart.title === '') {
      chart.title = '[Title]'
    }

    chart.filename = chart.title.replace(/[^A-Z0-9]+/ig, "").toLowerCase();

    chart.data = tsvJSON(json);
    chart.headers = tsvJSONHeaders(json);
    chart.series = tsvJSONColNames(json);
    chart.categories = tsvJSONRowNames(json);

    chart.aspectRatio = $('#aspect-ratio').val();

    if (chart.type === 'barline') {
      var types = {};
      var groups = [];
      var group = [];
      var seriesData = chart.series;
      for (var i = 0; i < seriesData.length; i++) {
        types[seriesData[i]] = $('#types_' + i).val() || 'bar';
      }
      (function () {
        $('#barline input:checkbox:checked').each(function () {
          group.push($(this).val());
        });
        groups.push(group);
        return groups;
      })();
      chart.types = types;
      chart.groups = groups;
    }

    chart.type = $('#chart-type').val();
    if (chart.type === 'table') {
      table = true;
    } else {
      table = false;
    }

    //console.log(chart);
    parseChartObject(chart);

    return chart;
  }

  function parseChartObject(chart) {

    // Determine if we have a time series
    var timeSeries = axisAsTimeSeries(chart.categories);
    if (timeSeries && timeSeries.length > 0) {
      chart.isTimeSeries = true;

      // We create data specific to time
      timeData = [];
      _.each(timeSeries, function (time) {
        var item = chart.data[time['row']];
        item.date = time['date'];
        item.label = time['label'];
        timeData.push(item);
      })

      chart.timeSeries = timeData;
    }
  }

// Data load from text box functions
  function tsvJSON(input) {
    var lines = input.split("\n");
    var result = [];
    var headers = lines[0].split("\t");

    for (var i = 1; i < lines.length; i++) {
      var obj = {};
      if (!table) {
        var currentline = lines[i].split(",").join("").split("\t");
      } else {
        var currentline = lines[i].split("\t");
      }
      for (var j = 0; j < headers.length; j++) {
        obj[headers[j]] = currentline[j];
      }
      result.push(obj);
    }
    return result; //JSON
  }

  function toTsv(data) {
    var output = "";

    for (var i = 0; i < data.headers.length; i++) {
      if (i === data.headers.length - 1) {
        output += data.headers[i];
      } else {
        output += data.headers[i] + "\t";
      }
    }

    for (var i = 0; i < data.categories.length; i++) {
      output += "\n" + toTsvLine(data.data[i], data.headers);
    }

    return output;
  }

  function toTsvLine(data, headers) {

    var output = "";

    for (var i = 0; i < headers.length; i++) {
      if (i === headers.length - 1) {
        output += data[headers[i]];
      } else {
        output += data[headers[i]] + "\t";
      }
    }
    return output;
  }

  function tsvJSONRowNames(input) {
    var lines = input.split("\n");
    var result = [];

    for (var i = 1; i < lines.length; i++) {
      var currentline = lines[i].split("\t");
      result.push(currentline[0]);
    }
    return result
  }

  function tsvJSONColNames(input) {
    var lines = input.split("\n");
    var headers = lines[0].split("\t");
    headers.shift();
    return headers;
  }

  function tsvJSONHeaders(input) {
    var lines = input.split("\n");
    var headers = lines[0].split("\t");
    return headers;
  }

  function exportToSVG() {
    var svgContainer = $('#chart');
    var svg = svgContainer.find('svg');

    var styleContent = "\n";
    for (var i = 0; i < document.styleSheets.length; i++) {
      str = document.styleSheets[i].href.split("/");
      if (str[str.length - 1] == "c3.css") {
        var rules = document.styleSheets[i].rules;
        for (var j = 0; j < rules.length; j++) {
          styleContent += (rules[j].cssText + "\n");
        }
        break;
      }
    }

    //var style = document.createElementNS("http://www.w3.org/2000/svg", "style");
    //$(style).textContent += "\n<![CDATA[\n" + styleContent + "\n]]>\n";
    //
    //svg.prepend(style);
    //svg[0].getElementsByTagName("defs")[0].appendChild(style);

    svg.prepend("\n");
    svg.find("style").textContent += "\n<![CDATA[" + styleContent + "]]>\n";

    //if ($('#chart-type').val() === 'line') {
    //  $('.c3 line').css("fill", "none");
    //  console.log($('.c3 line'))
    //}

    var source = (new XMLSerializer).serializeToString(svg[0]);
    //console.log(source);

    //add name spaces.
    if (!source.match(/^<svg[^>]+xmlns="http\:\/\/www\.w3\.org\/2000\/svg"/)) {
      source = source.replace(/^<svg/, '<svg xmlns="http://www.w3.org/2000/svg"');
    }
    if (!source.match(/^<svg[^>]+"http\:\/\/www\.w3\.org\/1999\/xlink"/)) {
      source = source.replace(/^<svg/, '<svg xmlns:xlink="http://www.w3.org/1999/xlink"');
    }
    //add padding
    source = source.replace(/style="overflow: hidden;"/, 'style="overflow: hidden; padding: 50px;"');

    //add xml declaration
    source = '<?xml version="1.0" standalone="no"?>\r\n' + source;

    return source;
  }

// Steps through time series points
  function axisAsTimeSeries(axis) {
    var result = [];
    var rowNumber = 0;

    _.each(axis, function (tryTimeString) {
      var time = convertTimeString(tryTimeString);
      if (time) {
        time.row = rowNumber;
        rowNumber = rowNumber + 1;

        result.push(time);
      } else {
        return null;
      }
    });
    return result;
  }

  function convertTimeString(timeString) {
    // First time around parse the time string according to rules from regular timeseries
    var result = {};
    result.label = timeString;

    // Format time string
    // Check for strings that will turn themselves into a strange format
    twoDigitYearEnd = timeString.match(/\W\d\d$/);
    if (twoDigitYearEnd !== null) {
      year = parseInt(twoDigitYearEnd = timeString.match(/\W\d\d$/));
      prefix = timeString.substr(0, timeString.length - 3);

      if (year >= 40) {
        timeString = prefix + " 19" + year;
      } else {
        timeString = prefix + " 20" + year;
      }
    }

    // We are going with all times in a common format
    var date = new Date(timeString);
    if (!isNaN(date.getTime())) {
      result.date = date;
      result.period = 'other';
      return result;
    }

    return (null);
  }

  function drawTable(data) {
    var title = data.headers;
    var rows = data.data;
    drawTitles(title);
    for (var i = 0; i < rows.length; i++) {
      drawRow(rows[i]);
    }

    function drawTitles(title) {
      var row = $("
");
      $("#dataTable").append(row);
      for (var j = 0; j < title.length; j++) {
        row.append($("" + title[j] + "	"));
      }
    }

    function drawRow(rowData) {
      var row = $("
")
      $("#dataTable").append(row);
      for (var j = 0; j < title.length; j++) {
        row.append($("" + rowData[title[j]] + "	"));
      }
    }
  }

  //generatePng();
  function generatePng() {

    var preview = $('#chart');
    var chartHeight = preview.width() * chart.aspectRatio;
    var chartWidth = preview.width();

    if (chartHeight > preview.height()) {
      chartHeight = preview.height();
      chartWidth = preview.height() / chart.aspectRatio;
    }

    var content = exportToSVG().trim();

    var $canvas = $('#hiddenCanvas');
    $canvas.width(chartWidth);
    $canvas.height(chartHeight);

    var canvas = $canvas.get(0);

    // Draw svg on canvas
    canvg(canvas, content);

    // get data url from canvas.
    var dataUrl = canvas.toDataURL('image/png');
    var pngData = dataUrl.replace(/^data:image\/(png|jpg);base64,/, "");
    //console.log(dataUrl);

    // render png
    //var $png = $('#hiddenPng');
    //var png = $png[0];
    //$png.attr('src', dataUrl);

    var raw = window.atob(pngData);
    var rawLength = raw.length;
    var array = new Uint8Array(new ArrayBuffer(rawLength));

    for (var i = 0; i < rawLength; i++) {
      array[i] = raw.charCodeAt(i);
    }

    var pngUri = pageUrl + "/" + chart.filename + ".png";
    $.ajax({
      url: "/zebedee/content/" + Florence.collection.id + "?uri=" + pngUri,
      type: "POST",
      data: new Blob([array], {type: 'image/png'}),
      contentType: "image/png",
      processData: false,
      success: function (res) {
        console.log('png uploaded!');
      }
    });
  }
}

<commit_msg>Only try and draw bar+line chart controls on load if an existing chart is provided.<commit_after>function loadChartBuilder(pageData, onSave, chart) {
  var chart = chart;
  var pageUrl = localStorage.getItem('pageurl');
  var html = templates.chartBuilder(chart);
  var table = false;
  $('body').append(html);
  $('.chart-builder').css("display", "block");

  if (chart) {
    $('#chart-data').val(toTsv(chart));
    refreshBarLineSection();
  }

  renderChart();

  function refreshBarLineSection() {
    var data = editBarline(chart);
    var html = templates.chartEditBarlines(data);
    $('#barline').html(html);
  }

  function editBarline(chart) {
    var data = [];
    var series = chart.series;

    if (chart.type === 'barline') { // if we have a bar line we want to populate the entries for each series
      if (chart.types) { // if we have existing types use them
        var type = _.values(chart.types);
        for (var i = 0; i < chart.series.length; i += 1) {
          data.push({
            series: series[i], type: type[i],
            isChecked: (function () {
              var checked = _.indexOf(chart.groups[0], series[i]);
              if (checked < 0) {
                return checked = false;
              } else {
                return checked = true;
              }
            })()
          });
        }
      } else { // if we have no existing types, default them
        for (var i = 0; i < chart.series.length; i += 1) {
          data.push({series: series[i], type: '', isChecked: false});
        }
      }
    }
    return data;
  }

  $('#edit-chart').on('input change', ':input', function () {
    chart = buildChartObject();
    refreshBarLineSection();
    renderChart();
  });

  $('.btn-chart-builder-cancel').on('click', function () {
    $('.chart-builder').stop().fadeOut(200).remove();
  });

  $('.btn-chart-builder-create').on('click', function () {

    if (!pageData.charts) {
      pageData.charts = []
    } else {
      if (_.find(pageData.charts, function (existingChart) {
          return existingChart.filename === chart.filename
        })) {
        alert("A chart with this name already exists.");
        return;
      }
    }

    var uriUploadJSON = pageUrl + "/" + chart.filename + ".json";
    $.ajax({
      url: "/zebedee/content/" + Florence.collection.id + "?uri=" + uriUploadJSON,
      type: "POST",
      data: JSON.stringify(buildChartObject()),
      processData: false,
      contentType: false,
      success: function (res) {
        if (!table) {
          generatePng();
        }

        pageData.charts.push({title: chart.title, filename: chart.filename});
        if (onSave) {
          onSave(chart.filename, '<ons-chart path="' + getPathName() + '/' + chart.filename + '" />');
        }
        $('.chart-builder').stop().fadeOut(200).remove();
      }
    });
  });

  // Builds, parses, and renders our chart in the chart editor
  function renderChart() {
    chart = buildChartObject();
    if (table) {
      $('#preview-chart').empty();
      $('#preview-chart').html('

');
      drawTable(chart);
    }

    var preview = $('#preview-chart');

//      preview.empty();
    preview.html('

');

    var chartHeight = preview.width() * chart.aspectRatio;
    var chartWidth = preview.width();

    if (chartHeight > preview.height()) {
      chartHeight = preview.height();
      chartWidth = preview.height() / chart.aspectRatio;
    }

    renderChartObject('#chart', chart, chartHeight, chartWidth);
  }

  function buildChartObject() {
    var json = $('#chart-data').val();
    if (!chart) {
      chart = {};
    }

    chart.title = $('#chart-title').val();
    chart.filename = chart.title.replace(/[^A-Z0-9]+/ig, "").toLowerCase();
    chart.subtitle = $('#chart-subtitle').val();
    chart.unit = $('#chart-unit').val();
    chart.source = $('#chart-source').val();
    chart.legend = $('#chart-legend').val();
    chart.hideLegend = (chart.legend === 'false') ? true : false;

    if (chart.title === '') {
      chart.title = '[Title]'
    }

    chart.filename = chart.title.replace(/[^A-Z0-9]+/ig, "").toLowerCase();

    chart.data = tsvJSON(json);
    chart.headers = tsvJSONHeaders(json);
    chart.series = tsvJSONColNames(json);
    chart.categories = tsvJSONRowNames(json);

    chart.aspectRatio = $('#aspect-ratio').val();

    if (chart.type === 'barline') {
      var types = {};
      var groups = [];
      var group = [];
      var seriesData = chart.series;
      for (var i = 0; i < seriesData.length; i++) {
        types[seriesData[i]] = $('#types_' + i).val() || 'bar';
      }
      (function () {
        $('#barline input:checkbox:checked').each(function () {
          group.push($(this).val());
        });
        groups.push(group);
        return groups;
      })();
      chart.types = types;
      chart.groups = groups;
    }

    chart.type = $('#chart-type').val();
    if (chart.type === 'table') {
      table = true;
    } else {
      table = false;
    }

    //console.log(chart);
    parseChartObject(chart);

    return chart;
  }

  function parseChartObject(chart) {

    // Determine if we have a time series
    var timeSeries = axisAsTimeSeries(chart.categories);
    if (timeSeries && timeSeries.length > 0) {
      chart.isTimeSeries = true;

      // We create data specific to time
      timeData = [];
      _.each(timeSeries, function (time) {
        var item = chart.data[time['row']];
        item.date = time['date'];
        item.label = time['label'];
        timeData.push(item);
      })

      chart.timeSeries = timeData;
    }
  }

// Data load from text box functions
  function tsvJSON(input) {
    var lines = input.split("\n");
    var result = [];
    var headers = lines[0].split("\t");

    for (var i = 1; i < lines.length; i++) {
      var obj = {};
      if (!table) {
        var currentline = lines[i].split(",").join("").split("\t");
      } else {
        var currentline = lines[i].split("\t");
      }
      for (var j = 0; j < headers.length; j++) {
        obj[headers[j]] = currentline[j];
      }
      result.push(obj);
    }
    return result; //JSON
  }

  function toTsv(data) {
    var output = "";

    for (var i = 0; i < data.headers.length; i++) {
      if (i === data.headers.length - 1) {
        output += data.headers[i];
      } else {
        output += data.headers[i] + "\t";
      }
    }

    for (var i = 0; i < data.categories.length; i++) {
      output += "\n" + toTsvLine(data.data[i], data.headers);
    }

    return output;
  }

  function toTsvLine(data, headers) {

    var output = "";

    for (var i = 0; i < headers.length; i++) {
      if (i === headers.length - 1) {
        output += data[headers[i]];
      } else {
        output += data[headers[i]] + "\t";
      }
    }
    return output;
  }

  function tsvJSONRowNames(input) {
    var lines = input.split("\n");
    var result = [];

    for (var i = 1; i < lines.length; i++) {
      var currentline = lines[i].split("\t");
      result.push(currentline[0]);
    }
    return result
  }

  function tsvJSONColNames(input) {
    var lines = input.split("\n");
    var headers = lines[0].split("\t");
    headers.shift();
    return headers;
  }

  function tsvJSONHeaders(input) {
    var lines = input.split("\n");
    var headers = lines[0].split("\t");
    return headers;
  }

  function exportToSVG() {
    var svgContainer = $('#chart');
    var svg = svgContainer.find('svg');

    var styleContent = "\n";
    for (var i = 0; i < document.styleSheets.length; i++) {
      str = document.styleSheets[i].href.split("/");
      if (str[str.length - 1] == "c3.css") {
        var rules = document.styleSheets[i].rules;
        for (var j = 0; j < rules.length; j++) {
          styleContent += (rules[j].cssText + "\n");
        }
        break;
      }
    }

    //var style = document.createElementNS("http://www.w3.org/2000/svg", "style");
    //$(style).textContent += "\n<![CDATA[\n" + styleContent + "\n]]>\n";
    //
    //svg.prepend(style);
    //svg[0].getElementsByTagName("defs")[0].appendChild(style);

    svg.prepend("\n");
    svg.find("style").textContent += "\n<![CDATA[" + styleContent + "]]>\n";

    //if ($('#chart-type').val() === 'line') {
    //  $('.c3 line').css("fill", "none");
    //  console.log($('.c3 line'))
    //}

    var source = (new XMLSerializer).serializeToString(svg[0]);
    //console.log(source);

    //add name spaces.
    if (!source.match(/^<svg[^>]+xmlns="http\:\/\/www\.w3\.org\/2000\/svg"/)) {
      source = source.replace(/^<svg/, '<svg xmlns="http://www.w3.org/2000/svg"');
    }
    if (!source.match(/^<svg[^>]+"http\:\/\/www\.w3\.org\/1999\/xlink"/)) {
      source = source.replace(/^<svg/, '<svg xmlns:xlink="http://www.w3.org/1999/xlink"');
    }
    //add padding
    source = source.replace(/style="overflow: hidden;"/, 'style="overflow: hidden; padding: 50px;"');

    //add xml declaration
    source = '<?xml version="1.0" standalone="no"?>\r\n' + source;

    return source;
  }

// Steps through time series points
  function axisAsTimeSeries(axis) {
    var result = [];
    var rowNumber = 0;

    _.each(axis, function (tryTimeString) {
      var time = convertTimeString(tryTimeString);
      if (time) {
        time.row = rowNumber;
        rowNumber = rowNumber + 1;

        result.push(time);
      } else {
        return null;
      }
    });
    return result;
  }

  function convertTimeString(timeString) {
    // First time around parse the time string according to rules from regular timeseries
    var result = {};
    result.label = timeString;

    // Format time string
    // Check for strings that will turn themselves into a strange format
    twoDigitYearEnd = timeString.match(/\W\d\d$/);
    if (twoDigitYearEnd !== null) {
      year = parseInt(twoDigitYearEnd = timeString.match(/\W\d\d$/));
      prefix = timeString.substr(0, timeString.length - 3);

      if (year >= 40) {
        timeString = prefix + " 19" + year;
      } else {
        timeString = prefix + " 20" + year;
      }
    }

    // We are going with all times in a common format
    var date = new Date(timeString);
    if (!isNaN(date.getTime())) {
      result.date = date;
      result.period = 'other';
      return result;
    }

    return (null);
  }

  function drawTable(data) {
    var title = data.headers;
    var rows = data.data;
    drawTitles(title);
    for (var i = 0; i < rows.length; i++) {
      drawRow(rows[i]);
    }

    function drawTitles(title) {
      var row = $("
");
      $("#dataTable").append(row);
      for (var j = 0; j < title.length; j++) {
        row.append($("" + title[j] + "	"));
      }
    }

    function drawRow(rowData) {
      var row = $("
")
      $("#dataTable").append(row);
      for (var j = 0; j < title.length; j++) {
        row.append($("" + rowData[title[j]] + "	"));
      }
    }
  }

  //generatePng();
  function generatePng() {

    var preview = $('#chart');
    var chartHeight = preview.width() * chart.aspectRatio;
    var chartWidth = preview.width();

    if (chartHeight > preview.height()) {
      chartHeight = preview.height();
      chartWidth = preview.height() / chart.aspectRatio;
    }

    var content = exportToSVG().trim();

    var $canvas = $('#hiddenCanvas');
    $canvas.width(chartWidth);
    $canvas.height(chartHeight);

    var canvas = $canvas.get(0);

    // Draw svg on canvas
    canvg(canvas, content);

    // get data url from canvas.
    var dataUrl = canvas.toDataURL('image/png');
    var pngData = dataUrl.replace(/^data:image\/(png|jpg);base64,/, "");
    //console.log(dataUrl);

    // render png
    //var $png = $('#hiddenPng');
    //var png = $png[0];
    //$png.attr('src', dataUrl);

    var raw = window.atob(pngData);
    var rawLength = raw.length;
    var array = new Uint8Array(new ArrayBuffer(rawLength));

    for (var i = 0; i < rawLength; i++) {
      array[i] = raw.charCodeAt(i);
    }

    var pngUri = pageUrl + "/" + chart.filename + ".png";
    $.ajax({
      url: "/zebedee/content/" + Florence.collection.id + "?uri=" + pngUri,
      type: "POST",
      data: new Blob([array], {type: 'image/png'}),
      contentType: "image/png",
      processData: false,
      success: function (res) {
        console.log('png uploaded!');
      }
    });
  }
}

<|endoftext|>


<commit_before>/* Javascript to track changes, and prompt the user for saving */

var ModificationTracker = Class.create({

    initialize: function(){
        if(AE.checkForModification){
        	this.form = $('command');

            if(this.form){
            	this.formElements =  this.form.select('textarea','input','select');
                AE.formFieldCount = this.formElements.size();

                //observe the change
                this.formElements.each(function(el){
                	el.observe('change',function(){
                		AE.formFieldModified=true;
                	});
                });

                window.onbeforeunload = this.checkForModificationsOnPage.bind(this);
            }

        }

    },

    //check if there is a form field change
    checkForModificationsOnPage: function(){
        if(AE.checkForModification){
           var curFormElementCount = this.form.select('textarea','input','select').size();
           if(AE.formFieldModified || (curFormElementCount != AE.formFieldCount) )
        	   return AE.UNSAVED_DATA_MESSAGE;
        }
    }

});

Event.observe(document, "dom:loaded", function(){
	new ModificationTracker();
});
<commit_msg>CAAERS-2731<commit_after>/* Javascript to track changes, and prompt the user for saving */

var ModificationTracker = Class.create({

    initialize: function(){
        if(AE.checkForModification){
        	this.form = $('command');

            if(this.form){
            	this.formElements =  this.form.select('textarea','input','select');
                AE.formFieldCount = this.formElements.size();

                //observe the change
                this.formElements.each(function(el){
                	el.observe('change',function(){
                		AE.formFieldModified=true;
                	});
                });

                //observe submit on the form
                this.form.observe('submit', function(){
                	AE.formFieldModified=false;
                });

                window.onbeforeunload = this.checkForModificationsOnPage.bind(this);
            }

        }

    },

    //check if there is a form field change
    checkForModificationsOnPage: function(){
        if(AE.checkForModification){
           var curFormElementCount = this.form.select('textarea','input','select').size();
           if(AE.formFieldModified || (curFormElementCount != AE.formFieldCount) )
        	   return AE.UNSAVED_DATA_MESSAGE;
        }
    }

});

Event.observe(document, "dom:loaded", function(){
	new ModificationTracker();
});
<|endoftext|>


<commit_before>/*
 * Script to obtain useful information from an Orange Your Account login in the UK.
 */

var system = require('system');

// Get the parameters from the launch script
var jsonString = system.args[1];
var params = JSON.parse(jsonString);

// Check we have the necessary parameters
if (!params.username)
{
	system.stderr.writeLine('[error] This script requires a username');
	phantom.exit();
}
if (!params.password)
{
	system.stderr.writeLine('[error] This script requires a password');
	phantom.exit();
}

function login(params)
{
	/**
	 * This function is a class to hold the load handler methods
	 */
	function LoadHandler()
	{
		this.constants = new Object();
		this.constants.PAGE_LOGIN = 'Login';
		this.constants.PAGE_LOGIN_FAILED = 'LoginFailed';
		// This is either the email request or the account home, don't care
		this.constants.PAGE_LOGIN_SUBMITTED = 'LoginSubmitted';
		this.constants.PAGE_EMAIL_REQUEST = 'Email';
		this.constants.PAGE_ACCOUNT_HOME = 'AccountHome';
		this.constants.PAGE_ALLOWANCE_PREP = 'AllowancePrep';
		this.constants.PAGE_ALLOWANCE_MAIN = 'AllowanceMain';
		this.constants.PAGE_USAGE = 'Usage';

		/**
		 * Handles the loading of the login screen
		 *
		 * @param page
		 * @param status
		 */
		this.onLoadLogin = function(page, status)
		{
			var pageId = page.evaluate(
				function(context)
				{
					var
						elementLoggedOut = document.querySelector('h1.PageTitleSignInEE'),
						titleLoggedOut = elementLoggedOut ? elementLoggedOut.innerText : '',

						elementAccount = document.querySelector('#header h1'),
						titleAccount = elementAccount ? elementAccount.innerText : '',

						pageId = null
					;

					// Determine whether we are logged out...
					if (titleLoggedOut.indexOf('SERVICES ON ORANGE.CO.UK') > -1)
					{
						pageId = context.constants.PAGE_LOGIN;
					}
					// ...or logged in from a previous run and found account home page
					else if (titleAccount.indexOf('YOUR ACCOUNT') > -1)
					{
						pageId = context.constants.PAGE_ACCOUNT_HOME;
					}
					else
					{
						console.log('[warning] We received something unexpected in onLoadLogin');
					}

					return pageId;
				},
				{
					constants: this.constants
				}
			);

			// Only make an attempt to log on if this is the login page
			if (pageId === this.constants.PAGE_LOGIN)
			{
				console.log('[ok] Found login screen');
				page.watcherId = this.constants.PAGE_LOGIN_SUBMITTED;
				page.evaluate(
					function(params)
					{
						// Set up the credentials and submit the form
						document.querySelector('input[name=LOGIN]').value = params.username;
						document.querySelector('input[name=PASSWORD]').value = params.password;
						document.querySelector('div.inner_left_ee form').submit();
					},
					page.watcherParams
				);
			}
			else if (pageId === this.constants.PAGE_ACCOUNT_HOME)
			{
				console.log('[ok] Found account page, already logged in');

				// Emulates a finished event for the account page
				page.watcherId = this.constants.PAGE_ACCOUNT_HOME;
				page.onLoadFinished(status);
			}
		};

		/**
		 * Handles the screen after the creds have been submitted
		 *
		 * If the credentials are wrong this is sometimes called twice
		 *
		 * @param page
		 * @param status
		 */
		this.onLoadLoginSubmitted = function(page, status)
		{
			var pageId = page.evaluate(
				function(context)
				{
					var
						// Test for wrong credentials
						elementError = document.querySelector('div.inner_left_ee p.error'),
						textError = elementError ? elementError.innerText : '',

						elementEmail = document.querySelector('.login-flow h2'),
						titleEmail = elementEmail ? elementEmail.innerText : '',

						pageId = null
					;

					if (textError.indexOf('Please enter a valid username and password') > -1)
					{
						pageId = context.constants.PAGE_LOGIN_FAILED;
					}
					else if (titleEmail.indexOf("WE'D LIKE TO GET TO KNOW YOU BETTER") > -1)
					{
						pageId = context.constants.PAGE_EMAIL_REQUEST;
					}
					else
					{
						console.log('[warning] We received something unexpected in onLoadLoginSubmitted');
					}

					return pageId;
				},
				{
					constants: this.constants
				}
			);

			if (pageId === this.constants.PAGE_LOGIN_FAILED)
			{
				console.log('[error] The username and password settings are incorrect');
				phantom.exit();
			}
			else if (pageId === this.constants.PAGE_EMAIL_REQUEST)
			{
				// Handle skip to next page here
				console.log('[ok] Found email request screen');

				// Point to the page we want to load
				page.watcherId = this.constants.PAGE_ACCOUNT_HOME;
				page.open('https://www.youraccount.orange.co.uk/sss/jfn?entry=true');
			}
		};

		this.onLoadAccountHome = function(page, status)
		{
			console.log('[ok] Now in account home screen');

			// This will cause a redirect, and we're interested in the second one (the first one says "processing")
			page.watcherId = this.constants.PAGE_ALLOWANCE_PREP;
			page.open('https://www.youraccount.orange.co.uk/sss/jfn?mfunc=877&cem=RMN0002_ViewRemMinAndText&jfnRC=1');
		};

		/**
		 * This renders a "processing" screen on the remote side, and redirects after a few seconds
		 *
		 * @param page
		 * @param status
		 */
		this.onLoadAllowancePrep = function(page, status)
		{
			console.log('[ok] Now in allowance preparation screen');
			page.watcherId = this.constants.PAGE_ALLOWANCE_MAIN;
		};

		this.onLoadAllowanceMain = function(page, status)
		{
			console.log('[ok] Now in allowance main screen');

			var data = page.evaluate(
				function()
				{
					var
						elementBalance = document.querySelector('#paymBalanceIncVAT'),
						textBalance = elementBalance ? elementBalance.innerText : '',

						elementAllowance = document.querySelector('.bundleSummary .status'),
						textAllowance = elementAllowance ? elementAllowance.innerText : '',

						elementLastUpdated = document.querySelector('#viewRemMinsTextsSummaryForm .bundleUpdated'),
						textLastUpdated = elementLastUpdated ? elementLastUpdated.innerText : ''
					;

					return {
						balance: textBalance,
						allowance: textAllowance,
						lastUpdated: textLastUpdated
					};
				}
			);

			console.log('Balance: ' + data.balance);
			console.log('Allowance: ' + data.allowance);
			console.log('Last updated: ' + data.lastUpdated);

			// Finally pop over to the usage screen
			page.watcherId = this.constants.PAGE_USAGE;
			page.open('https://www.youraccount.orange.co.uk/sss/jfn?mfunc=1559&jfnRC=6');
		};

		this.onLoadUsage = function(page, status)
		{
			console.log('[ok] Now in data usage screen');

			var data = page.evaluate(
				function()
				{
					var
						elementUsage = document.querySelector('#viewUkDataAllowanceForm .dataUsageFont'),
						textUsage = elementUsage ? elementUsage.innerText : ''
					;

					return {
						usage: textUsage
					};
				}
			);

			console.log('Usage: ' + data.usage);
		};
	}

	// Logon to the system
	var
		page = require('webpage').create(),
		loadHandler = new LoadHandler()
	;

	page.onLoadStarted = function() {
		console.log('[ok] Page load started: ' + page.watcherId);
	};

	/**
	 * This is called, I think, when a redirect happens
	 *
	 * @param targetUrl
	 */
	page.onUrlChanged = function(targetUrl) {
		console.log('[debug] URL changed: ' + targetUrl + ' on page: ' + page.watcherId);
	};

	// Thanks to https://www.princeton.edu/~crmarsh/phantomjs/
	page.onConsoleMessage = function(msg)
	{
		console.log('[remote] Console log: ' + msg);
	};

	/**
	 * Handles all load finished events
	 *
	 * @todo Add in a delay here, so as to reduce load on the remote server
	 *
	 * @param status
	 */
	page.onLoadFinished = function(status) {
		console.log('[ok] Page load finished, page: ' + page.watcherId + ', status: ' + status);

		// Call the custom handler, passing in interesting params
		var method = loadHandler['onLoad' + page.watcherId];
		if (typeof method === 'function')
		{
			method.call(page.watcherHandler, page, status);
		}
		else
		{
			console.log('[error] Handler missing for load event: ' + page.watcherId);
		}
	};

	page.watcherId = 'Login';
	page.watcherParams = params;
	page.watcherHandler = loadHandler;
	page.open('https://web.orange.co.uk/r/login/');
}

login(params);
<commit_msg>Add some @todo notes<commit_after>/*
 * Script to obtain useful information from an Orange Your Account login in the UK.
 *
 * @todo Add delay to loaded handler, so as to reduce load on the remote server
 * @todo Use special methods for console [ok], [debug], [error] etc
 * @todo Switch any system.stdOut/stdErr to console.log
 * @todo Can we reduce reliance on full URLs?
 * @todo Can we get Orange working without SSL security disabled?
 */

var system = require('system');

// Get the parameters from the launch script
var jsonString = system.args[1];
var params = JSON.parse(jsonString);

// Check we have the necessary parameters
if (!params.username)
{
	system.stderr.writeLine('[error] This script requires a username');
	phantom.exit();
}
if (!params.password)
{
	system.stderr.writeLine('[error] This script requires a password');
	phantom.exit();
}

function login(params)
{
	/**
	 * This function is a class to hold the load handler methods
	 */
	function LoadHandler()
	{
		this.constants = new Object();
		this.constants.PAGE_LOGIN = 'Login';
		this.constants.PAGE_LOGIN_FAILED = 'LoginFailed';
		// This is either the email request or the account home, don't care
		this.constants.PAGE_LOGIN_SUBMITTED = 'LoginSubmitted';
		this.constants.PAGE_EMAIL_REQUEST = 'Email';
		this.constants.PAGE_ACCOUNT_HOME = 'AccountHome';
		this.constants.PAGE_ALLOWANCE_PREP = 'AllowancePrep';
		this.constants.PAGE_ALLOWANCE_MAIN = 'AllowanceMain';
		this.constants.PAGE_USAGE = 'Usage';

		/**
		 * Handles the loading of the login screen
		 *
		 * @param page
		 * @param status
		 */
		this.onLoadLogin = function(page, status)
		{
			var pageId = page.evaluate(
				function(context)
				{
					var
						elementLoggedOut = document.querySelector('h1.PageTitleSignInEE'),
						titleLoggedOut = elementLoggedOut ? elementLoggedOut.innerText : '',

						elementAccount = document.querySelector('#header h1'),
						titleAccount = elementAccount ? elementAccount.innerText : '',

						pageId = null
					;

					// Determine whether we are logged out...
					if (titleLoggedOut.indexOf('SERVICES ON ORANGE.CO.UK') > -1)
					{
						pageId = context.constants.PAGE_LOGIN;
					}
					// ...or logged in from a previous run and found account home page
					else if (titleAccount.indexOf('YOUR ACCOUNT') > -1)
					{
						pageId = context.constants.PAGE_ACCOUNT_HOME;
					}
					else
					{
						console.log('[warning] We received something unexpected in onLoadLogin');
					}

					return pageId;
				},
				{
					constants: this.constants
				}
			);

			// Only make an attempt to log on if this is the login page
			if (pageId === this.constants.PAGE_LOGIN)
			{
				console.log('[ok] Found login screen');
				page.watcherId = this.constants.PAGE_LOGIN_SUBMITTED;
				page.evaluate(
					function(params)
					{
						// Set up the credentials and submit the form
						document.querySelector('input[name=LOGIN]').value = params.username;
						document.querySelector('input[name=PASSWORD]').value = params.password;
						document.querySelector('div.inner_left_ee form').submit();
					},
					page.watcherParams
				);
			}
			else if (pageId === this.constants.PAGE_ACCOUNT_HOME)
			{
				console.log('[ok] Found account page, already logged in');

				// Emulates a finished event for the account page
				page.watcherId = this.constants.PAGE_ACCOUNT_HOME;
				page.onLoadFinished(status);
			}
		};

		/**
		 * Handles the screen after the creds have been submitted
		 *
		 * If the credentials are wrong this is sometimes called twice
		 *
		 * @param page
		 * @param status
		 */
		this.onLoadLoginSubmitted = function(page, status)
		{
			var pageId = page.evaluate(
				function(context)
				{
					var
						// Test for wrong credentials
						elementError = document.querySelector('div.inner_left_ee p.error'),
						textError = elementError ? elementError.innerText : '',

						elementEmail = document.querySelector('.login-flow h2'),
						titleEmail = elementEmail ? elementEmail.innerText : '',

						pageId = null
					;

					if (textError.indexOf('Please enter a valid username and password') > -1)
					{
						pageId = context.constants.PAGE_LOGIN_FAILED;
					}
					else if (titleEmail.indexOf("WE'D LIKE TO GET TO KNOW YOU BETTER") > -1)
					{
						pageId = context.constants.PAGE_EMAIL_REQUEST;
					}
					else
					{
						console.log('[warning] We received something unexpected in onLoadLoginSubmitted');
					}

					return pageId;
				},
				{
					constants: this.constants
				}
			);

			if (pageId === this.constants.PAGE_LOGIN_FAILED)
			{
				console.log('[error] The username and password settings are incorrect');
				phantom.exit();
			}
			else if (pageId === this.constants.PAGE_EMAIL_REQUEST)
			{
				// Handle skip to next page here
				console.log('[ok] Found email request screen');

				// Point to the page we want to load
				page.watcherId = this.constants.PAGE_ACCOUNT_HOME;
				page.open('https://www.youraccount.orange.co.uk/sss/jfn?entry=true');
			}
		};

		this.onLoadAccountHome = function(page, status)
		{
			console.log('[ok] Now in account home screen');

			// This will cause a redirect, and we're interested in the second one (the first one says "processing")
			page.watcherId = this.constants.PAGE_ALLOWANCE_PREP;
			page.open('https://www.youraccount.orange.co.uk/sss/jfn?mfunc=877&cem=RMN0002_ViewRemMinAndText&jfnRC=1');
		};

		/**
		 * This renders a "processing" screen on the remote side, and redirects after a few seconds
		 *
		 * @param page
		 * @param status
		 */
		this.onLoadAllowancePrep = function(page, status)
		{
			console.log('[ok] Now in allowance preparation screen');
			page.watcherId = this.constants.PAGE_ALLOWANCE_MAIN;
		};

		this.onLoadAllowanceMain = function(page, status)
		{
			console.log('[ok] Now in allowance main screen');

			var data = page.evaluate(
				function()
				{
					var
						elementBalance = document.querySelector('#paymBalanceIncVAT'),
						textBalance = elementBalance ? elementBalance.innerText : '',

						elementAllowance = document.querySelector('.bundleSummary .status'),
						textAllowance = elementAllowance ? elementAllowance.innerText : '',

						elementLastUpdated = document.querySelector('#viewRemMinsTextsSummaryForm .bundleUpdated'),
						textLastUpdated = elementLastUpdated ? elementLastUpdated.innerText : ''
					;

					return {
						balance: textBalance,
						allowance: textAllowance,
						lastUpdated: textLastUpdated
					};
				}
			);

			console.log('Balance: ' + data.balance);
			console.log('Allowance: ' + data.allowance);
			console.log('Last updated: ' + data.lastUpdated);

			// Finally pop over to the usage screen
			page.watcherId = this.constants.PAGE_USAGE;
			page.open('https://www.youraccount.orange.co.uk/sss/jfn?mfunc=1559&jfnRC=6');
		};

		this.onLoadUsage = function(page, status)
		{
			console.log('[ok] Now in data usage screen');

			var data = page.evaluate(
				function()
				{
					var
						elementUsage = document.querySelector('#viewUkDataAllowanceForm .dataUsageFont'),
						textUsage = elementUsage ? elementUsage.innerText : ''
					;

					return {
						usage: textUsage
					};
				}
			);

			console.log('Usage: ' + data.usage);
		};
	}

	// Logon to the system
	var
		page = require('webpage').create(),
		loadHandler = new LoadHandler()
	;

	page.onLoadStarted = function() {
		console.log('[ok] Page load started: ' + page.watcherId);
	};

	/**
	 * This is called, I think, when a redirect happens
	 *
	 * @param targetUrl
	 */
	page.onUrlChanged = function(targetUrl) {
		console.log('[debug] URL changed: ' + targetUrl + ' on page: ' + page.watcherId);
	};

	// Thanks to https://www.princeton.edu/~crmarsh/phantomjs/
	page.onConsoleMessage = function(msg)
	{
		console.log('[remote] Console log: ' + msg);
	};

	/**
	 * Handles all load finished events
	 *
	 * @param status
	 */
	page.onLoadFinished = function(status) {
		console.log('[ok] Page load finished, page: ' + page.watcherId + ', status: ' + status);

		// Call the custom handler, passing in interesting params
		var method = loadHandler['onLoad' + page.watcherId];
		if (typeof method === 'function')
		{
			method.call(page.watcherHandler, page, status);
		}
		else
		{
			console.log('[error] Handler missing for load event: ' + page.watcherId);
		}
	};

	page.watcherId = 'Login';
	page.watcherParams = params;
	page.watcherHandler = loadHandler;
	page.open('https://web.orange.co.uk/r/login/');
}

login(params);
<|endoftext|>


<commit_before>var debug = require('debug')('loopback-ds-readonly-mixin');

module.exports = function(Model, options) {
  'use strict';

  debug('ReadOnly mixin for Model %s', Model.modelName);

  var loopback = require('loopback');

  // Make sure emailVerified is not set by creation
  Model.stripReadOnlyProperties = function(ctx, modelInstance, next) {
    var body = ctx.req.body;
    if (!body) {
      return next();
    }
    var properties = (Object.keys(options).length) ? options : null;
    if (properties) {
      debug('Creating %s : Read only properties are %j', 'modelInstance.Model.modelName', properties);
      Object.keys(properties).forEach(function(key) {
        debug('The \'%s\' property is readonly, removing incoming data', key);
        delete body[key];
      });
      next();
    } else {
      var err = new Error('Unable to update: ' + Model.modelName + ' is read only.');
      err.statusCode = 403;
      next(err);
    }
  };

  // Make sure emailVerified is not set by creation
  Model.beforeRemote('create', function(ctx, modelInstance, next) {
    Model.stripReadOnlyProperties(ctx, modelInstance, next);
  });
  Model.beforeRemote('upsert', function(ctx, modelInstance, next) {
    Model.stripReadOnlyProperties(ctx, modelInstance, next);
  });
  Model.beforeRemote('prototype.updateAttributes', function(ctx, modelInstance, next) {
    Model.stripReadOnlyProperties(ctx, modelInstance, next);
  });
  Model.beforeRemote('updateAll', function(ctx, modelInstance, next) {
    Model.stripReadOnlyProperties(ctx, modelInstance, next);
  });
}
<commit_msg>Removed unused dependency.<commit_after>var debug = require('debug')('loopback-ds-readonly-mixin');

module.exports = function(Model, options) {
  'use strict';

  debug('ReadOnly mixin for Model %s', Model.modelName);

  // Make sure emailVerified is not set by creation
  Model.stripReadOnlyProperties = function(ctx, modelInstance, next) {
    var body = ctx.req.body;
    if (!body) {
      return next();
    }
    var properties = (Object.keys(options).length) ? options : null;
    if (properties) {
      debug('Creating %s : Read only properties are %j', 'modelInstance.Model.modelName', properties);
      Object.keys(properties).forEach(function(key) {
        debug('The \'%s\' property is readonly, removing incoming data', key);
        delete body[key];
      });
      next();
    } else {
      var err = new Error('Unable to update: ' + Model.modelName + ' is read only.');
      err.statusCode = 403;
      next(err);
    }
  };

  // Make sure emailVerified is not set by creation
  Model.beforeRemote('create', function(ctx, modelInstance, next) {
    Model.stripReadOnlyProperties(ctx, modelInstance, next);
  });
  Model.beforeRemote('upsert', function(ctx, modelInstance, next) {
    Model.stripReadOnlyProperties(ctx, modelInstance, next);
  });
  Model.beforeRemote('prototype.updateAttributes', function(ctx, modelInstance, next) {
    Model.stripReadOnlyProperties(ctx, modelInstance, next);
  });
  Model.beforeRemote('updateAll', function(ctx, modelInstance, next) {
    Model.stripReadOnlyProperties(ctx, modelInstance, next);
  });
}
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2012 KTH, Royal Institute of Technology (http://www.kth.se/)
 *
 * This file is part of tinywhiteboard.
 *
 * Tiny Whiteboard is free software: you can redistribute it and/or modify
 * it under the terms of a MIT style license which is included in
 * the software.
 */

tinyMCEPopup.requireLangPack();

/*
 * JavaScript object driving the whiteboard dialog.
 */
var TinyWhiteboardDialog = {
    url : null,
    span : null,
    drawing : false,
    context : null,
    history : [],
    scale : {
        x : 1,
        y : 1
    },

    /*
     * Start of stroke. Adds the current image to the undo history
     * and begins drawing.
     */
    drawStart : function(ev) {
        var tw = TinyWhiteboardDialog;

        tw.scale.x = ev.currentTarget.width / ev.currentTarget.offsetWidth;
        tw.scale.y = ev.currentTarget.height / ev.currentTarget.offsetHeight;

        tw.drawing = true;
        tw.history.push(tw.context.getImageData(0, 0, ev.currentTarget.offsetWidth, ev.currentTarget.offsetHeight));

        tw.context.beginPath();
        tw.context.moveTo(
            tw.scale.x * (ev.pageX - $(ev.currentTarget).offset().left),
            tw.scale.y * (ev.pageY - $(ev.currentTarget).offset().top)
        );

        $("#undo").removeClass("disabled");
    },

    /*
     * Currently in stroke.
     */
    draw : function(ev) {
        var tw = TinyWhiteboardDialog;

        if (tw.drawing) {
            ev.preventDefault();
            tw.context.lineTo(
                tw.scale.x * (ev.pageX - $(ev.currentTarget).offset().left),
                tw.scale.y * (ev.pageY - $(ev.currentTarget).offset().top)
            );
            tw.context.stroke();
        }
    },

    /*
     * Finish up the current stroke.
     */
    drawEnd : function(ev) {
        var tw = TinyWhiteboardDialog;

        if (tw.drawing) {
            tw.context.closePath();
            tw.drawing = false;
        }
    },

    /*
     * Restore data from the undo history and maintain button state.
     */
    undo : function() {
        var tw = TinyWhiteboardDialog;
        if (tw.history.length) {
            tw.context.putImageData(tw.history.pop(), 0, 0);
            tw.context.save();
        }
        if (! tw.history.length) {
            $("#undo").addClass("disabled");
        }
    },

    /*
     * Set up the window and populate data from selection in editor if any.
     */
    init : function() {
        var tw = this,
            ed = tinyMCEPopup.editor,
            canvas = $("#canvas"),
            selected;

        $("#undo")
            .click(tw.undo)
            .addClass("disabled");

        canvas
            .bind('mousedown touchstart', tw.drawStart)
            .bind('mousemove touchmove', tw.draw)
            .bind('mouseup mouseleave touchleave touchend', tw.drawEnd);

        /* Chrome cursor workaround */
        // $(document).bind('selectstart', function(ev) {
            // ev.preventDefault();
        // });

        tw.context = canvas[0].getContext('2d');
        tw.context.lineCap = "round";

        tw.selectColor("black");
        $("#pens div").click(function() {
            tw.selectColor(this.id);
        });

        tw.url = tinyMCEPopup.getWindowArg('whiteboard_url');
        tw.span = tinyMCEPopup.getWindowArg('marker');

        selected = ed.dom.get(tw.span);
        if (selected && selected.hasChildNodes()) {
            tw.context.drawImage(ed.dom.get(tw.span).firstChild, 0, 0);
        }
    },

    selectColor : function(color) {
        var tw = this,
            pen = $("#" + color);

        tw.context.strokeStyle = color;
        if (color == "white") {
            tw.context.lineWidth = 8;
        } else {
            tw.context.lineWidth = 1;
        }
        $("#pens div").removeClass("selected");
        pen.addClass("selected");
        $("#canvas")
            .css("cursor", "url(img/" + color + "-cursor.png), crosshair")
            .css("cursor:focus", "url(img/" + color + "-cursor.png), crosshair")
            .css("cursor:active", "url(img/" + color + "-cursor.png), crosshair");
    },

    /*
     * Send synchronous call to whiteboard backend service for image,
     * will update the editor if successful, indicate error states
     * otherwise.
     */
    callWhiteboard : function(img) {
        var tw = this;

        $("#inProgress").show();

        $.ajax({
                url: tw.url,
                type: 'POST',
                dataType: 'json',
                data: JSON.stringify({
                    id: $(img).attr('id'),
                    src: $(img).attr('src'),
                    width: $(img).attr('width'),
                    height: $(img).attr('height'),
                    data: $("#canvas")[0].toDataURL("image/png", 1)
                })
            })
            .done(function(d, s, xhr) {
                $("#inProgress").hide();
        		$(img)
        		  .attr('id', d.id)
        		  .attr('src', d.src)
        		  .attr('width', d.width)
        		  .attr('height', d.height)
                tw.updateEditor(img);
            })
            .fail(function(d, s, xhr) {
                alert("failed store image, please try again.");
            })
            .always(function() {
                $("#inProgress").hide();
            });
    },

    /*
     * Attempt to store the image as a data URI directly in document.
     */
    noWhiteboardFallback : function(img) {
        var tw = this;

        $(img).attr('src', $("canvas")[0].toDataURL("image/png"), 0.1);
        tw.updateEditor(img);
    },

    /*
     * Update the editor and close popup.
     */
    updateEditor : function(img) {
        var tw = this,
            ed = tinyMCEPopup.editor;

        ed.dom.setHTML(ed.dom.get(tw.span), img.outerHTML);
        ed.execCommand('mceRepaint', false);
        tinyMCEPopup.close();
    },

    /*
     * Indicates whether there is ongoing fetch activity in UI or not.
     */
    inProgress : function(inProgress) {
        $("#inProgress").toggle(inProgress);
    },

    /*
     * Callback for the insert button in dialog.
     */
    insert : function() {
        var tw = this,
            ed = tinyMCEPopup.editor,
            canvas = $("canvas"),
            img = ed.dom.create('img', {
                'alt' : 'Whiteboard image',
                'class' : 'whiteboard',
                'width' : canvas.width(),
                'height' : canvas.height()
            });

        if (tw.url != null) {
            tw.callWhiteboard(img);
        } else {
            tw.noWhiteboardFallback(img);
        }
    }
};

tinyMCEPopup.onInit.add(TinyWhiteboardDialog.init, TinyWhiteboardDialog);
<commit_msg>Try to get x,y on touch and add lots of debugging output.<commit_after>/*
 * Copyright (C) 2012 KTH, Royal Institute of Technology (http://www.kth.se/)
 *
 * This file is part of tinywhiteboard.
 *
 * Tiny Whiteboard is free software: you can redistribute it and/or modify
 * it under the terms of a MIT style license which is included in
 * the software.
 */

tinyMCEPopup.requireLangPack();

/*
 * JavaScript object driving the whiteboard dialog.
 */
var TinyWhiteboardDialog = {
    url : null,
    span : null,
    drawing : false,
    context : null,
    history : [],
    scale : {
        x : 1,
        y : 1
    },

    getX : function (ev) {
        if (typeof ev.pageX === "undefined") {
            return ev.touches[0].pageX;
        } else {
            return ev.pageX;
        }
    },

    getY : function (ev) {
        if (typeof ev.pageY === "undefined") {
            return ev.touches[0].pageY;
        } else {
            return ev.pageY;
        }
    },

    /*
     * Start of stroke. Adds the current image to the undo history
     * and begins drawing.
     */
    drawStart : function(ev) {
        var tw = TinyWhiteboardDialog;

        tw.scale.x = ev.currentTarget.width / ev.currentTarget.offsetWidth;
        tw.scale.y = ev.currentTarget.height / ev.currentTarget.offsetHeight;

        tw.drawing = true;
        tw.history.push(tw.context.getImageData(0, 0, ev.currentTarget.offsetWidth, ev.currentTarget.offsetHeight));

        tw.context.beginPath();
        tw.context.moveTo(
            tw.scale.x * (tw.getX(ev) - $(ev.currentTarget).offset().left),
            tw.scale.y * (tw.getY(ev) - $(ev.currentTarget).offset().top));

        $("#undo").removeClass("disabled");
    },

    /*
     * Currently in stroke.
     */
    draw : function(ev) {
        var tw = TinyWhiteboardDialog,
            x = tw.scale.x * (tw.getX(ev) - $(ev.currentTarget).offset().left),
            y = tw.scale.y * (tw.getY(ev) - $(ev.currentTarget).offset().top);

        console.log("********************");
        console.log(ev.pageX);
        console.log(ev.pageY);
        console.log($(ev.currentTarget).offset().left);
        console.log($(ev.currentTarget).offset().top);
        console.log("drawing: " + tw.drawing + " " + x + "," + y);

        if (tw.drawing) {
            ev.preventDefault();
            tw.context.lineTo(
                tw.scale.x * (tw.getX(ev) - $(ev.currentTarget).offset().left),
                tw.scale.y * (tw.getY(ev) - $(ev.currentTarget).offset().top)
            );
            tw.context.stroke();
        }
    },

    /*
     * Finish up the current stroke.
     */
    drawEnd : function(ev) {
        var tw = TinyWhiteboardDialog;

        if (tw.drawing) {
            tw.context.closePath();
            tw.drawing = false;
        }
    },

    /*
     * Restore data from the undo history and maintain button state.
     */
    undo : function() {
        var tw = TinyWhiteboardDialog;
        if (tw.history.length) {
            tw.context.putImageData(tw.history.pop(), 0, 0);
            tw.context.save();
        }
        if (! tw.history.length) {
            $("#undo").addClass("disabled");
        }
    },

    /*
     * Set up the window and populate data from selection in editor if any.
     */
    init : function() {
        var tw = this,
            ed = tinyMCEPopup.editor,
            canvas = $("#canvas"),
            selected;

        $("#undo")
            .click(tw.undo)
            .addClass("disabled");

        canvas
            .bind('mousedown touchstart', tw.drawStart)
            .bind('mousemove touchmove', tw.draw)
            .bind('mouseup mouseleave touchleave touchend', tw.drawEnd);

        /* Chrome cursor workaround */
        // $(document).bind('selectstart', function(ev) {
            // ev.preventDefault();
        // });

        tw.context = canvas[0].getContext('2d');
        tw.context.lineCap = "round";

        tw.selectColor("black");
        $("#pens div").click(function() {
            tw.selectColor(this.id);
        });

        tw.url = tinyMCEPopup.getWindowArg('whiteboard_url');
        tw.span = tinyMCEPopup.getWindowArg('marker');

        selected = ed.dom.get(tw.span);
        if (selected && selected.hasChildNodes()) {
            tw.context.drawImage(ed.dom.get(tw.span).firstChild, 0, 0);
        }
    },

    selectColor : function(color) {
        var tw = this,
            pen = $("#" + color);

        tw.context.strokeStyle = color;
        if (color == "white") {
            tw.context.lineWidth = 8;
        } else {
            tw.context.lineWidth = 1;
        }
        $("#pens div").removeClass("selected");
        pen.addClass("selected");
        $("#canvas")
            .css("cursor", "url(img/" + color + "-cursor.png), crosshair")
            .css("cursor:focus", "url(img/" + color + "-cursor.png), crosshair")
            .css("cursor:active", "url(img/" + color + "-cursor.png), crosshair");
    },

    /*
     * Send synchronous call to whiteboard backend service for image,
     * will update the editor if successful, indicate error states
     * otherwise.
     */
    callWhiteboard : function(img) {
        var tw = this;

        $("#inProgress").show();

        $.ajax({
                url: tw.url,
                type: 'POST',
                dataType: 'json',
                data: JSON.stringify({
                    id: $(img).attr('id'),
                    src: $(img).attr('src'),
                    width: $(img).attr('width'),
                    height: $(img).attr('height'),
                    data: $("#canvas")[0].toDataURL("image/png", 1)
                })
            })
            .done(function(d, s, xhr) {
                $("#inProgress").hide();
        		$(img)
        		  .attr('id', d.id)
        		  .attr('src', d.src)
        		  .attr('width', d.width)
        		  .attr('height', d.height)
                tw.updateEditor(img);
            })
            .fail(function(d, s, xhr) {
                alert("failed store image, please try again.");
            })
            .always(function() {
                $("#inProgress").hide();
            });
    },

    /*
     * Attempt to store the image as a data URI directly in document.
     */
    noWhiteboardFallback : function(img) {
        var tw = this;

        $(img).attr('src', $("canvas")[0].toDataURL("image/png"), 0.1);
        tw.updateEditor(img);
    },

    /*
     * Update the editor and close popup.
     */
    updateEditor : function(img) {
        var tw = this,
            ed = tinyMCEPopup.editor;

        ed.dom.setHTML(ed.dom.get(tw.span), img.outerHTML);
        ed.execCommand('mceRepaint', false);
        tinyMCEPopup.close();
    },

    /*
     * Indicates whether there is ongoing fetch activity in UI or not.
     */
    inProgress : function(inProgress) {
        $("#inProgress").toggle(inProgress);
    },

    /*
     * Callback for the insert button in dialog.
     */
    insert : function() {
        var tw = this,
            ed = tinyMCEPopup.editor,
            canvas = $("canvas"),
            img = ed.dom.create('img', {
                'alt' : 'Whiteboard image',
                'class' : 'whiteboard',
                'width' : canvas.width(),
                'height' : canvas.height()
            });

        if (tw.url != null) {
            tw.callWhiteboard(img);
        } else {
            tw.noWhiteboardFallback(img);
        }
    }
};

tinyMCEPopup.onInit.add(TinyWhiteboardDialog.init, TinyWhiteboardDialog);
<|endoftext|>