<commit_before>(function(global) {

  /**
   * Class: RemoteStorage.IndexedDB
   *
   *
   * IndexedDB Interface
   * -------------------
   *
   * TODO rewrite, doesn't expose GPD anymore, it's in cachinglayer now
   *
   * This file exposes a get/put/delete interface, accessing data in an IndexedDB.
   *
   * There are multiple parts to this interface:
   *
   *   The RemoteStorage integration:
   *     - RemoteStorage.IndexedDB._rs_supported() determines if IndexedDB support
   *       is available. If it isn't, RemoteStorage won't initialize the feature.
   *     - RemoteStorage.IndexedDB._rs_init() initializes the feature. It returns
   *       a promise that is fulfilled as soon as the database has been opened and
   *       migrated.
   *
   *   The storage interface (RemoteStorage.IndexedDB object):
   *     - Usually this is accessible via "remoteStorage.local"
   *     - #get() takes a path and returns a promise.
   *     - #put() takes a path, body and contentType and also returns a promise.
   *     - #delete() takes a path and also returns a promise.
   *     - #on('change', ...) events, being fired whenever something changes in
   *       the storage. Change events roughly follow the StorageEvent pattern.
   *       They have "oldValue" and "newValue" properties, which can be used to
   *       distinguish create/update/delete operations and analyze changes in
   *       change handlers. In addition they carry a "origin" property, which
   *       is either "window", "local", or "remote". "remote" events are fired
   *       whenever a change comes in from RemoteStorage.Sync. In the future,
   *       "device" origin events will also be fired for changes happening in
   *       other windows on the same device.
   *
   *   The sync interface (also on RemoteStorage.IndexedDB object):
   *     - #getNodes([paths]) returns the requested nodes in a promise.
   *     - #setNodes(map) stores all the nodes given in the (path -> node) map.
   *
   */

  var RS = RemoteStorage;

  var DB_VERSION = 2;

  var DEFAULT_DB_NAME = 'remotestorage';
  var DEFAULT_DB;

  RS.IndexedDB = function(database) {
    this.db = database || DEFAULT_DB;

    if (!this.db) {
      RemoteStorage.log("[IndexedDB] Failed to open DB");
      return undefined;
    }

    RS.cachingLayer(this);
    RS.eventHandling(this, 'change');

    this.getsRunning = 0;
    this.putsRunning = 0;
  };

  RS.IndexedDB.prototype = {
    getNodes: function(paths) {
      var promise = promising();
      var transaction = this.db.transaction(['nodes'], 'readonly');
      var nodes = transaction.objectStore('nodes');
      var retrievedNodes = {};
      var startTime = new Date().getTime();

      this.getsRunning++;

      for (var i=0; i<paths.length; i++) {
        (function(index) {
          var path = paths[index];
          nodes.get(path).onsuccess = function(evt) {
            retrievedNodes[path] = evt.target.result;
          };
        })(i);
      }

      transaction.oncomplete = function() {
        promise.fulfill(retrievedNodes);
        this.getsRunning--;
      }.bind(this);

      transaction.onerror = transaction.onabort = function() {
        promise.reject('get transaction error/abort');
        this.getsRunning--;
      }.bind(this);

      return promise;
    },

    setNodes: function(nodes) {
      var promise = promising();
      var transaction = this.db.transaction(['nodes'], 'readwrite');
      var nodesStore = transaction.objectStore('nodes');
      var startTime = new Date().getTime();

      this.putsRunning++;

      RemoteStorage.log('[IndexedDB] Starting put', nodes, this.putsRunning);

      for (var path in nodes) {
        var node = nodes[path];
        if(typeof(node) === 'object') {
          try {
            nodesStore.put(node);
          } catch(e) {
            RemoteStorage.log('[IndexedDB] Error while putting', node, e);
            throw e;
          }
        } else {
          try {
            nodesStore.delete(path);
          } catch(e) {
            RemoteStorage.log('[IndexedDB] Error while removing', nodesStore, node, e);
            throw e;
          }
        }
      }

      transaction.oncomplete = function() {
        promise.fulfill();
        this.putsRunning--;
        RemoteStorage.log('[IndexedDB] Finished put', nodes, this.putsRunning, (new Date().getTime() - startTime)+'ms');
      }.bind(this);

      transaction.onerror = function() {
        promise.reject('transaction error');
        this.putsRunning--;
      }.bind(this);

      transaction.onabort = function() {
        promise.reject('transaction abort');
        this.putsRunning--;
      }.bind(this);

      return promise;
    },

    reset: function(callback) {
      var dbName = this.db.name;
      var self = this;

      this.db.close();

      RS.IndexedDB.clean(this.db.name, function() {
        RS.IndexedDB.open(dbName, function(other) {
          // hacky!
          self.db = other.db;
          callback(self);
        });
      });
    },

    forAllNodes: function(cb) {
      var promise = promising();
      var transaction = this.db.transaction(['nodes'], 'readonly');
      var cursorReq = transaction.objectStore('nodes').openCursor();

      cursorReq.onsuccess = function(evt) {
        var cursor = evt.target.result;

        if (cursor) {
          cb(this.migrate(cursor.value));
          cursor.continue();
        } else {
          promise.fulfill();
        }
      }.bind(this);

      return promise;
    },

    closeDB: function() {
      this.db.close();
    }

  };

  RS.IndexedDB.open = function(name, callback) {
    var timer = setTimeout(function() {
      callback("timeout trying to open db");
    }, 10000);

    var req = indexedDB.open(name, DB_VERSION);

    req.onerror = function() {
      RemoteStorage.log('[IndexedDB] Opening DB failed', req);

      clearTimeout(timer);
      callback(req.error);
    };

    req.onupgradeneeded = function(event) {
      var db = req.result;

      RemoteStorage.log("[IndexedDB] Upgrade: from ", event.oldVersion, " to ", event.newVersion);

      if (event.oldVersion !== 1) {
        RemoteStorage.log("[IndexedDB] Creating object store: nodes");
        db.createObjectStore('nodes', { keyPath: 'path' });
      }

      RemoteStorage.log("[IndexedDB] Creating object store: changes");

      db.createObjectStore('changes', { keyPath: 'path' });
    };

    req.onsuccess = function() {
      clearTimeout(timer);
      callback(null, req.result);
    };
  };

  RS.IndexedDB.clean = function(databaseName, callback) {
    var req = indexedDB.deleteDatabase(databaseName);

    req.onsuccess = function() {
      RemoteStorage.log('[IndexedDB] Done removing DB');
      callback();
    };

    req.onerror = req.onabort = function(evt) {
      console.error('Failed to remove database "' + databaseName + '"', evt);
    };
  };

  RS.IndexedDB._rs_init = function(remoteStorage) {
    var promise = promising();

    RS.IndexedDB.open(DEFAULT_DB_NAME, function(err, db) {
      if (err) {
        promise.reject(err);
      } else {
        DEFAULT_DB = db;
        db.onerror = function() { remoteStorage._emit('error', err); };
        promise.fulfill();
      }
    });

    return promise;
  };

  RS.IndexedDB._rs_supported = function() {
    var promise = promising();

    if ('indexedDB' in global) {
      try {
        var check = indexedDB.open("MyTestDatabase");
        check.onerror = function(event) {
          promise.reject();
        };
        check.onsuccess = function(event) {
          promise.fulfill();
        };
      } catch(e) {
        promise.reject();
      }
    } else {
      promise.reject();
    }

    return promise;
  };

  RS.IndexedDB._rs_cleanup = function(remoteStorage) {
    var promise = promising();

    if (remoteStorage.local) {
      remoteStorage.local.closeDB();
    }

    RS.IndexedDB.clean(DEFAULT_DB_NAME, function() {
      promise.fulfill();
    });

    return promise;
  };

})(typeof(window) !== 'undefined' ? window : global);
<commit_msg>Fix error in RS.IndexedDB.reset (refs #630)<commit_after>(function(global) {

  /**
   * Class: RemoteStorage.IndexedDB
   *
   *
   * IndexedDB Interface
   * -------------------
   *
   * TODO rewrite, doesn't expose GPD anymore, it's in cachinglayer now
   *
   * This file exposes a get/put/delete interface, accessing data in an IndexedDB.
   *
   * There are multiple parts to this interface:
   *
   *   The RemoteStorage integration:
   *     - RemoteStorage.IndexedDB._rs_supported() determines if IndexedDB support
   *       is available. If it isn't, RemoteStorage won't initialize the feature.
   *     - RemoteStorage.IndexedDB._rs_init() initializes the feature. It returns
   *       a promise that is fulfilled as soon as the database has been opened and
   *       migrated.
   *
   *   The storage interface (RemoteStorage.IndexedDB object):
   *     - Usually this is accessible via "remoteStorage.local"
   *     - #get() takes a path and returns a promise.
   *     - #put() takes a path, body and contentType and also returns a promise.
   *     - #delete() takes a path and also returns a promise.
   *     - #on('change', ...) events, being fired whenever something changes in
   *       the storage. Change events roughly follow the StorageEvent pattern.
   *       They have "oldValue" and "newValue" properties, which can be used to
   *       distinguish create/update/delete operations and analyze changes in
   *       change handlers. In addition they carry a "origin" property, which
   *       is either "window", "local", or "remote". "remote" events are fired
   *       whenever a change comes in from RemoteStorage.Sync. In the future,
   *       "device" origin events will also be fired for changes happening in
   *       other windows on the same device.
   *
   *   The sync interface (also on RemoteStorage.IndexedDB object):
   *     - #getNodes([paths]) returns the requested nodes in a promise.
   *     - #setNodes(map) stores all the nodes given in the (path -> node) map.
   *
   */

  var RS = RemoteStorage;

  var DB_VERSION = 2;

  var DEFAULT_DB_NAME = 'remotestorage';
  var DEFAULT_DB;

  RS.IndexedDB = function(database) {
    this.db = database || DEFAULT_DB;

    if (!this.db) {
      RemoteStorage.log("[IndexedDB] Failed to open DB");
      return undefined;
    }

    RS.cachingLayer(this);
    RS.eventHandling(this, 'change');

    this.getsRunning = 0;
    this.putsRunning = 0;
  };

  RS.IndexedDB.prototype = {
    getNodes: function(paths) {
      var promise = promising();
      var transaction = this.db.transaction(['nodes'], 'readonly');
      var nodes = transaction.objectStore('nodes');
      var retrievedNodes = {};
      var startTime = new Date().getTime();

      this.getsRunning++;

      for (var i=0; i<paths.length; i++) {
        (function(index) {
          var path = paths[index];
          nodes.get(path).onsuccess = function(evt) {
            retrievedNodes[path] = evt.target.result;
          };
        })(i);
      }

      transaction.oncomplete = function() {
        promise.fulfill(retrievedNodes);
        this.getsRunning--;
      }.bind(this);

      transaction.onerror = transaction.onabort = function() {
        promise.reject('get transaction error/abort');
        this.getsRunning--;
      }.bind(this);

      return promise;
    },

    setNodes: function(nodes) {
      var promise = promising();
      var transaction = this.db.transaction(['nodes'], 'readwrite');
      var nodesStore = transaction.objectStore('nodes');
      var startTime = new Date().getTime();

      this.putsRunning++;

      RemoteStorage.log('[IndexedDB] Starting put', nodes, this.putsRunning);

      for (var path in nodes) {
        var node = nodes[path];
        if(typeof(node) === 'object') {
          try {
            nodesStore.put(node);
          } catch(e) {
            RemoteStorage.log('[IndexedDB] Error while putting', node, e);
            throw e;
          }
        } else {
          try {
            nodesStore.delete(path);
          } catch(e) {
            RemoteStorage.log('[IndexedDB] Error while removing', nodesStore, node, e);
            throw e;
          }
        }
      }

      transaction.oncomplete = function() {
        promise.fulfill();
        this.putsRunning--;
        RemoteStorage.log('[IndexedDB] Finished put', nodes, this.putsRunning, (new Date().getTime() - startTime)+'ms');
      }.bind(this);

      transaction.onerror = function() {
        promise.reject('transaction error');
        this.putsRunning--;
      }.bind(this);

      transaction.onabort = function() {
        promise.reject('transaction abort');
        this.putsRunning--;
      }.bind(this);

      return promise;
    },

    reset: function(callback) {
      var dbName = this.db.name;
      var self = this;

      this.db.close();

      RS.IndexedDB.clean(this.db.name, function() {
        RS.IndexedDB.open(dbName, function(err, other) {
          if (err) {
            RemoteStorage.log('[IndexedDB] Error while resetting local storage', err);
          } else {
            // hacky!
            self.db = other;
          }
          if (typeof callback === 'function') { callback(self); }
        });
      });
    },

    forAllNodes: function(cb) {
      var promise = promising();
      var transaction = this.db.transaction(['nodes'], 'readonly');
      var cursorReq = transaction.objectStore('nodes').openCursor();

      cursorReq.onsuccess = function(evt) {
        var cursor = evt.target.result;

        if (cursor) {
          cb(this.migrate(cursor.value));
          cursor.continue();
        } else {
          promise.fulfill();
        }
      }.bind(this);

      return promise;
    },

    closeDB: function() {
      this.db.close();
    }

  };

  RS.IndexedDB.open = function(name, callback) {
    var timer = setTimeout(function() {
      callback("timeout trying to open db");
    }, 10000);

    var req = indexedDB.open(name, DB_VERSION);

    req.onerror = function() {
      RemoteStorage.log('[IndexedDB] Opening DB failed', req);

      clearTimeout(timer);
      callback(req.error);
    };

    req.onupgradeneeded = function(event) {
      var db = req.result;

      RemoteStorage.log("[IndexedDB] Upgrade: from ", event.oldVersion, " to ", event.newVersion);

      if (event.oldVersion !== 1) {
        RemoteStorage.log("[IndexedDB] Creating object store: nodes");
        db.createObjectStore('nodes', { keyPath: 'path' });
      }

      RemoteStorage.log("[IndexedDB] Creating object store: changes");

      db.createObjectStore('changes', { keyPath: 'path' });
    };

    req.onsuccess = function() {
      clearTimeout(timer);
      callback(null, req.result);
    };
  };

  RS.IndexedDB.clean = function(databaseName, callback) {
    var req = indexedDB.deleteDatabase(databaseName);

    req.onsuccess = function() {
      RemoteStorage.log('[IndexedDB] Done removing DB');
      callback();
    };

    req.onerror = req.onabort = function(evt) {
      console.error('Failed to remove database "' + databaseName + '"', evt);
    };
  };

  RS.IndexedDB._rs_init = function(remoteStorage) {
    var promise = promising();

    RS.IndexedDB.open(DEFAULT_DB_NAME, function(err, db) {
      if (err) {
        promise.reject(err);
      } else {
        DEFAULT_DB = db;
        db.onerror = function() { remoteStorage._emit('error', err); };
        promise.fulfill();
      }
    });

    return promise;
  };

  RS.IndexedDB._rs_supported = function() {
    var promise = promising();

    if ('indexedDB' in global) {
      try {
        var check = indexedDB.open("MyTestDatabase");
        check.onerror = function(event) {
          promise.reject();
        };
        check.onsuccess = function(event) {
          promise.fulfill();
        };
      } catch(e) {
        promise.reject();
      }
    } else {
      promise.reject();
    }

    return promise;
  };

  RS.IndexedDB._rs_cleanup = function(remoteStorage) {
    var promise = promising();

    if (remoteStorage.local) {
      remoteStorage.local.closeDB();
    }

    RS.IndexedDB.clean(DEFAULT_DB_NAME, function() {
      promise.fulfill();
    });

    return promise;
  };

})(typeof(window) !== 'undefined' ? window : global);
<|endoftext|>


<commit_before>import { create, createFromBrowser } from './actions'
import reducer from './reducer'

export function defaultInitState(location, title) {
  const action = create(location, title)
  const state = reducer(undefined, action)
  return state
}
export function stateFromBrowser(historyState) {
  const action = createFromBrowser(historyState)
  const state = reducer(undefined, action)
  state.refresh = true
  return state
}
export default function getInitState(location, title, history) {
  if (history.state && history.state.key) return stateFromBrowser(history.state)
  return defaultInitState(location, title)
}
<commit_msg>use browserHasHistory()<commit_after>import { create, createFromBrowser } from './actions'
import reducer from './reducer'
import { browserHasHistory } from './select'

export function defaultInitState(location, title) {
  const action = create(location, title)
  const state = reducer(undefined, action)
  return state
}
export function stateFromBrowser(historyState) {
  const action = createFromBrowser(historyState)
  const state = reducer(undefined, action)
  return state
}
export default function getInitState(location, title, history) {
  if (browserHasHistory(history)) return stateFromBrowser(history.state)
  return defaultInitState(location, title)
}
<|endoftext|>


<commit_before>var h = require('html')
var replaceBBox = require('replace-bbox')
var Rect = require('rect')

function Inspector(editor) {
  this.editor = editor
  this.selectedObject = null

  this.el = h('.inspector panel', [
    h('.panel-header'),
    h('.panel-content', [
      h('section.matrix', [
        h('.row', [
          h('label', ['Position']),
          h('.field', [
            this.inputX = h('input', {type: 'number', disabled: true}),
            h('label', ['X'])
          ]),
          h('.mid'),
          h('.field', [
            this.inputY = h('input', {type: 'number', disabled: true}),
            h('label', ['Y'])
          ])
        ]),
        h('.row', [
          h('label', ['Size']),
          h('.field', [
            this.inputWidth = h('input', {type: 'number', disabled: true}),
            h('label', ['Width'])
          ]),
          h('.mid'),
          h('.field', [
            this.inputHeight = h('input', {type: 'number', disabled: true}),
            h('label', ['Height'])
          ])
        ]),
        h('.row', [
          h('label', ['Transform']),
          h('.field', [
            this.inputRotate = h('input', {type: 'number', disabled: true}),
            h('label', ['Rotate'])
          ]),
          h('.mid'),
          h('.field', [
            h('.input', [
              h('.segmented', [
                this.buttonFlipH = h('button.icon16', {disabled: true}),
                this.buttonFlipV = h('button.icon16', {disabled: true})
              ])
            ]),
            h('label', ['Flip'])
          ])
        ])
      ]),
      h('section.matrix', [
        h('.row', [
          h('label', ['Opacity']),
          h('.slider', [
            this.inputOpacitySlider = h('input', {type: 'range', value: 0, min: 0, max: 100, disabled: true})
          ]),
          h('.field', [
            this.inputOpacity = h('input', {type: 'number', min: 0, max: 100, disabled: true})
          ])
        ]),
        h('.row', [
          h('label', ['Blending']),
          h('.field', [
            this.inputBlending = h('select', {disabled: true}, [
              h('option', ['Normal']),
              h('hr'),
              h('option', ['Darken']),
              h('option', ['Multiply']),
              h('option', ['Color Burn']),
              h('hr'),
              h('option', ['Lighten']),
              h('option', ['Screen']),
              h('option', ['Color Dodge']),
              h('hr'),
              h('option', ['Overlay']),
              h('option', ['Soft Light']),
              h('option', ['Hard Light']),
              h('hr'),
              h('option', ['Difference']),
              h('option', ['Exclusion']),
              h('hr'),
              h('option', ['Hue']),
              h('option', ['Saturation']),
              h('option', ['Color']),
              h('option', ['Luminosity'])
            ])
          ])
        ])
      ]),
      h('section', [h('h1', ['Fills'])]),
      h('section', [h('h1', ['Borders'])]),
      h('section', [h('h1', ['Shadows'])]),
      h('section', [h('h1', ['Inner Shadows'])]),
      h('section', [h('h1', ['Gaussian Blur'])]),
    ]),
    h('.panel-footer')
  ])

  this.controls = [this.inputX, this.inputY, this.inputWidth, this.inputHeight, this.inputRotate, this.buttonFlipH, this.buttonFlipV, this.inputOpacitySlider, this.inputOpacity, this.inputBlending]

  this.updateBBox = this.updateBBox.bind(this)

  ;[this.inputX, this.inputY, this.inputWidth, this.inputHeight].forEach(function(input) {
    input.addEventListener('input', this.updateBBox)
  }, this)
  this.inputOpacity.addEventListener('input', this.updateOpacity.bind(this))
  this.inputOpacitySlider.addEventListener('input', this.updateOpacityFromSlider.bind(this))
}

Inspector.prototype.selectObject = function(object) {
  this.selectedObject = object
  this.setUpFields()
  this.updateFields()
}

Inspector.prototype.setUpFields = function() {
  var object = this.selectedObject
  if (!object) {
    this.controls.forEach(function(control) {
      control.disabled = true
      control.value =
        control === this.inputBlending ? 'Normal' :
        control === this.inputOpacitySlider ? 0 : ''
    }, this)
    return
  }
  this.controls.forEach(function(control) {control.disabled = false})
}

Inspector.prototype.updateFields = function() {
  var object = this.selectedObject
  if (!object) {
    return
  }

  var bb = object.getBBox().floor()
  this.inputX.valueAsNumber = bb.x
  this.inputY.valueAsNumber = bb.y
  this.inputWidth.valueAsNumber = bb.width
  this.inputHeight.valueAsNumber = bb.height

  this.inputOpacity.valueAsNumber =
  this.inputOpacitySlider.valueAsNumber = getComputedStyle(object).opacity * 100
}

Inspector.prototype.updateBBox = function() {
  var object = this.selectedObject
  if (!object || !this.inputX.value || !this.inputY.value || !this.inputWidth.value || !this.inputHeight.value) return
  replaceBBox(object, Rect.normalized(this.inputX.valueAsNumber, this.inputY.valueAsNumber, this.inputWidth.valueAsNumber, this.inputHeight.valueAsNumber))
  this.editor.canvas.updateSelectionBox()
}

Inspector.prototype.updateOpacity = function() {
  var object = this.selectedObject
  if (!object) return

  var value = Math.max(0, Math.min(100, this.inputOpacity.valueAsNumber))
  object.style.opacity = value / 100
  this.inputOpacitySlider.valueAsNumber = value
}

Inspector.prototype.updateOpacityFromSlider = function() {
  var object = this.selectedObject
  if (!object) return

  var value = Math.max(0, Math.min(100, this.inputOpacitySlider.valueAsNumber))
  object.style.opacity = value / 100
  this.inputOpacity.valueAsNumber = value
}

exports = Inspector
<commit_msg>Implement blending in inspector<commit_after>var h = require('html')
var replaceBBox = require('replace-bbox')
var Rect = require('rect')

function Inspector(editor) {
  this.editor = editor
  this.selectedObject = null

  this.el = h('.inspector panel', [
    h('.panel-header'),
    h('.panel-content', [
      h('section.matrix', [
        h('.row', [
          h('label', ['Position']),
          h('.field', [
            this.inputX = h('input', {type: 'number', disabled: true}),
            h('label', ['X'])
          ]),
          h('.mid'),
          h('.field', [
            this.inputY = h('input', {type: 'number', disabled: true}),
            h('label', ['Y'])
          ])
        ]),
        h('.row', [
          h('label', ['Size']),
          h('.field', [
            this.inputWidth = h('input', {type: 'number', disabled: true}),
            h('label', ['Width'])
          ]),
          h('.mid'),
          h('.field', [
            this.inputHeight = h('input', {type: 'number', disabled: true}),
            h('label', ['Height'])
          ])
        ]),
        h('.row', [
          h('label', ['Transform']),
          h('.field', [
            this.inputRotate = h('input', {type: 'number', disabled: true}),
            h('label', ['Rotate'])
          ]),
          h('.mid'),
          h('.field', [
            h('.input', [
              h('.segmented', [
                this.buttonFlipH = h('button.icon16', {disabled: true}),
                this.buttonFlipV = h('button.icon16', {disabled: true})
              ])
            ]),
            h('label', ['Flip'])
          ])
        ])
      ]),
      h('section.matrix', [
        h('.row', [
          h('label', ['Opacity']),
          h('.slider', [
            this.inputOpacitySlider = h('input', {type: 'range', value: 0, min: 0, max: 100, disabled: true})
          ]),
          h('.field', [
            this.inputOpacity = h('input', {type: 'number', min: 0, max: 100, disabled: true})
          ])
        ]),
        h('.row', [
          h('label', ['Blending']),
          h('.field', [
            this.inputBlending = h('select', {disabled: true}, [
              h('option', {value: 'normal'}, ['Normal']),
              h('hr'),
              h('option', {value: 'darken'}, ['Darken']),
              h('option', {value: 'multiply'}, ['Multiply']),
              h('option', {value: 'color-burn'}, ['Color Burn']),
              h('hr'),
              h('option', {value: 'lighten'}, ['Lighten']),
              h('option', {value: 'screen'}, ['Screen']),
              h('option', {value: 'color-dodge'}, ['Color Dodge']),
              h('hr'),
              h('option', {value: 'overlay'}, ['Overlay']),
              h('option', {value: 'soft-light'}, ['Soft Light']),
              h('option', {value: 'hard-light'}, ['Hard Light']),
              h('hr'),
              h('option', {value: 'difference'}, ['Difference']),
              h('option', {value: 'exclusion'}, ['Exclusion']),
              h('hr'),
              h('option', {value: 'hue'}, ['Hue']),
              h('option', {value: 'saturation'}, ['Saturation']),
              h('option', {value: 'color'}, ['Color']),
              h('option', {value: 'luminosity'}, ['Luminosity'])
            ])
          ])
        ])
      ]),
      h('section', [h('h1', ['Fills'])]),
      h('section', [h('h1', ['Borders'])]),
      h('section', [h('h1', ['Shadows'])]),
      h('section', [h('h1', ['Inner Shadows'])]),
      h('section', [h('h1', ['Gaussian Blur'])]),
    ]),
    h('.panel-footer')
  ])

  this.controls = [this.inputX, this.inputY, this.inputWidth, this.inputHeight, this.inputRotate, this.buttonFlipH, this.buttonFlipV, this.inputOpacitySlider, this.inputOpacity, this.inputBlending]

  this.updateBBox = this.updateBBox.bind(this)

  ;[this.inputX, this.inputY, this.inputWidth, this.inputHeight].forEach(function(input) {
    input.addEventListener('input', this.updateBBox)
  }, this)
  this.inputOpacity.addEventListener('input', this.updateOpacity.bind(this))
  this.inputOpacitySlider.addEventListener('input', this.updateOpacityFromSlider.bind(this))
  this.inputBlending.addEventListener('input', this.updateBlending.bind(this))
}

Inspector.prototype.selectObject = function(object) {
  this.selectedObject = object
  this.setUpFields()
  this.updateFields()
}

Inspector.prototype.setUpFields = function() {
  var object = this.selectedObject
  if (!object) {
    this.controls.forEach(function(control) {
      control.disabled = true
      control.value =
        control === this.inputBlending ? 'normal' :
        control === this.inputOpacitySlider ? 0 : ''
    }, this)
    return
  }
  this.controls.forEach(function(control) {control.disabled = false})
}

Inspector.prototype.updateFields = function() {
  var object = this.selectedObject
  if (!object) {
    return
  }

  var bb = object.getBBox().floor()
  var style = getComputedStyle(object)

  this.inputX.valueAsNumber = bb.x
  this.inputY.valueAsNumber = bb.y
  this.inputWidth.valueAsNumber = bb.width
  this.inputHeight.valueAsNumber = bb.height

  this.inputOpacity.valueAsNumber =
  this.inputOpacitySlider.valueAsNumber = style.opacity * 100

  this.inputBlending.value = style.mixBlendMode
}

Inspector.prototype.updateBBox = function() {
  var object = this.selectedObject
  if (!object || !this.inputX.value || !this.inputY.value || !this.inputWidth.value || !this.inputHeight.value) return
  replaceBBox(object, Rect.normalized(this.inputX.valueAsNumber, this.inputY.valueAsNumber, this.inputWidth.valueAsNumber, this.inputHeight.valueAsNumber))
  this.editor.canvas.updateSelectionBox()
}

Inspector.prototype.updateOpacity = function() {
  var object = this.selectedObject
  if (!object) return

  var value = Math.max(0, Math.min(100, this.inputOpacity.valueAsNumber))
  object.style.opacity = value / 100
  this.inputOpacitySlider.valueAsNumber = value
}

Inspector.prototype.updateOpacityFromSlider = function() {
  var object = this.selectedObject
  if (!object) return

  var value = Math.max(0, Math.min(100, this.inputOpacitySlider.valueAsNumber))
  object.style.opacity = value / 100
  this.inputOpacity.valueAsNumber = value
}

Inspector.prototype.updateBlending = function() {
  var object = this.selectedObject
  if (!object) return

  object.style.mixBlendMode = this.inputBlending.value
}

exports = Inspector
<|endoftext|>


<commit_before>ScratchExtensions.loadExternalJS('http://www.googledrive.com/host/0BwZCk9UtDHYoTHdDUGlFa0Q2aXM');
ScratchExtensions.loadExternalJS('http://www.googledrive.com/host/0BwZCk9UtDHYoUHdSQklzYjlkeHM');
ScratchExtensions.loadExternalJS('http://www.googledrive.com/host/0BwZCk9UtDHYoTkUwLXIxNE1SN1k');
ScratchExtensions.loadExternalJS('http://www.googledrive.com/host/0BwZCk9UtDHYod0lMV21iaHRadFE');
ScratchExtensions.loadExternalJS('http://www.googledrive.com/host/0BwZCk9UtDHYoaGxZMXcwRlplRVU');
ScratchExtensions.loadExternalJS('http://www.googledrive.com/host/0BwZCk9UtDHYoenZTdk9uT1R3WDQ');
$(".box-head > .buttons").append('
Scratch Boost
');
var inf = "";
function info() {
    if (inf === "") {
        inf = $("#info").html();
        $("#info").html('

Scratch Boost Info

Scratch Boost by: iwotastic

');
      }else{
        $("#info").html(inf);
        inf = "";
    }
}
<commit_msg>Update installer.js<commit_after>ScratchExtensions.loadExternalJS('https://raw.githubusercontent.com/Iwotastic/Scratch-Boost/master/src/strings.js');
ScratchExtensions.loadExternalJS('https://raw.githubusercontent.com/Iwotastic/Scratch-Boost/master/src/strings.js');
ScratchExtensions.loadExternalJS('https://raw.githubusercontent.com/Iwotastic/Scratch-Boost/master/src/strings.js');
ScratchExtensions.loadExternalJS('https://raw.githubusercontent.com/Iwotastic/Scratch-Boost/master/src/strings.js');
ScratchExtensions.loadExternalJS('https://raw.githubusercontent.com/Iwotastic/Scratch-Boost/master/src/strings.js');
ScratchExtensions.loadExternalJS('https://raw.githubusercontent.com/Iwotastic/Scratch-Boost/master/src/strings.js');
$(".box-head > .buttons").append('
Scratch Boost
');
var inf = "";
function info() {
    if (inf === "") {
        inf = $("#info").html();
        $("#info").html('

Scratch Boost Info

Scratch Boost by: iwotastic

');
      }else{
        $("#info").html(inf);
        inf = "";
    }
}
<|endoftext|>


<commit_before>'use strict'

var _ = require('lodash')
var asar = require('asar')
var async = require('async')
var child = require('child_process')
var debug = require('debug')
var fs = require('fs-extra')
var fsize = require('get-folder-size')
var glob = require('glob')
var path = require('path')
var temp = require('temp').track()
var wrap = require('word-wrap')

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

var defaultLogger = debug(pkg.name)

var defaultRename = function (dest, src) {
  return path.join(dest, '<%= name %>_<%= version %>_<%= arch %>.deb')
}

/**
 * Spawn a child process.
 */
var spawn = function (options, command, args, callback) {
  var spawnedProcess = null
  var error = null
  var stderr = ''

  options.logger('Executing command ' + command + ' ' + args.join(' '))

  try {
    spawnedProcess = child.spawn(command, args)
  } catch (err) {
    process.nextTick(function () {
      callback(err, stderr)
    })
    return
  }

  spawnedProcess.stderr.on('data', function (data) {
    stderr += data
  })

  spawnedProcess.on('error', function (err) {
    error = error || err
  })

  spawnedProcess.on('close', function (code, signal) {
    if (code !== 0) {
      error = error || signal || code
    }

    callback(error && new Error('Error executing command (' + (error.message || error) + '): ' +
      '\n' + command + ' ' + args.join(' ') + '\n' + stderr))
  })
}

/**
 * Read `package.json` either from `resources.app.asar` (if the app is packaged)
 * or from `resources/app/package.json` (if it is not).
 */
var readMeta = function (options, callback) {
  var withAsar = path.join(options.src, 'resources/app.asar')
  var withoutAsar = path.join(options.src, 'resources/app/package.json')

  try {
    fs.accessSync(withAsar)
    options.logger('Reading package metadata from ' + withAsar)
    callback(null, JSON.parse(asar.extractFile(withAsar, 'package.json')))
    return
  } catch (err) {
  }

  try {
    options.logger('Reading package metadata from ' + withoutAsar)
    callback(null, fs.readJsonSync(withoutAsar))
  } catch (err) {
    callback(new Error('Error reading package metadata: ' + (err.message || err)))
  }
}

/**
 * Read `LICENSE` from the root of the app.
 */
var readLicense = function (options, callback) {
  var licenseSrc = path.join(options.src, 'LICENSE')
  options.logger('Reading license file from ' + licenseSrc)

  fs.readFile(licenseSrc, callback)
}

/**
 * Get the size of the app.
 */
var getSize = function (options, callback) {
  fsize(options.src, callback)
}

/**
 * Get the hash of default options for the installer. Some come from the info
 * read from `package.json`, and some are hardcoded.
 */
var getDefaults = function (data, callback) {
  async.parallel([
    async.apply(readMeta, data),
    async.apply(getSize, {src: data.src})
  ], function (err, results) {
    var pkg = results[0] || {}
    var size = results[1] || 0

    var defaults = {
      name: pkg.name || 'electron',
      productName: pkg.productName || pkg.name,
      genericName: pkg.genericName || pkg.productName || pkg.name,
      description: pkg.description,
      productDescription: pkg.productDescription || pkg.description,
      // Use '~' on pre-releases for proper debain version ordering.
      // See https://www.debian.org/doc/debian-policy/ch-controlfields.html#s-f-Version
      version: (pkg.version || '0.0.0').replace(/(\d)[_.+-]?((RC|rc|pre|dev|beta|alpha)[_.+-]?\d*)$/, '$1~$2'),
      revision: pkg.revision || '1',

      section: 'utils',
      priority: 'optional',
      arch: undefined,
      size: Math.ceil(size / 1024),

      depends: [
        'gconf-service',
        'gconf2',
        'gvfs-bin',
        'libc6',
        'libcap2',
        'libgcrypt11 | libgcrypt20',
        'libgtk2.0-0',
        'libnotify4',
        'libnss3',
        'libudev0 | libudev1',
        'libxtst6',
        'xdg-utils'
      ],
      recommends: [
        'lsb-release'
      ],
      suggests: [
        'gir1.2-gnomekeyring-1.0',
        'libgnome-keyring0'
      ],
      enhances: [
      ],
      preDepends: [
      ],

      maintainer: pkg.author && (typeof pkg.author === 'string'
        ? pkg.author.replace(/\s+\([^)]+\)/, '')
        : pkg.author.name +
          (pkg.author.email != null ? ' <' + pkg.author.email + '>' : '')
      ),

      homepage: pkg.homepage || (pkg.author && (typeof pkg.author === 'string'
        ? pkg.author.replace(/.*\(([^)]+)\).*/, '$1')
        : pkg.author.url
      )),

      bin: pkg.name || 'electron',
      icon: path.resolve(__dirname, '../resources/icon.png'),

      categories: [
        'GNOME',
        'GTK',
        'Utility'
      ],

      mimeType: [],

      lintianOverrides: []
    }

    callback(err, defaults)
  })
}

/**
 * Get the hash of options for the installer.
 */
var getOptions = function (data, defaults, callback) {
  // Flatten everything for ease of use.
  var options = _.defaults({}, data, data.options, defaults)

  // Wrap the extended description to avoid lintian warning about
  // `extended-description-line-too-long`.
  options.productDescription = wrap(options.productDescription, {width: 80, indent: ' '})

  callback(null, options)
}

/**
 * Fill in a template with the hash of options.
 */
var generateTemplate = function (options, file, callback) {
  options.logger('Generating template from ' + file)

  async.waterfall([
    async.apply(fs.readFile, file),
    function (template, callback) {
      var result = _.template(template)(options)
      options.logger('Generated template from ' + file + '\n' + result)
      callback(null, result)
    }
  ], callback)
}

/**
 * Create the control file for the package.
 *
 * See: https://www.debian.org/doc/debian-policy/ch-controlfields.html
 */
var createControl = function (options, dir, callback) {
  var controlSrc = path.resolve(__dirname, '../resources/control.ejs')
  var controlDest = path.join(dir, 'DEBIAN/control')
  options.logger('Creating control file at ' + controlDest)

  async.waterfall([
    async.apply(generateTemplate, options, controlSrc),
    async.apply(fs.outputFile, controlDest)
  ], function (err) {
    callback(err && new Error('Error creating control file: ' + (err.message || err)))
  })
}

/**
 * Create the binary for the package.
 */
var createBinary = function (options, dir, callback) {
  var binDir = path.join(dir, 'usr/bin')
  var binSrc = path.join('../lib', options.name, options.bin)
  var binDest = path.join(binDir, options.name)
  options.logger('Symlinking binary from ' + binSrc + ' to ' + binDest)

  async.waterfall([
    async.apply(fs.ensureDir, binDir),
    async.apply(fs.symlink, binSrc, binDest, 'file')
  ], function (err) {
    callback(err && new Error('Error creating binary file: ' + (err.message || err)))
  })
}

/**
 * Create the desktop file for the package.
 *
 * See: http://standards.freedesktop.org/desktop-entry-spec/latest/
 */
var createDesktop = function (options, dir, callback) {
  var desktopSrc = path.resolve(__dirname, '../resources/desktop.ejs')
  var desktopDest = path.join(dir, 'usr/share/applications', options.name + '.desktop')
  options.logger('Creating desktop file at ' + desktopDest)

  async.waterfall([
    async.apply(generateTemplate, options, desktopSrc),
    async.apply(fs.outputFile, desktopDest)
  ], function (err) {
    callback(err && new Error('Error creating desktop file: ' + (err.message || err)))
  })
}

/**
 * Create pixmap icon for the package.
 */
var createPixmapIcon = function (options, dir, callback) {
  var iconFile = path.join(dir, 'usr/share/pixmaps', options.name + '.png')
  options.logger('Creating icon file at ' + iconFile)

  fs.copy(options.icon, iconFile, function (err) {
    callback(err && new Error('Error creating icon file: ' + (err.message || err)))
  })
}

/**
 * Create hicolor icon for the package.
 */
var createHicolorIcon = function (options, dir, callback) {
  async.forEachOf(options.icon, function (icon, resolution, callback) {
    var iconFile = path.join(dir, 'usr/share/icons/hicolor', resolution, 'apps', options.name + '.png')
    options.logger('Creating icon file at ' + iconFile)

    fs.copy(icon, iconFile, callback)
  }, function (err) {
    callback(err && new Error('Error creating icon file: ' + (err.message || err)))
  })
}

/**
 * Create icon for the package.
 */
var createIcon = function (options, dir, callback) {
  if (_.isObject(options.icon)) {
    createHicolorIcon(options, dir, callback)
  } else {
    createPixmapIcon(options, dir, callback)
  }
}

/**
 * Create copyright for the package.
 */
var createCopyright = function (options, dir, callback) {
  var copyrightFile = path.join(dir, 'usr/share/doc', options.name, 'copyright')
  options.logger('Creating copyright file at ' + copyrightFile)

  async.waterfall([
    async.apply(readLicense, options),
    async.apply(fs.outputFile, copyrightFile)
  ], function (err) {
    callback(err && new Error('Error creating copyright file: ' + (err.message || err)))
  })
}

/**
 * Create lintian overrides for the package.
 */
var createOverrides = function (options, dir, callback) {
  var overridesSrc = path.resolve(__dirname, '../resources/overrides.ejs')
  var overridesDest = path.join(dir, 'usr/share/lintian/overrides', options.name)
  options.logger('Creating lintian overrides at ' + overridesDest)

  async.waterfall([
    async.apply(generateTemplate, options, overridesSrc),
    async.apply(fs.outputFile, overridesDest)
  ], function (err) {
    callback(err && new Error('Error creating lintian overrides file: ' + (err.message || err)))
  })
}

/**
 * Copy the application into the package.
 */
var createApplication = function (options, dir, callback) {
  var applicationDir = path.join(dir, 'usr/lib', options.name)
  options.logger('Copying application to ' + applicationDir)

  async.waterfall([
    async.apply(fs.ensureDir, applicationDir),
    async.apply(fs.copy, options.src, applicationDir)
  ], function (err) {
    callback(err && new Error('Error copying application directory: ' + (err.message || err)))
  })
}

/**
 * Create temporary directory where the contents of the package will live.
 */
var createDir = function (options, callback) {
  options.logger('Creating temporary directory')

  async.waterfall([
    async.apply(temp.mkdir, 'electron-'),
    function (dir, callback) {
      dir = path.join(dir, options.name + '_' + options.version + '_' + options.arch)
      fs.ensureDir(dir, callback)
    }
  ], function (err, dir) {
    callback(err && new Error('Error creating temporary directory: ' + (err.message || err)), dir)
  })
}

/**
 * Create the contents of the package.
 */
var createContents = function (options, dir, callback) {
  options.logger('Creating contents of package')

  async.parallel([
    async.apply(createControl, options, dir),
    async.apply(createBinary, options, dir),
    async.apply(createDesktop, options, dir),
    async.apply(createIcon, options, dir),
    async.apply(createCopyright, options, dir),
    async.apply(createOverrides, options, dir),
    async.apply(createApplication, options, dir)
  ], function (err) {
    callback(err, dir)
  })
}

/**
 * Package everything using `dpkg` and `fakeroot`.
 */
var createPackage = function (options, dir, callback) {
  options.logger('Creating package at ' + dir)

  spawn(options, 'fakeroot', ['dpkg-deb', '--build', dir], function (err) {
    callback(err, dir)
  })
}

/**
 * Move the package to the specified destination.
 */
var movePackage = function (options, dir, callback) {
  options.logger('Moving package to destination')

  var packagePattern = path.join(dir, '../*.deb')
  async.waterfall([
    async.apply(glob, packagePattern),
    function (files, callback) {
      async.each(files, function (file) {
        var dest = options.rename(options.dest, path.basename(file))
        dest = _.template(dest)(options)
        options.logger('Moving file ' + file + ' to ' + dest)
        fs.move(file, dest, {clobber: true}, callback)
      }, callback)
    }
  ], function (err) {
    callback(err && new Error('Error moving package files: ' + (err.message || err)), dir)
  })
}

/* ************************************************************************** */

module.exports = function (data, callback) {
  data.rename = data.rename || defaultRename
  data.logger = data.logger || defaultLogger

  async.waterfall([
    async.apply(getDefaults, data),
    async.apply(getOptions, data),
    function (options, callback) {
      data.logger('Creating package with options\n' + JSON.stringify(options, null, 2))
      async.waterfall([
        async.apply(createDir, options),
        async.apply(createContents, options),
        async.apply(createPackage, options),
        async.apply(movePackage, options)
      ], function (err) {
        callback(err, options)
      })
    }
  ], function (err, options) {
    if (!err) {
      data.logger('Successfully created package at ' + options.dest)
    } else {
      data.logger('Error creating package: ' + (err.message || err))
    }

    callback(err, options)
  })
}
<commit_msg>refactor(installer): return more-helpful error message on fakeroot ENOENT<commit_after>'use strict'

var _ = require('lodash')
var asar = require('asar')
var async = require('async')
var child = require('child_process')
var debug = require('debug')
var fs = require('fs-extra')
var fsize = require('get-folder-size')
var glob = require('glob')
var path = require('path')
var temp = require('temp').track()
var wrap = require('word-wrap')

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

var defaultLogger = debug(pkg.name)

var defaultRename = function (dest, src) {
  return path.join(dest, '<%= name %>_<%= version %>_<%= arch %>.deb')
}

/**
 * Spawn a child process.
 */
var spawn = function (options, command, args, callback) {
  var spawnedProcess = null
  var error = null
  var stderr = ''

  options.logger('Executing command ' + command + ' ' + args.join(' '))

  try {
    spawnedProcess = child.spawn(command, args)
  } catch (err) {
    process.nextTick(function () {
      callback(err, stderr)
    })
    return
  }

  spawnedProcess.stderr.on('data', function (data) {
    stderr += data
  })

  spawnedProcess.on('error', function (err) {
    if (err.name = 'ENOENT') {
      var installer = process.platform === 'darwin' ? 'brew' : 'apt-get'

      if (err.message.indexOf('fakeroot') > -1) {
        err.message = 'Your system is missing the fakeroot package. Try ' + installer + ' install fakeroot'
      }
    }

    error = error || err
  })

  spawnedProcess.on('close', function (code, signal) {
    if (code !== 0) {
      error = error || signal || code
    }

    callback(error && new Error('Error executing command (' + (error.message || error) + '): ' +
      '\n' + command + ' ' + args.join(' ') + '\n' + stderr))
  })
}

/**
 * Read `package.json` either from `resources.app.asar` (if the app is packaged)
 * or from `resources/app/package.json` (if it is not).
 */
var readMeta = function (options, callback) {
  var withAsar = path.join(options.src, 'resources/app.asar')
  var withoutAsar = path.join(options.src, 'resources/app/package.json')

  try {
    fs.accessSync(withAsar)
    options.logger('Reading package metadata from ' + withAsar)
    callback(null, JSON.parse(asar.extractFile(withAsar, 'package.json')))
    return
  } catch (err) {
  }

  try {
    options.logger('Reading package metadata from ' + withoutAsar)
    callback(null, fs.readJsonSync(withoutAsar))
  } catch (err) {
    callback(new Error('Error reading package metadata: ' + (err.message || err)))
  }
}

/**
 * Read `LICENSE` from the root of the app.
 */
var readLicense = function (options, callback) {
  var licenseSrc = path.join(options.src, 'LICENSE')
  options.logger('Reading license file from ' + licenseSrc)

  fs.readFile(licenseSrc, callback)
}

/**
 * Get the size of the app.
 */
var getSize = function (options, callback) {
  fsize(options.src, callback)
}

/**
 * Get the hash of default options for the installer. Some come from the info
 * read from `package.json`, and some are hardcoded.
 */
var getDefaults = function (data, callback) {
  async.parallel([
    async.apply(readMeta, data),
    async.apply(getSize, {src: data.src})
  ], function (err, results) {
    var pkg = results[0] || {}
    var size = results[1] || 0

    var defaults = {
      name: pkg.name || 'electron',
      productName: pkg.productName || pkg.name,
      genericName: pkg.genericName || pkg.productName || pkg.name,
      description: pkg.description,
      productDescription: pkg.productDescription || pkg.description,
      // Use '~' on pre-releases for proper debain version ordering.
      // See https://www.debian.org/doc/debian-policy/ch-controlfields.html#s-f-Version
      version: (pkg.version || '0.0.0').replace(/(\d)[_.+-]?((RC|rc|pre|dev|beta|alpha)[_.+-]?\d*)$/, '$1~$2'),
      revision: pkg.revision || '1',

      section: 'utils',
      priority: 'optional',
      arch: undefined,
      size: Math.ceil(size / 1024),

      depends: [
        'gconf-service',
        'gconf2',
        'gvfs-bin',
        'libc6',
        'libcap2',
        'libgcrypt11 | libgcrypt20',
        'libgtk2.0-0',
        'libnotify4',
        'libnss3',
        'libudev0 | libudev1',
        'libxtst6',
        'xdg-utils'
      ],
      recommends: [
        'lsb-release'
      ],
      suggests: [
        'gir1.2-gnomekeyring-1.0',
        'libgnome-keyring0'
      ],
      enhances: [
      ],
      preDepends: [
      ],

      maintainer: pkg.author && (typeof pkg.author === 'string'
        ? pkg.author.replace(/\s+\([^)]+\)/, '')
        : pkg.author.name +
          (pkg.author.email != null ? ' <' + pkg.author.email + '>' : '')
      ),

      homepage: pkg.homepage || (pkg.author && (typeof pkg.author === 'string'
        ? pkg.author.replace(/.*\(([^)]+)\).*/, '$1')
        : pkg.author.url
      )),

      bin: pkg.name || 'electron',
      icon: path.resolve(__dirname, '../resources/icon.png'),

      categories: [
        'GNOME',
        'GTK',
        'Utility'
      ],

      mimeType: [],

      lintianOverrides: []
    }

    callback(err, defaults)
  })
}

/**
 * Get the hash of options for the installer.
 */
var getOptions = function (data, defaults, callback) {
  // Flatten everything for ease of use.
  var options = _.defaults({}, data, data.options, defaults)

  // Wrap the extended description to avoid lintian warning about
  // `extended-description-line-too-long`.
  options.productDescription = wrap(options.productDescription, {width: 80, indent: ' '})

  callback(null, options)
}

/**
 * Fill in a template with the hash of options.
 */
var generateTemplate = function (options, file, callback) {
  options.logger('Generating template from ' + file)

  async.waterfall([
    async.apply(fs.readFile, file),
    function (template, callback) {
      var result = _.template(template)(options)
      options.logger('Generated template from ' + file + '\n' + result)
      callback(null, result)
    }
  ], callback)
}

/**
 * Create the control file for the package.
 *
 * See: https://www.debian.org/doc/debian-policy/ch-controlfields.html
 */
var createControl = function (options, dir, callback) {
  var controlSrc = path.resolve(__dirname, '../resources/control.ejs')
  var controlDest = path.join(dir, 'DEBIAN/control')
  options.logger('Creating control file at ' + controlDest)

  async.waterfall([
    async.apply(generateTemplate, options, controlSrc),
    async.apply(fs.outputFile, controlDest)
  ], function (err) {
    callback(err && new Error('Error creating control file: ' + (err.message || err)))
  })
}

/**
 * Create the binary for the package.
 */
var createBinary = function (options, dir, callback) {
  var binDir = path.join(dir, 'usr/bin')
  var binSrc = path.join('../lib', options.name, options.bin)
  var binDest = path.join(binDir, options.name)
  options.logger('Symlinking binary from ' + binSrc + ' to ' + binDest)

  async.waterfall([
    async.apply(fs.ensureDir, binDir),
    async.apply(fs.symlink, binSrc, binDest, 'file')
  ], function (err) {
    callback(err && new Error('Error creating binary file: ' + (err.message || err)))
  })
}

/**
 * Create the desktop file for the package.
 *
 * See: http://standards.freedesktop.org/desktop-entry-spec/latest/
 */
var createDesktop = function (options, dir, callback) {
  var desktopSrc = path.resolve(__dirname, '../resources/desktop.ejs')
  var desktopDest = path.join(dir, 'usr/share/applications', options.name + '.desktop')
  options.logger('Creating desktop file at ' + desktopDest)

  async.waterfall([
    async.apply(generateTemplate, options, desktopSrc),
    async.apply(fs.outputFile, desktopDest)
  ], function (err) {
    callback(err && new Error('Error creating desktop file: ' + (err.message || err)))
  })
}

/**
 * Create pixmap icon for the package.
 */
var createPixmapIcon = function (options, dir, callback) {
  var iconFile = path.join(dir, 'usr/share/pixmaps', options.name + '.png')
  options.logger('Creating icon file at ' + iconFile)

  fs.copy(options.icon, iconFile, function (err) {
    callback(err && new Error('Error creating icon file: ' + (err.message || err)))
  })
}

/**
 * Create hicolor icon for the package.
 */
var createHicolorIcon = function (options, dir, callback) {
  async.forEachOf(options.icon, function (icon, resolution, callback) {
    var iconFile = path.join(dir, 'usr/share/icons/hicolor', resolution, 'apps', options.name + '.png')
    options.logger('Creating icon file at ' + iconFile)

    fs.copy(icon, iconFile, callback)
  }, function (err) {
    callback(err && new Error('Error creating icon file: ' + (err.message || err)))
  })
}

/**
 * Create icon for the package.
 */
var createIcon = function (options, dir, callback) {
  if (_.isObject(options.icon)) {
    createHicolorIcon(options, dir, callback)
  } else {
    createPixmapIcon(options, dir, callback)
  }
}

/**
 * Create copyright for the package.
 */
var createCopyright = function (options, dir, callback) {
  var copyrightFile = path.join(dir, 'usr/share/doc', options.name, 'copyright')
  options.logger('Creating copyright file at ' + copyrightFile)

  async.waterfall([
    async.apply(readLicense, options),
    async.apply(fs.outputFile, copyrightFile)
  ], function (err) {
    callback(err && new Error('Error creating copyright file: ' + (err.message || err)))
  })
}

/**
 * Create lintian overrides for the package.
 */
var createOverrides = function (options, dir, callback) {
  var overridesSrc = path.resolve(__dirname, '../resources/overrides.ejs')
  var overridesDest = path.join(dir, 'usr/share/lintian/overrides', options.name)
  options.logger('Creating lintian overrides at ' + overridesDest)

  async.waterfall([
    async.apply(generateTemplate, options, overridesSrc),
    async.apply(fs.outputFile, overridesDest)
  ], function (err) {
    callback(err && new Error('Error creating lintian overrides file: ' + (err.message || err)))
  })
}

/**
 * Copy the application into the package.
 */
var createApplication = function (options, dir, callback) {
  var applicationDir = path.join(dir, 'usr/lib', options.name)
  options.logger('Copying application to ' + applicationDir)

  async.waterfall([
    async.apply(fs.ensureDir, applicationDir),
    async.apply(fs.copy, options.src, applicationDir)
  ], function (err) {
    callback(err && new Error('Error copying application directory: ' + (err.message || err)))
  })
}

/**
 * Create temporary directory where the contents of the package will live.
 */
var createDir = function (options, callback) {
  options.logger('Creating temporary directory')

  async.waterfall([
    async.apply(temp.mkdir, 'electron-'),
    function (dir, callback) {
      dir = path.join(dir, options.name + '_' + options.version + '_' + options.arch)
      fs.ensureDir(dir, callback)
    }
  ], function (err, dir) {
    callback(err && new Error('Error creating temporary directory: ' + (err.message || err)), dir)
  })
}

/**
 * Create the contents of the package.
 */
var createContents = function (options, dir, callback) {
  options.logger('Creating contents of package')

  async.parallel([
    async.apply(createControl, options, dir),
    async.apply(createBinary, options, dir),
    async.apply(createDesktop, options, dir),
    async.apply(createIcon, options, dir),
    async.apply(createCopyright, options, dir),
    async.apply(createOverrides, options, dir),
    async.apply(createApplication, options, dir)
  ], function (err) {
    callback(err, dir)
  })
}

/**
 * Package everything using `dpkg` and `fakeroot`.
 */
var createPackage = function (options, dir, callback) {
  options.logger('Creating package at ' + dir)

  spawn(options, 'fakeroot', ['dpkg-deb', '--build', dir], function (err) {
    callback(err, dir)
  })
}

/**
 * Move the package to the specified destination.
 */
var movePackage = function (options, dir, callback) {
  options.logger('Moving package to destination')

  var packagePattern = path.join(dir, '../*.deb')
  async.waterfall([
    async.apply(glob, packagePattern),
    function (files, callback) {
      async.each(files, function (file) {
        var dest = options.rename(options.dest, path.basename(file))
        dest = _.template(dest)(options)
        options.logger('Moving file ' + file + ' to ' + dest)
        fs.move(file, dest, {clobber: true}, callback)
      }, callback)
    }
  ], function (err) {
    callback(err && new Error('Error moving package files: ' + (err.message || err)), dir)
  })
}

/* ************************************************************************** */

module.exports = function (data, callback) {
  data.rename = data.rename || defaultRename
  data.logger = data.logger || defaultLogger

  async.waterfall([
    async.apply(getDefaults, data),
    async.apply(getOptions, data),
    function (options, callback) {
      data.logger('Creating package with options\n' + JSON.stringify(options, null, 2))
      async.waterfall([
        async.apply(createDir, options),
        async.apply(createContents, options),
        async.apply(createPackage, options),
        async.apply(movePackage, options)
      ], function (err) {
        callback(err, options)
      })
    }
  ], function (err, options) {
    if (!err) {
      data.logger('Successfully created package at ' + options.dest)
    } else {
      data.logger('Error creating package: ' + (err.message || err))
    }

    callback(err, options)
  })
}
<|endoftext|>


<commit_before>module.exports = function(client) {
  return {
    /**
     * Change focus to another window. The window to change focus to may be specified by its server assigned window handle, or by the value of its name attribute.
     *
     * To find out the window handle use `window_handles` protocol action
     *
     * ```
     *  this.demoTest = function (browser) {
     *    browser.window_handles(function(result) {
     *      var handle = result.value[0];
     *      browser.switchWindow(handle);
     *    });
     *  };
     * ```
     *
     * @method switchWindow
     * @param {string} handleOrName The server assigned window handle or the name attribute.
     * @param {function} [callback] Optional callback function to be called when the command finishes.
     * @see window
     * @since v0.3.0
     * @api commands
     */
    switchWindow : function(handleOrName, callback) {
      var self = this;
      this.window('POST', handleOrName, function(result) {
        if (typeof callback === 'function') {
          callback.call(self, result);
        }
      });
      return this;
    },

    /**
     * Resizes the current window.
     *
     * ```
     *  this.demoTest = function (browser) {
     *    browser.resizeWindow(1000, 800);
     *  };
     * ```
     *
     * @method resizeWindow
     * @param {number} width The new window width.
     * @param {height} width The new window height.
     * @param {function} [callback] Optional callback function to be called when the command finishes.
     * @see windowSize
     * @since v0.3.0
     * @api commands
     */
    resizeWindow : function(width, height, callback) {
      var self = this;
      return this.windowSize('current', width, height, function(result) {
        if (typeof callback === 'function') {
          callback.call(self, result);
        }
      });
    },

    /**
     * Maximizes the current window.
     *
     * ```
     *  this.demoTest = function (browser) {
     *    browser.maximizeWindow();
     *  };
     * ```
     *
     * @method resizeWindow
     * @param {function} [callback] Optional callback function to be called when the command finishes.
     * @see windowSize
     * @since v0.5.13
     * @api commands
     */
    maximizeWindow: function(callback) {
      var self = this;
      return this.windowMaximize('current', function(result) {
        if (typeof callback === 'function') {
          callback.call(self, result);
        }
      });
    },

    /**
     * Take a screenshot of the current page and saves it as the given filename.
     *
     * ```
     *  this.demoTest = function (browser) {
     *    browser.saveScreenshot('/path/to/fileName.png');
     *  };
     * ```
     *
     * @method saveScreenshot
     * @param {string} fileName The complete path to the file name where the screenshot should be saved.
     * @param {function} [callback] Optional callback function to be called when the command finishes.
     * @see screenshot
     * @api commands
     */
    saveScreenshot : function(fileName, callback) {
      var self = this;
      return this.screenshot(client.api.options.log_screenshot_data, function(result) {
        client.saveScreenshotToFile(fileName, result.value, function() {
          if (typeof callback === 'function') {
            callback.call(self, result);
          }
        });
      });
    },

    /**
     * Returns the title of the current page. Uses title protocol command.
     *
     * ```
     *  this.demoTest = function (browser) {
     *    browser.getTitle(function(title) {
     *      this.assert.equal(typeof title, 'string');
     *      this.assert.equal(title, 'Nightwatch.js');
     *    });
     *  };
     * ```
     *
     * @method getTitle
     * @param {function} [callback] Optional callback function to be called when the command finishes.
     * @see title
     * @returns {string} The page title.
     * @api commands
     */
    getTitle : function(callback) {
      var self = this;
      return this.title(function(result) {
        callback.call(self, result.value);
      });
    },

    /**
     * Ends the session. Uses session protocol command.
     *
     * ```
     * this.demoTest = function (browser) {
     *   browser.end();
     * };
     * ```
     *
     * @method end
     * @param {function} [callback] Optional callback function to be called when the command finishes.
     * @see session
     * @api commands
     */
    end : function(callback) {
      var self = this;
      return this.session('delete', function(result) {
        client.sessionId = null;
        if (typeof callback === 'function') {
          callback.call(self, result);
        }
      });
    },

    /**
     * Close the current window. This can be useful when you're working with multiple windows open (e.g. an OAuth login).
     * Uses `window` protocol command.
     *
     * ```
     * this.demoTest = function (client) {
     *   client.closeWindow();
     * };
     * ```
     *
     * @method closeWindow
     * @param {function} [callback] Optional callback function to be called when the command finishes.
     * @see window
     * @since v0.3.0
     * @api commands
     */
    closeWindow : function(callback) {
      var self = this;
      return this.window('DELETE', function(result) {
        if (typeof callback === 'function') {
          callback.call(self, result);
        }
      });
    },

    /**
     * This command is an alias to url and also a convenience method when called without any arguments in the sense that it performs a call to .url() with passing the value of `launch_url` field from the settings file.
     * Uses `url` protocol command.
     *
     * ```
     * this.demoTest = function (client) {
     *   client.init();
     * };
     * ```
     *
     * @method init
     * @param {string} [url] Url to navigate to.
     * @see url
     * @since v0.4.0
     * @api commands
     */
    init : function(url) {
      if (url) {
        return this.url(url);
      }

      if (!this.launchUrl) {
        return this;
      }
      return this.url(this.launchUrl);
    },

    /**
     * Convenience method that adds the specified hash (i.e. url fragment) to the current value of the `launch_url` as set in `nightwatch.json`.
     *
     * ```
     * this.demoTest = function (client) {
     *   client.urlHash('#hashvalue');
     *   // or
     *   client.urlHash('hashvalue');
     * };
     * ```
     *
     * @method urlHash
     * @param {string} hash The hash to add/replace to the current url (i.e. the value set in the launch_url property in nightwatch.json).
     * @see url
     * @since v0.4.0
     * @api commands
     */
    urlHash : function(hash) {
      if (hash.charAt(0) === '#') {
        hash = hash.substring(1);
      }
      return this.url(this.launchUrl + '#' + hash);
    },

    /**
     * Retrieve all cookies visible to the current page. The cookies are returned as an array of cookie JSON object, as defined [here](https://code.google.com/p/selenium/wiki/JsonWireProtocol#Cookie_JSON_Object).
     *
     * Uses `cookie` protocol command.
     *
     * ```
     * this.demoTest = function(browser) {
     *   browser.getCookies(function callback(result) {
     *     this.assert.equal(result.value.length, 1);
     *     this.assert.equals(result.value[0].name, 'test_cookie');
     *   });
     * }
     * ```
     *
     * @method getCookies
     * @param {function} callback The callback function which will receive the response as an argument.
     * @api commands
     * @since v0.4.0
     * @see cookie
     * @returns {Array.<object>} A list of cookies.
     */
    getCookies : function(callback) {
      return this.cookie('GET', callback);
    },

    /**
     * Retrieve a single cookie visible to the current page. The cookie is returned as a cookie JSON object, as defined [here](https://code.google.com/p/selenium/wiki/JsonWireProtocol#Cookie_JSON_Object).
     *
     * Uses `cookie` protocol command.
     *
     * ```
     * this.demoTest = function(browser) {
     *   browser.getCookie(function callback(result) {
     *     this.assert.equal(result.value, '123456');
     *     this.assert.equals(result.name, 'test_cookie');
     *   });
     * }
     * ```
     *
     * @method getCookie
     * @param {string} name The cookie name.
     * @param {function} callback The callback function which will receive the response as an argument.
     * @api commands
     * @since v0.4.0
     * @see cookie
     * @returns {object|null} The cookie object as a selenium cookie JSON object or null if the cookie wasn't found.
     */
    getCookie : function(name, callback) {
      return this.cookie('GET', function(result) {
        if (!result.value || result.value.length === 0) {
          callback(null);
          return;
        }

        var cookie = null;
        for (var i = 0; i < result.value.length; i++) {
          if (result.value[i].name === name) {
            cookie = result.value[i];
            break;
          }
        }

        callback(cookie);
      });
    },

    /**
     * Set a cookie, specified as a cookie JSON object, as defined [here](https://code.google.com/p/selenium/wiki/JsonWireProtocol#Cookie_JSON_Object).
     *
     * Uses `cookie` protocol command.
     *
     * ```
     * this.demoTest = function(browser) {
     *   browser.setCookie({
     *     name     : "test_cookie",
     *     value    : "test_value",
     *     path     : "/", (Optional)
     *     domain   : "example.org", (Optional)
     *     secure   : false, (Optional)
     *     httpOnly : false, // (Optional)
     *     expiry   : 1395002765 // (Optional) time in seconds since midnight, January 1, 1970 UTC
     *   });
     * }
     * ```
     *
     * @method setCookie
     * @param {object} cookie The cookie object.
     * @param {function} [callback] Optional callback function to be called when the command finishes.
     * @api commands
     * @since v0.4.0
     * @see cookie
     */
    setCookie : function(cookie, callback) {
      return this.cookie('POST', cookie, callback);
    },

    /**
     * Delete the cookie with the given name. This command is a no-op if there is no such cookie visible to the current page.
     *
     * ```
     * this.demoTest = function(browser) {
     *   browser.deleteCookie("test_cookie", function() {
     *     // do something more in here
     *   });
     * ```
     *
     * @method deleteCookie
     * @param cookieName The name of the cookie to delete.
     * @param {function} [callback] Optional callback function to be called when the command finishes.
     * @api commands
     * @since v0.4.0
     * @see cookie
     */
    deleteCookie : function(cookieName, callback) {
      return this.cookie('DELETE', cookieName, callback);
    },

    /**
     * Delete all cookies visible to the current page.
     *
     * ```
     * this.demoTest = function(browser) {
     *   browser.deleteCookies(function() {
     *     // do something more in here
     *   });
     * ```
     *
     * @method deleteCookies
     * @param {function} [callback] Optional callback function to be called when the command finishes.
     * @api commands
     * @since v0.4.0
     * @see cookie
     */
    deleteCookies : function(callback) {
      return this.cookie('DELETE', callback);
    },

    /**
     * Utility command to load an external script into the page specified by url.
     *
     * ```
     * this.demoTest = function(client) {
     *   this.injectScript('http://example.org/js/utility.js', function() {
     *     // we're all done here.
     *   }
     * };
     * ```
     *
     * @method injectScript
     * @param {string} scriptUrl The script file url
     * @param {string} [id] Dom element id to be set on the script tag.
     * @param {function} [callback] Optional callback function to be called when the command finishes.
     * @api commands
     * @since v0.4.0
     * @returns {HTMLScriptElement} The newly created script tag.
     */
    injectScript : function(scriptUrl, id, callback) {
      var args = [scriptUrl];
      if (arguments.length == 2 && typeof arguments[1] == 'function') {
        callback = arguments[1];
      } else if (typeof id == 'string') {
        args.push(id);
      }

      return this.execute(function(u,i) {/* jshint browser:true */return (function(d){var e=d.createElement('script');var m=d.getElementsByTagName('head')[0];e.src=u;if(i){e.id=i;}m.appendChild(e);return e;})(document);}, args, callback);
    }
  };
};
<commit_msg>fixed maximizeWindow api documentation<commit_after>module.exports = function(client) {
  return {
    /**
     * Change focus to another window. The window to change focus to may be specified by its server assigned window handle, or by the value of its name attribute.
     *
     * To find out the window handle use `window_handles` protocol action
     *
     * ```
     *  this.demoTest = function (browser) {
     *    browser.window_handles(function(result) {
     *      var handle = result.value[0];
     *      browser.switchWindow(handle);
     *    });
     *  };
     * ```
     *
     * @method switchWindow
     * @param {string} handleOrName The server assigned window handle or the name attribute.
     * @param {function} [callback] Optional callback function to be called when the command finishes.
     * @see window
     * @since v0.3.0
     * @api commands
     */
    switchWindow : function(handleOrName, callback) {
      var self = this;
      this.window('POST', handleOrName, function(result) {
        if (typeof callback === 'function') {
          callback.call(self, result);
        }
      });
      return this;
    },

    /**
     * Resizes the current window.
     *
     * ```
     *  this.demoTest = function (browser) {
     *    browser.resizeWindow(1000, 800);
     *  };
     * ```
     *
     * @method resizeWindow
     * @param {number} width The new window width.
     * @param {height} width The new window height.
     * @param {function} [callback] Optional callback function to be called when the command finishes.
     * @see windowSize
     * @since v0.3.0
     * @api commands
     */
    resizeWindow : function(width, height, callback) {
      var self = this;
      return this.windowSize('current', width, height, function(result) {
        if (typeof callback === 'function') {
          callback.call(self, result);
        }
      });
    },

    /**
     * Maximizes the current window.
     *
     * ```
     *  this.demoTest = function (browser) {
     *    browser.maximizeWindow();
     *  };
     * ```
     *
     * @method maximizeWindow
     * @param {function} [callback] Optional callback function to be called when the command finishes.
     * @see windowMaximize
     * @since v0.5.13
     * @api commands
     */
    maximizeWindow: function(callback) {
      var self = this;
      return this.windowMaximize('current', function(result) {
        if (typeof callback === 'function') {
          callback.call(self, result);
        }
      });
    },

    /**
     * Take a screenshot of the current page and saves it as the given filename.
     *
     * ```
     *  this.demoTest = function (browser) {
     *    browser.saveScreenshot('/path/to/fileName.png');
     *  };
     * ```
     *
     * @method saveScreenshot
     * @param {string} fileName The complete path to the file name where the screenshot should be saved.
     * @param {function} [callback] Optional callback function to be called when the command finishes.
     * @see screenshot
     * @api commands
     */
    saveScreenshot : function(fileName, callback) {
      var self = this;
      return this.screenshot(client.api.options.log_screenshot_data, function(result) {
        client.saveScreenshotToFile(fileName, result.value, function() {
          if (typeof callback === 'function') {
            callback.call(self, result);
          }
        });
      });
    },

    /**
     * Returns the title of the current page. Uses title protocol command.
     *
     * ```
     *  this.demoTest = function (browser) {
     *    browser.getTitle(function(title) {
     *      this.assert.equal(typeof title, 'string');
     *      this.assert.equal(title, 'Nightwatch.js');
     *    });
     *  };
     * ```
     *
     * @method getTitle
     * @param {function} [callback] Optional callback function to be called when the command finishes.
     * @see title
     * @returns {string} The page title.
     * @api commands
     */
    getTitle : function(callback) {
      var self = this;
      return this.title(function(result) {
        callback.call(self, result.value);
      });
    },

    /**
     * Ends the session. Uses session protocol command.
     *
     * ```
     * this.demoTest = function (browser) {
     *   browser.end();
     * };
     * ```
     *
     * @method end
     * @param {function} [callback] Optional callback function to be called when the command finishes.
     * @see session
     * @api commands
     */
    end : function(callback) {
      var self = this;
      return this.session('delete', function(result) {
        client.sessionId = null;
        if (typeof callback === 'function') {
          callback.call(self, result);
        }
      });
    },

    /**
     * Close the current window. This can be useful when you're working with multiple windows open (e.g. an OAuth login).
     * Uses `window` protocol command.
     *
     * ```
     * this.demoTest = function (client) {
     *   client.closeWindow();
     * };
     * ```
     *
     * @method closeWindow
     * @param {function} [callback] Optional callback function to be called when the command finishes.
     * @see window
     * @since v0.3.0
     * @api commands
     */
    closeWindow : function(callback) {
      var self = this;
      return this.window('DELETE', function(result) {
        if (typeof callback === 'function') {
          callback.call(self, result);
        }
      });
    },

    /**
     * This command is an alias to url and also a convenience method when called without any arguments in the sense that it performs a call to .url() with passing the value of `launch_url` field from the settings file.
     * Uses `url` protocol command.
     *
     * ```
     * this.demoTest = function (client) {
     *   client.init();
     * };
     * ```
     *
     * @method init
     * @param {string} [url] Url to navigate to.
     * @see url
     * @since v0.4.0
     * @api commands
     */
    init : function(url) {
      if (url) {
        return this.url(url);
      }

      if (!this.launchUrl) {
        return this;
      }
      return this.url(this.launchUrl);
    },

    /**
     * Convenience method that adds the specified hash (i.e. url fragment) to the current value of the `launch_url` as set in `nightwatch.json`.
     *
     * ```
     * this.demoTest = function (client) {
     *   client.urlHash('#hashvalue');
     *   // or
     *   client.urlHash('hashvalue');
     * };
     * ```
     *
     * @method urlHash
     * @param {string} hash The hash to add/replace to the current url (i.e. the value set in the launch_url property in nightwatch.json).
     * @see url
     * @since v0.4.0
     * @api commands
     */
    urlHash : function(hash) {
      if (hash.charAt(0) === '#') {
        hash = hash.substring(1);
      }
      return this.url(this.launchUrl + '#' + hash);
    },

    /**
     * Retrieve all cookies visible to the current page. The cookies are returned as an array of cookie JSON object, as defined [here](https://code.google.com/p/selenium/wiki/JsonWireProtocol#Cookie_JSON_Object).
     *
     * Uses `cookie` protocol command.
     *
     * ```
     * this.demoTest = function(browser) {
     *   browser.getCookies(function callback(result) {
     *     this.assert.equal(result.value.length, 1);
     *     this.assert.equals(result.value[0].name, 'test_cookie');
     *   });
     * }
     * ```
     *
     * @method getCookies
     * @param {function} callback The callback function which will receive the response as an argument.
     * @api commands
     * @since v0.4.0
     * @see cookie
     * @returns {Array.<object>} A list of cookies.
     */
    getCookies : function(callback) {
      return this.cookie('GET', callback);
    },

    /**
     * Retrieve a single cookie visible to the current page. The cookie is returned as a cookie JSON object, as defined [here](https://code.google.com/p/selenium/wiki/JsonWireProtocol#Cookie_JSON_Object).
     *
     * Uses `cookie` protocol command.
     *
     * ```
     * this.demoTest = function(browser) {
     *   browser.getCookie(function callback(result) {
     *     this.assert.equal(result.value, '123456');
     *     this.assert.equals(result.name, 'test_cookie');
     *   });
     * }
     * ```
     *
     * @method getCookie
     * @param {string} name The cookie name.
     * @param {function} callback The callback function which will receive the response as an argument.
     * @api commands
     * @since v0.4.0
     * @see cookie
     * @returns {object|null} The cookie object as a selenium cookie JSON object or null if the cookie wasn't found.
     */
    getCookie : function(name, callback) {
      return this.cookie('GET', function(result) {
        if (!result.value || result.value.length === 0) {
          callback(null);
          return;
        }

        var cookie = null;
        for (var i = 0; i < result.value.length; i++) {
          if (result.value[i].name === name) {
            cookie = result.value[i];
            break;
          }
        }

        callback(cookie);
      });
    },

    /**
     * Set a cookie, specified as a cookie JSON object, as defined [here](https://code.google.com/p/selenium/wiki/JsonWireProtocol#Cookie_JSON_Object).
     *
     * Uses `cookie` protocol command.
     *
     * ```
     * this.demoTest = function(browser) {
     *   browser.setCookie({
     *     name     : "test_cookie",
     *     value    : "test_value",
     *     path     : "/", (Optional)
     *     domain   : "example.org", (Optional)
     *     secure   : false, (Optional)
     *     httpOnly : false, // (Optional)
     *     expiry   : 1395002765 // (Optional) time in seconds since midnight, January 1, 1970 UTC
     *   });
     * }
     * ```
     *
     * @method setCookie
     * @param {object} cookie The cookie object.
     * @param {function} [callback] Optional callback function to be called when the command finishes.
     * @api commands
     * @since v0.4.0
     * @see cookie
     */
    setCookie : function(cookie, callback) {
      return this.cookie('POST', cookie, callback);
    },

    /**
     * Delete the cookie with the given name. This command is a no-op if there is no such cookie visible to the current page.
     *
     * ```
     * this.demoTest = function(browser) {
     *   browser.deleteCookie("test_cookie", function() {
     *     // do something more in here
     *   });
     * ```
     *
     * @method deleteCookie
     * @param cookieName The name of the cookie to delete.
     * @param {function} [callback] Optional callback function to be called when the command finishes.
     * @api commands
     * @since v0.4.0
     * @see cookie
     */
    deleteCookie : function(cookieName, callback) {
      return this.cookie('DELETE', cookieName, callback);
    },

    /**
     * Delete all cookies visible to the current page.
     *
     * ```
     * this.demoTest = function(browser) {
     *   browser.deleteCookies(function() {
     *     // do something more in here
     *   });
     * ```
     *
     * @method deleteCookies
     * @param {function} [callback] Optional callback function to be called when the command finishes.
     * @api commands
     * @since v0.4.0
     * @see cookie
     */
    deleteCookies : function(callback) {
      return this.cookie('DELETE', callback);
    },

    /**
     * Utility command to load an external script into the page specified by url.
     *
     * ```
     * this.demoTest = function(client) {
     *   this.injectScript('http://example.org/js/utility.js', function() {
     *     // we're all done here.
     *   }
     * };
     * ```
     *
     * @method injectScript
     * @param {string} scriptUrl The script file url
     * @param {string} [id] Dom element id to be set on the script tag.
     * @param {function} [callback] Optional callback function to be called when the command finishes.
     * @api commands
     * @since v0.4.0
     * @returns {HTMLScriptElement} The newly created script tag.
     */
    injectScript : function(scriptUrl, id, callback) {
      var args = [scriptUrl];
      if (arguments.length == 2 && typeof arguments[1] == 'function') {
        callback = arguments[1];
      } else if (typeof id == 'string') {
        args.push(id);
      }

      return this.execute(function(u,i) {/* jshint browser:true */return (function(d){var e=d.createElement('script');var m=d.getElementsByTagName('head')[0];e.src=u;if(i){e.id=i;}m.appendChild(e);return e;})(document);}, args, callback);
    }
  };
};
<|endoftext|>