<commit_before>define(function() {

  'use strict';

  var _BYTESOURCE = Symbol('byteSource');

  var wavHeaderTemplate = new Uint8Array([
  	'R'.charCodeAt(0),
  	'I'.charCodeAt(0),
  	'F'.charCodeAt(0),
  	'F'.charCodeAt(0),
  	0,0,0,0, // uint32 @ 4: set to wavHeaderTemplate.length + data length (aligned to 2)
  	'W'.charCodeAt(0),
  	'A'.charCodeAt(0),
  	'V'.charCodeAt(0),
  	'E'.charCodeAt(0),
  	'f'.charCodeAt(0),
  	'm'.charCodeAt(0),
  	't'.charCodeAt(0),
  	' '.charCodeAt(0),
  	16,0,0,0,
  	1,0,
  	1,0,     // uint16 @ 22: number of channels
  	22,56,0,0, // uint32 @ 24: sampling rate
  	22,56,0,0, // uint32 @ 28: sampling rate * channels * bytes per sample
  	1,0, // uint16 @ 32: block align, channels * bytes per sample
  	8,0, // uint16 @ 34: bytes per sample * 8
  	'd'.charCodeAt(0),
  	'a'.charCodeAt(0),
  	't'.charCodeAt(0),
  	'a'.charCodeAt(0),
  	0,0,0,0 // uint32 @ 40: number of bytes
  	]);

  var itemObjectModel = {
    createItem: function(title) {
      var itemElement = document.createElement('SECTION');
      itemElement.classList.add('item');

      itemElement.appendChild(itemElement.titleElement = document.createElement('HEADER'));

      itemElement.titleTextElement = itemElement.titleElement;

      Object.defineProperties(itemElement, this.itemProperties);

      if (title) itemElement.itemTitle = title;

      itemElement.addEventListener('click', clickItem);

      return itemElement;
    },
    itemProperties: {
      itemTitle: {
        get: function() {
          return this.titleTextElement.innerText;
        },
        set: function(text) {
          this.titleTextElement.innerText = text;
        },
        enumerable: true,
      },
      byteSource: {
        get: function() {
          return this[_BYTESOURCE];
        },
        set: function(byteSource) {
          this[_BYTESOURCE] = byteSource;
          if (!byteSource) {
            this.titleElement.innerText = this.titleTextElement.innerText;
            this.titleTextElement = this.titleElement;
          }
          else {
            var link = document.createElement('A');
            link.href = '#';
            link.classList.add('item-download');
            link.download = this.itemTitle.replace(/[\\\/:"<>\*\?\|]/g, '_');
            link.innerText = this.titleTextElement.innerText;
            this.titleElement.innerHTML = '';
            this.titleElement.appendChild(this.titleTextElement = link);
          }
        },
      },
      startAddingItems: {
        value: function() {
          if (!this.classList.contains('has-subitems')) {
            this.classList.add('has-subitems');
            this.appendChild(this.subitemsElement = document.createElement('SECTION'));
            this.subitemsElement.classList.add('subitems');
          }
        },
      },
      addItem: {
        value: function(item) {
          this.startAddingItems();
          this.classList.add('itemizing');
          this.subitemsElement.appendChild(item);
          this.dispatchEvent(new CustomEvent(itemObjectModel.EVT_ITEM_ADDED, {detail:{item:item}}));
        },
      },
      confirmAllItemsAdded: {
        value: function() {
          this.classList.remove('itemizing');
          this.classList.add('itemized');
        },
      },
      text: {
        set: function(text) {
          var textContainer = document.createElement('PRE');
          textContainer.appendChild(document.createTextNode(text));
          this.addItem(textContainer);
        },
      },
      getBytes: {
        value: function() {
          return this.byteSource.getBytes();
        },
      },
      setDataObject: {
        value: function(value) {
          var textContainer = document.createElement('PRE');
          textContainer.appendChild(document.createTextNode(JSON.stringify(value, null, 2)));
          this.addItem(textContainer);
        },
      },
      withPixels: {
        value: function(width, height, callback) {
          var canvas = document.createElement('CANVAS');
          canvas.width = width;
          canvas.height = height;
          this.addItem(canvas);
          var ctx = canvas.getContext('2d');
          var imageData = ctx.createImageData(width, height);
          callback(imageData.data);
          ctx.putImageData(imageData, 0, 0);
        },
      },
      with2DContext: {
        value: function(width, height, callback) {
          var canvas = document.createElement('CANVAS');
          canvas.width = width;
          canvas.height = height;
          this.addItem(canvas);
          var ctx = canvas.getContext('2d');
          callback(ctx);
        },
      },
      setHotspot: {
        value: function(x, y) {
          this.dataset.hotspotX = x;
          this.dataset.hotspotY = y;
        },
      },
      setOffset: {
        value: function(x, y) {
          this.dataset.offsetX = x;
          this.dataset.offsetY = y;
        },
      },
      populate: {
        value: function() {
          var promises = [];
          this.dispatchEvent(new CustomEvent(itemObjectModel.EVT_POPULATE, {detail:{promises:promises /* and relax */}}));
          return Promise.all(promises).then(Promise.resolve(this));
        },
      },
      setRawAudio: {
        value: function(audioInfo) {
          var channelCount = audioInfo.channels || 1;
          var samplingRate = audioInfo.samplingRate || 22050;
          var samples = audioInfo.samples;
          var bytesPerSample = audioInfo.bytesPerSample || 1;

          var wavHeader = new Uint8Array(wavHeaderTemplate.length);
          wavHeader.set(wavHeaderTemplate);

          var wavFooter = new Uint8Array(samples.byteLength % 2);

          var wavView = new DataView(wavHeader.buffer, wavHeader.byteOffset, wavHeader.byteLength);
          wavView.setUint32(4, wavHeader.byteLength + samples.byteLength + wavFooter.byteLength, true);
          wavView.setUint16(22, channelCount, true);
          wavView.setUint32(24, samplingRate, true);
          wavView.setUint32(28, samplingRate * channelCount * bytesPerSample, true);
          wavView.setUint16(32, channelCount * bytesPerSample, true);
          wavView.setUint16(34, bytesPerSample * 8, true);
          wavView.setUint32(40, samples.byteLength, true);

          var resourceEl = document.createElement('AUDIO');
          resourceEl.src = URL.createObjectURL(new Blob([wavHeader, samples, wavFooter], {type:'audio/wav'}));
          resourceEl.controls = true;
          this.addItem(resourceEl);
        },
      },
    },
  };

  function clickItem(e) {
    e.stopPropagation();
    if (e.target.classList.contains('item-download')) {
      if (!e.target.classList.contains('prepared')) {
        e.preventDefault();
        if (!e.target.classList.contains('preparing')) {
          e.target.classList.add('preparing');
          this.byteSource.getURL().then(function(url) {
            e.target.classList.remove('preparing');
            e.target.classList.add('prepared');
            e.target.href = url;
            e.target.click();
          });
        }
      }
      return;
    }
    if (this.classList.contains('has-subitems')) {
      if (this.classList.toggle('open')) {
        this.dispatchEvent(new Event(itemObjectModel.EVT_POPULATE));
      }
    }
  }

  Object.defineProperties(itemObjectModel, {
    EVT_POPULATE: {value:'item-populate'},
    EVT_ITEM_ADDED: {value:'item-added'},
  });

  return itemObjectModel;

});
<commit_msg>Add events for population phase start & end<commit_after>define(function() {

  'use strict';

  var _BYTESOURCE = Symbol('byteSource');

  var wavHeaderTemplate = new Uint8Array([
  	'R'.charCodeAt(0),
  	'I'.charCodeAt(0),
  	'F'.charCodeAt(0),
  	'F'.charCodeAt(0),
  	0,0,0,0, // uint32 @ 4: set to wavHeaderTemplate.length + data length (aligned to 2)
  	'W'.charCodeAt(0),
  	'A'.charCodeAt(0),
  	'V'.charCodeAt(0),
  	'E'.charCodeAt(0),
  	'f'.charCodeAt(0),
  	'm'.charCodeAt(0),
  	't'.charCodeAt(0),
  	' '.charCodeAt(0),
  	16,0,0,0,
  	1,0,
  	1,0,     // uint16 @ 22: number of channels
  	22,56,0,0, // uint32 @ 24: sampling rate
  	22,56,0,0, // uint32 @ 28: sampling rate * channels * bytes per sample
  	1,0, // uint16 @ 32: block align, channels * bytes per sample
  	8,0, // uint16 @ 34: bytes per sample * 8
  	'd'.charCodeAt(0),
  	'a'.charCodeAt(0),
  	't'.charCodeAt(0),
  	'a'.charCodeAt(0),
  	0,0,0,0 // uint32 @ 40: number of bytes
  	]);

  var itemObjectModel = {
    createItem: function(title) {
      var itemElement = document.createElement('SECTION');
      itemElement.classList.add('item');

      itemElement.appendChild(itemElement.titleElement = document.createElement('HEADER'));

      itemElement.titleTextElement = itemElement.titleElement;

      Object.defineProperties(itemElement, this.itemProperties);

      if (title) itemElement.itemTitle = title;

      itemElement.addEventListener('click', clickItem);

      return itemElement;
    },
    itemProperties: {
      itemTitle: {
        get: function() {
          return this.titleTextElement.innerText;
        },
        set: function(text) {
          this.titleTextElement.innerText = text;
        },
        enumerable: true,
      },
      byteSource: {
        get: function() {
          return this[_BYTESOURCE];
        },
        set: function(byteSource) {
          this[_BYTESOURCE] = byteSource;
          if (!byteSource) {
            this.titleElement.innerText = this.titleTextElement.innerText;
            this.titleTextElement = this.titleElement;
          }
          else {
            var link = document.createElement('A');
            link.href = '#';
            link.classList.add('item-download');
            link.download = this.itemTitle.replace(/[\\\/:"<>\*\?\|]/g, '_');
            link.innerText = this.titleTextElement.innerText;
            this.titleElement.innerHTML = '';
            this.titleElement.appendChild(this.titleTextElement = link);
          }
        },
      },
      startAddingItems: {
        value: function() {
          if (!this.classList.contains('has-subitems')) {
            this.classList.add('has-subitems');
            this.appendChild(this.subitemsElement = document.createElement('SECTION'));
            this.subitemsElement.classList.add('subitems');
          }
        },
      },
      addItem: {
        value: function(item) {
          this.startAddingItems();
          this.classList.add('itemizing');
          this.subitemsElement.appendChild(item);
          this.dispatchEvent(new CustomEvent(itemObjectModel.EVT_ITEM_ADDED, {detail:{item:item}}));
        },
      },
      confirmAllItemsAdded: {
        value: function() {
          this.classList.remove('itemizing');
          this.classList.add('itemized');
        },
      },
      text: {
        set: function(text) {
          var textContainer = document.createElement('PRE');
          textContainer.appendChild(document.createTextNode(text));
          this.addItem(textContainer);
        },
      },
      getBytes: {
        value: function() {
          return this.byteSource.getBytes();
        },
      },
      setDataObject: {
        value: function(value) {
          var textContainer = document.createElement('PRE');
          textContainer.appendChild(document.createTextNode(JSON.stringify(value, null, 2)));
          this.addItem(textContainer);
        },
      },
      withPixels: {
        value: function(width, height, callback) {
          var canvas = document.createElement('CANVAS');
          canvas.width = width;
          canvas.height = height;
          this.addItem(canvas);
          var ctx = canvas.getContext('2d');
          var imageData = ctx.createImageData(width, height);
          callback(imageData.data);
          ctx.putImageData(imageData, 0, 0);
        },
      },
      with2DContext: {
        value: function(width, height, callback) {
          var canvas = document.createElement('CANVAS');
          canvas.width = width;
          canvas.height = height;
          this.addItem(canvas);
          var ctx = canvas.getContext('2d');
          callback(ctx);
        },
      },
      setHotspot: {
        value: function(x, y) {
          this.dataset.hotspotX = x;
          this.dataset.hotspotY = y;
        },
      },
      setOffset: {
        value: function(x, y) {
          this.dataset.offsetX = x;
          this.dataset.offsetY = y;
        },
      },
      populate: {
        value: function() {
          var promises = [];
          this.dispatchEvent(new CustomEvent(itemObjectModel.EVT_POPULATE, {detail:{promises:promises /* and relax */}}));
          if (promises.length === 0) {
            return Promise.resolve(this);
          }
          this.dispatchEvent(new Event(itemObjectModel.EVT_POPULATE_STARTED));
          var self = this;
          return Promise.all(promises).then(function() {
            self.dispatchEvent(new Event(itemObjectModel.EVT_POPULATE_ENDED));
            return self;
          });
        },
      },
      setRawAudio: {
        value: function(audioInfo) {
          var channelCount = audioInfo.channels || 1;
          var samplingRate = audioInfo.samplingRate || 22050;
          var samples = audioInfo.samples;
          var bytesPerSample = audioInfo.bytesPerSample || 1;

          var wavHeader = new Uint8Array(wavHeaderTemplate.length);
          wavHeader.set(wavHeaderTemplate);

          var wavFooter = new Uint8Array(samples.byteLength % 2);

          var wavView = new DataView(wavHeader.buffer, wavHeader.byteOffset, wavHeader.byteLength);
          wavView.setUint32(4, wavHeader.byteLength + samples.byteLength + wavFooter.byteLength, true);
          wavView.setUint16(22, channelCount, true);
          wavView.setUint32(24, samplingRate, true);
          wavView.setUint32(28, samplingRate * channelCount * bytesPerSample, true);
          wavView.setUint16(32, channelCount * bytesPerSample, true);
          wavView.setUint16(34, bytesPerSample * 8, true);
          wavView.setUint32(40, samples.byteLength, true);

          var resourceEl = document.createElement('AUDIO');
          resourceEl.src = URL.createObjectURL(new Blob([wavHeader, samples, wavFooter], {type:'audio/wav'}));
          resourceEl.controls = true;
          this.addItem(resourceEl);
        },
      },
    },
  };

  function clickItem(e) {
    e.stopPropagation();
    if (e.target.classList.contains('item-download')) {
      if (!e.target.classList.contains('prepared')) {
        e.preventDefault();
        if (!e.target.classList.contains('preparing')) {
          e.target.classList.add('preparing');
          this.byteSource.getURL().then(function(url) {
            e.target.classList.remove('preparing');
            e.target.classList.add('prepared');
            e.target.href = url;
            e.target.click();
          });
        }
      }
      return;
    }
    if (this.classList.contains('has-subitems')) {
      if (this.classList.toggle('open')) {
        this.dispatchEvent(new Event(itemObjectModel.EVT_POPULATE));
      }
    }
  }

  Object.defineProperties(itemObjectModel, {
    EVT_POPULATE: {value:'item-populate'},
    EVT_POPULATE_STARTED: {value:'item-populate-started'},
    EVT_POPULATE_ENDED: {value: 'item-populate-ended'},
    EVT_ITEM_ADDED: {value:'item-added'},
  });

  return itemObjectModel;

});
<|endoftext|>


<commit_before>(function (jStorage, undefined) {
    function writeCookie(name, value, days) {
        if (days) {
            var date = new Date();
            date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
            var expires = "; expires=" + date.toGMTString();
        }
        else var expires = "";
        document.cookie = name + "=" + value + expires + "; path=/";
    }

    function readCookie(name) {
        var nameEQ = name + "=";
        var ca = document.cookie.split(';');
        for (var i = 0; i < ca.length; i++) {
            var c = ca[i];
            while (c.charAt(0) == ' ') c = c.substring(1, c.length);
            if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length, c.length);
        }
        return null;
    }

    function eraseCookie(name) {
        writeCookie(name, "", -1);
    }

    function githubRequest(method, address, token, data, callback) {
        var xmlhttp;
        if (window.XMLHttpRequest) {// code for IE7+, Firefox, Chrome, Opera, Safari
            xmlhttp = new XMLHttpRequest();
        }
        else {// code for IE6, IE5
            xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
        }

        xmlhttp.dataType = "json";

        xmlhttp.open(method, address, true);

        xmlhttp.onreadystatechange = function () {
            if (this.readyState == 4) {
                if (this.status >= 200 && this.status < 300 || this.status === 304) {
                    var responseData = this.responseText ? JSON.parse(this.responseText) : {};
                    responseData['last-modified'] = this.getResponseHeader("Last-Modified");
                    callback(null, responseData, this);
                } else {
                    // This is a special for get directory calls...
                    if (this.status == 0 && address.indexOf('/contents/') > 0) {
                        callback(null, [], this);
                    }

                    callback({
                        path: address,
                        request: this,
                        error: this.status,
                    });
                }
            }
        };

        //xmlhttp.setRequestHeader('Accept', 'application/vnd.github.raw+json');
        xmlhttp.setRequestHeader('Accept', 'application/json;charset=UTF-8');
        xmlhttp.setRequestHeader('Content-Type', 'application/json;charset=UTF-8');
        //xmlhttp.setRequestHeader("Access-Control-Allow-Origin", window.location.protocol + '//' + window.location.host);
        //xmlhttp.setRequestHeader('Content-Type', 'application/xml');
        if (token) {
            xmlhttp.setRequestHeader('Authorization', 'token ' + token);
        }

        data ? xmlhttp.send(JSON.stringify(data)) : xmlhttp.send();
    }

    jStorage.providers.github = {
        init: function (wrapper, config) {
            var self = this;

            console.log('github init');

            this._config = config;
            this._hasCallback = config && typeof (config.callback) === "function";
            this._state = 'random' + new Date().getTime();
            this._code = false;
            this._shaCache = {};

            this.ensureAuth(wrapper, config);

            // https://github.com/settings/tokens/new
            var token = this._config.token;

            this._hasRepo = config && typeof (config.repo) === "string";
            this._hasToken = config && typeof (config.token) === "string";

            var callStatus = false;
            if (this._hasRepo && this._hasToken) {
                callStatus = {
                    'isOK': true,
                    'code': 0,
                    'msg': ''
                };
            } else {
                var msg = '';
                if (!this._hasToken) {
                    msg += 'No user token specified. ';
                }
                if (!this._hasRepo) {
                    msg += 'No repository specified. ';
                }
                callStatus = {
                    'isOK': false,
                    'code': -1,
                    'msg': msg
                };
            }

            if (self._hasCallback) {
                try {
                    self._config.callback(wrapper, callStatus);
                } catch (e) {
                    // TODO: handle error
                }
            }
        },
        get: function (name, callback) {
            var self = this;

            // Remove begining slash
            if (name && name.indexOf('/') == 0) {
                name = name.substring(1);
            }

            // Remove ending slash
            if (name && name[name.length - 1] == '/') {
                name = name.substring(0, name.length - 1);
            }

            addr = "https://api.github.com/repos/" + this._config.repo + "/contents/" + name;
            githubRequest("GET", addr, self._config.token, false, function () {
                console.log('GET', arguments);
                if (arguments.length >= 2) {
                    var info = arguments[1];
                    if ('length' in info) {
                        for (var i = 0; i < info.length; i++) {
                            self._shaCache[info[i].path] = info[i].sha;
                        }
                        callback(null, { 'isOK': false, 'msg': 'this is a directory', 'code': -2 });
                    }
                    else if (info.type == "file") {
                        var data = arguments[1].content;
                        self._shaCache[name] = info.sha;
                        callback(
                            {
                                'name': info.path,
                                'size': info.size,
                                'mime-type': 'text/html',
                                'modified': info['last-modified'],
                                'data': atob(data)
                            },
                            { 'isOK': true, 'msg': '', 'code': 0 });
                    } else {
                        callback(null, { 'isOK': false, 'msg': 'This is not a valid file', 'code': -1 });
                    }
                } else {
                    callback(null, { 'isOK': false, 'msg': arguments[0].request.statusText, 'code': arguments[0].request.status });
                }
            });
        },
        set: function (name, content, callback) {
            var self = this;

            // Remove begining slash
            if (name && name.indexOf('/') == 0) {
                name = name.substring(1);
            }

            // Remove ending slash
            if (name && name[name.length - 1] == '/') {
                name = name.substring(0, name.length - 1);
            }

            // update content of file
            addr = "https://api.github.com/repos/" + self._config.repo + "/contents/" + name;
            var data = {
                "message": "jStorage add/update",
                "content": btoa(content)
            };
            // This is required to update existing file (we need to tell github from what version we are trying to update)
            var sha = self._shaCache[name];
            if (sha) {
                data["sha"] = sha;
            }

            githubRequest("PUT", addr, self._config.token, data, function () {
                if (arguments.length >= 2) {
                    var info = arguments[1];
                    if (info.content && info.content.type == "file") {
                        callback(
                            {
                                'name': info.content.path,
                                'size': info.content.size,
                                'mime-type': 'text/html',
                                'modified': info['last-modified'],
                            },
                            { 'isOK': true, 'msg': '', 'code': 0 });
                    } else {
                        callback(null, { 'isOK': false, 'msg': 'This is not a valid file', 'code': -1 });
                    }
                } else {
                    callback(null, { 'isOK': false, 'msg': arguments[0].request.statusText, 'code': arguments[0].request.status });
                }
            });
        },
        move: function (currentName, newName, callback) {
            var self = this;

            //// Remove begining slash
            //if (name && name.indexOf('/') == 0) {
            //    name = name.substring(1);
            //}

            //// Remove ending slash
            //if (name && name[name.length - 1] == '/') {
            //    name = name.substring(0, name.length - 1);
            //}
            console.log('github move');
        },
        del: function (name, callback) {
            var self = this;

            // Remove begining slash
            if (name && name.indexOf('/') == 0) {
                name = name.substring(1);
            }

            // Remove ending slash
            if (name && name[name.length - 1] == '/') {
                name = name.substring(0, name.length - 1);
            }

            console.log('del', name);

            // update content of file
            addr = "https://api.github.com/repos/" + self._config.repo + "/contents/" + name;
            // sha is required to remove file, so we need to have called get before we can delete a file right now.
            var sha = self._shaCache[name];
            // If we don't already have sha, get it.
            if (!sha) {
                // find parent directory name
                var parentName = name;
                var index = parentName.lastIndexOf('/');
                if (index != -1) {
                    // We are looking a subdirectory
                    parentName = parentName.substring(0, index);
                } else {
                    // We are looking in root..
                    parentName = '';
                }

                var test = function (info, status) {
                    if (status.code == -2) {
                        // sha has been set, call ourself...
                        self.del(name, callback);
                    } else {
                        callback({ 'isOK': false, 'msg': 'no file or folder matching name', 'code': 404 });
                    }
                };
                self.get(parentName, test);
                return;
            }

            var data = {
                "message": "jStorage delete",
                "sha": sha
            };

            githubRequest("DELETE", addr, self._config.token, data, function () {
                if (arguments.length >= 2) {
                    var data = arguments[1].content;
                    callback({ 'isOK': true, 'msg': '', 'code': 0 });
                } else {
                    callback({ 'isOK': false, 'msg': arguments[0].request.statusText, 'code': arguments[0].request.status });
                }
            });
        },
        list: function (name, callback) {
            console.log('github list');

            // Remove begining slash
            if (name && name.indexOf('/') == 0) {
                name = name.substring(1);
            }

            // Remove ending slash
            if (name && name[name.length - 1] == '/') {
                name = name.substring(0, name.length - 1);
            }

            var self = this;
            addr = "https://api.github.com/repos/" + this._config.repo + "/contents/" + name;
            githubRequest("GET", addr, self._config.token, false, function () {
                console.log('listA', arguments);
                if (arguments.length >= 2) {
                    var info = arguments[1];
                    console.log('list', info);
                    if (info.type != "file") {

                        var list = [];
                        for (var i = 0; i < info.length; i++) {
                            list.push({
                                'name': info[i].name,
                                'path': '/' + info[i].path,
                                'size': info[i].size,
                                'mime-type': 'text/html',
                                'modified': info['last-modified']
                            });
                            self._shaCache[info[i].path] = info[i].sha;
                        }

                        console.log('list content:', list);

                        if (list.length != 0) {
                            callback(list, { 'isOK': true, 'msg': '', 'code': 0 });
                        } else {
                            callback(list, { 'isOK': false, 'msg': 'nothing to list', 'code': 404 });
                        }
                    } else {
                        callback([], { 'isOK': false, 'msg': 'This is not a valid file', 'code': -1 });
                    }
                } else {
                    callback([], { 'isOK': false, 'msg': arguments[0].request.statusText, 'code': arguments[0].request.status });
                }
            });
        },
        exists: function (name, callback) {
            console.log('github exists');
        },
        ensureAuth: function (wrapper, config) {
            // TODO: we require token today, allow for oauth fetching of token here

            //var scope = 'public_repo';
            //if (config.access && config.access == "private") {
            //    scope = 'repo';
            //}

            //// https://dev.jstorage.flowertwig.org/tests/github.html
            //var redirectUrl = window.location.protocol + '//' + window.location.host + window.location.pathname;

            //var sections = window.location.search.split("&");
            //for (var i = 0; i < sections.length; i++) {
            //    var pair = sections[i].split('=');
            //    if (pair.length == 2) {
            //        var key = pair[0];
            //        var value = pair[1];

            //        key = key.replace(/^\?/, '');

            //        console.log('key: ' + key);
            //        console.log('value: ' + value);

            //        switch (key) {
            //            case 'state':
            //                _state = value;
            //                break;
            //            case 'code':
            //                _code = value;
            //                writeCookie('jStorage-github-code', value, 1);
            //                break;
            //        }
            //    }
            //}

            //var step = readCookie('jStorage-github-step');
            //switch (step) {
            //    default:
            //        writeCookie('jStorage-github-step', 'step2');
            //        writeCookie('jStorage-github-state', this._state, 1);

            //        window.location.assign('https://github.com/login/oauth/authorize?client_id=' + config.clientId + '&redirect_uri=' + redirectUrl + '&scope=' + scope + '&state=' + this._state);
            //        break;
            //    case 'step2':
            //        // TODO: Validate so state in cookie matches the state in query param.
            //        var code = readCookie('jStorage-github-code');

            //        var addr = 'https://github.com/login/oauth/access_token';
            //        //var addr = "https://githubservice.jstorage.flowertwig.org/api/Values";
            //        var data = 'client_id=' + config.clientId + '&client_secret=' + config.clientSecret + '&code=' + code + '&redirect_uri=' + redirectUrl;

            //        //var s = document.createElement('script'),
            //        //    h = document.getElementsByTagName('head')[0];
            //        //s.src = addr;
            //        //h.appendChild(s);

            //        //document.getElementsByName('client_id')[0].value = config.clientId;
            //        //document.getElementsByName('client_secret')[0].value = config.clientSecret;
            //        //document.getElementsByName('code')[0].value = code;
            //        //document.getElementsByName('redirect_uri')[0].value = redirectUrl;

            //        githubRequest('POST', addr, false, data, function() {
            //            console.log(arguments);
            //        });

            //        //request('https://github.com/login/oauth/access_token?client_id=' + config.clientId + '&client_secret=' + config.clientSecret + '&code=' + code + '&redirect_uri=' + redirectUrl);
            //        //https://github.com/login/oauth/access_token?client_id=df3a0f28472a4ad20f39&client_secret=f3029af7a93f39319b580dea5afbce693bb56876&code=d58c49123ecdaa0ba73c&redirect_uri=https://dev.jstorage.flowertwig.org/tests/github.html
            //            //access_token=9c32c17fe2225a36cc04a0f8975b8cd64a4210e0&scope=public_repo&token_type=bearer
            //        //writeCookie('jStorage-github-step', 'step3');
            //        break;
            //    case 'step3':
            //        //eraseCookie('jStorage-github-step');
            //        //eraseCookie('jStorage-github-state');
            //        //eraseCookie('jStorage-github-code');
            //        break;
            //}
        }
    };
})(jStorage);<commit_msg>github - get returns newlines that atob can't handle so we need to remove them before calling atob<commit_after>(function (jStorage, undefined) {
    function writeCookie(name, value, days) {
        if (days) {
            var date = new Date();
            date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
            var expires = "; expires=" + date.toGMTString();
        }
        else var expires = "";
        document.cookie = name + "=" + value + expires + "; path=/";
    }

    function readCookie(name) {
        var nameEQ = name + "=";
        var ca = document.cookie.split(';');
        for (var i = 0; i < ca.length; i++) {
            var c = ca[i];
            while (c.charAt(0) == ' ') c = c.substring(1, c.length);
            if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length, c.length);
        }
        return null;
    }

    function eraseCookie(name) {
        writeCookie(name, "", -1);
    }

    function githubRequest(method, address, token, data, callback) {
        var xmlhttp;
        if (window.XMLHttpRequest) {// code for IE7+, Firefox, Chrome, Opera, Safari
            xmlhttp = new XMLHttpRequest();
        }
        else {// code for IE6, IE5
            xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
        }

        xmlhttp.dataType = "json";

        xmlhttp.open(method, address, true);

        xmlhttp.onreadystatechange = function () {
            if (this.readyState == 4) {
                if (this.status >= 200 && this.status < 300 || this.status === 304) {
                    var responseData = this.responseText ? JSON.parse(this.responseText) : {};
                    responseData['last-modified'] = this.getResponseHeader("Last-Modified");
                    callback(null, responseData, this);
                } else {
                    // This is a special for get directory calls...
                    if (this.status == 0 && address.indexOf('/contents/') > 0) {
                        callback(null, [], this);
                    }

                    callback({
                        path: address,
                        request: this,
                        error: this.status,
                    });
                }
            }
        };

        //xmlhttp.setRequestHeader('Accept', 'application/vnd.github.raw+json');
        xmlhttp.setRequestHeader('Accept', 'application/json;charset=UTF-8');
        xmlhttp.setRequestHeader('Content-Type', 'application/json;charset=UTF-8');
        //xmlhttp.setRequestHeader("Access-Control-Allow-Origin", window.location.protocol + '//' + window.location.host);
        //xmlhttp.setRequestHeader('Content-Type', 'application/xml');
        if (token) {
            xmlhttp.setRequestHeader('Authorization', 'token ' + token);
        }

        data ? xmlhttp.send(JSON.stringify(data)) : xmlhttp.send();
    }

    jStorage.providers.github = {
        init: function (wrapper, config) {
            var self = this;

            console.log('github init');

            this._config = config;
            this._hasCallback = config && typeof (config.callback) === "function";
            this._state = 'random' + new Date().getTime();
            this._code = false;
            this._shaCache = {};

            this.ensureAuth(wrapper, config);

            // https://github.com/settings/tokens/new
            var token = this._config.token;

            this._hasRepo = config && typeof (config.repo) === "string";
            this._hasToken = config && typeof (config.token) === "string";

            var callStatus = false;
            if (this._hasRepo && this._hasToken) {
                callStatus = {
                    'isOK': true,
                    'code': 0,
                    'msg': ''
                };
            } else {
                var msg = '';
                if (!this._hasToken) {
                    msg += 'No user token specified. ';
                }
                if (!this._hasRepo) {
                    msg += 'No repository specified. ';
                }
                callStatus = {
                    'isOK': false,
                    'code': -1,
                    'msg': msg
                };
            }

            if (self._hasCallback) {
                try {
                    self._config.callback(wrapper, callStatus);
                } catch (e) {
                    // TODO: handle error
                }
            }
        },
        get: function (name, callback) {
            var self = this;

            // Remove begining slash
            if (name && name.indexOf('/') == 0) {
                name = name.substring(1);
            }

            // Remove ending slash
            if (name && name[name.length - 1] == '/') {
                name = name.substring(0, name.length - 1);
            }

            addr = "https://api.github.com/repos/" + this._config.repo + "/contents/" + name;
            githubRequest("GET", addr, self._config.token, false, function () {
                console.log('GET', arguments);
                if (arguments.length >= 2) {
                    var info = arguments[1];
                    if ('length' in info) {
                        for (var i = 0; i < info.length; i++) {
                            self._shaCache[info[i].path] = info[i].sha;
                        }
                        callback(null, { 'isOK': false, 'msg': 'this is a directory', 'code': -2 });
                    }
                    else if (info.type == "file") {
                        var data = arguments[1].content;

                        if (data && data.indexOf('\n') !== -1){
                            // Fixing data format returned by github as atob doesn't know what todo with newlines.
                            data = data.replace(/\n/g, '');
                        }

                        self._shaCache[name] = info.sha;
                        callback(
                            {
                                'name': info.path,
                                'size': info.size,
                                'mime-type': 'text/html',
                                'modified': info['last-modified'],
                                'data': atob(data)
                            },
                            { 'isOK': true, 'msg': '', 'code': 0 });
                    } else {
                        callback(null, { 'isOK': false, 'msg': 'This is not a valid file', 'code': -1 });
                    }
                } else {
                    callback(null, { 'isOK': false, 'msg': arguments[0].request.statusText, 'code': arguments[0].request.status });
                }
            });
        },
        set: function (name, content, callback) {
            var self = this;

            // Remove begining slash
            if (name && name.indexOf('/') == 0) {
                name = name.substring(1);
            }

            // Remove ending slash
            if (name && name[name.length - 1] == '/') {
                name = name.substring(0, name.length - 1);
            }

            // update content of file
            addr = "https://api.github.com/repos/" + self._config.repo + "/contents/" + name;
            var data = {
                "message": "jStorage add/update",
                "content": btoa(content)
            };
            // This is required to update existing file (we need to tell github from what version we are trying to update)
            var sha = self._shaCache[name];
            if (sha) {
                data["sha"] = sha;
            }

            githubRequest("PUT", addr, self._config.token, data, function () {
                if (arguments.length >= 2) {
                    var info = arguments[1];
                    if (info.content && info.content.type == "file") {
                        callback(
                            {
                                'name': info.content.path,
                                'size': info.content.size,
                                'mime-type': 'text/html',
                                'modified': info['last-modified'],
                            },
                            { 'isOK': true, 'msg': '', 'code': 0 });
                    } else {
                        callback(null, { 'isOK': false, 'msg': 'This is not a valid file', 'code': -1 });
                    }
                } else {
                    callback(null, { 'isOK': false, 'msg': arguments[0].request.statusText, 'code': arguments[0].request.status });
                }
            });
        },
        move: function (currentName, newName, callback) {
            var self = this;

            //// Remove begining slash
            //if (name && name.indexOf('/') == 0) {
            //    name = name.substring(1);
            //}

            //// Remove ending slash
            //if (name && name[name.length - 1] == '/') {
            //    name = name.substring(0, name.length - 1);
            //}
            console.log('github move');
        },
        del: function (name, callback) {
            var self = this;

            // Remove begining slash
            if (name && name.indexOf('/') == 0) {
                name = name.substring(1);
            }

            // Remove ending slash
            if (name && name[name.length - 1] == '/') {
                name = name.substring(0, name.length - 1);
            }

            console.log('del', name);

            // update content of file
            addr = "https://api.github.com/repos/" + self._config.repo + "/contents/" + name;
            // sha is required to remove file, so we need to have called get before we can delete a file right now.
            var sha = self._shaCache[name];
            // If we don't already have sha, get it.
            if (!sha) {
                // find parent directory name
                var parentName = name;
                var index = parentName.lastIndexOf('/');
                if (index != -1) {
                    // We are looking a subdirectory
                    parentName = parentName.substring(0, index);
                } else {
                    // We are looking in root..
                    parentName = '';
                }

                var test = function (info, status) {
                    if (status.code == -2) {
                        // sha has been set, call ourself...
                        self.del(name, callback);
                    } else {
                        callback({ 'isOK': false, 'msg': 'no file or folder matching name', 'code': 404 });
                    }
                };
                self.get(parentName, test);
                return;
            }

            var data = {
                "message": "jStorage delete",
                "sha": sha
            };

            githubRequest("DELETE", addr, self._config.token, data, function () {
                if (arguments.length >= 2) {
                    var data = arguments[1].content;
                    callback({ 'isOK': true, 'msg': '', 'code': 0 });
                } else {
                    callback({ 'isOK': false, 'msg': arguments[0].request.statusText, 'code': arguments[0].request.status });
                }
            });
        },
        list: function (name, callback) {
            console.log('github list');

            // Remove begining slash
            if (name && name.indexOf('/') == 0) {
                name = name.substring(1);
            }

            // Remove ending slash
            if (name && name[name.length - 1] == '/') {
                name = name.substring(0, name.length - 1);
            }

            var self = this;
            addr = "https://api.github.com/repos/" + this._config.repo + "/contents/" + name;
            githubRequest("GET", addr, self._config.token, false, function () {
                console.log('listA', arguments);
                if (arguments.length >= 2) {
                    var info = arguments[1];
                    console.log('list', info);
                    if (info.type != "file") {

                        var list = [];
                        for (var i = 0; i < info.length; i++) {
                            list.push({
                                'name': info[i].name,
                                'path': '/' + info[i].path,
                                'size': info[i].size,
                                'mime-type': 'text/html',
                                'modified': info['last-modified']
                            });
                            self._shaCache[info[i].path] = info[i].sha;
                        }

                        console.log('list content:', list);

                        if (list.length != 0) {
                            callback(list, { 'isOK': true, 'msg': '', 'code': 0 });
                        } else {
                            callback(list, { 'isOK': false, 'msg': 'nothing to list', 'code': 404 });
                        }
                    } else {
                        callback([], { 'isOK': false, 'msg': 'This is not a valid file', 'code': -1 });
                    }
                } else {
                    callback([], { 'isOK': false, 'msg': arguments[0].request.statusText, 'code': arguments[0].request.status });
                }
            });
        },
        exists: function (name, callback) {
            console.log('github exists');
        },
        ensureAuth: function (wrapper, config) {
            // TODO: we require token today, allow for oauth fetching of token here

            //var scope = 'public_repo';
            //if (config.access && config.access == "private") {
            //    scope = 'repo';
            //}

            //// https://dev.jstorage.flowertwig.org/tests/github.html
            //var redirectUrl = window.location.protocol + '//' + window.location.host + window.location.pathname;

            //var sections = window.location.search.split("&");
            //for (var i = 0; i < sections.length; i++) {
            //    var pair = sections[i].split('=');
            //    if (pair.length == 2) {
            //        var key = pair[0];
            //        var value = pair[1];

            //        key = key.replace(/^\?/, '');

            //        console.log('key: ' + key);
            //        console.log('value: ' + value);

            //        switch (key) {
            //            case 'state':
            //                _state = value;
            //                break;
            //            case 'code':
            //                _code = value;
            //                writeCookie('jStorage-github-code', value, 1);
            //                break;
            //        }
            //    }
            //}

            //var step = readCookie('jStorage-github-step');
            //switch (step) {
            //    default:
            //        writeCookie('jStorage-github-step', 'step2');
            //        writeCookie('jStorage-github-state', this._state, 1);

            //        window.location.assign('https://github.com/login/oauth/authorize?client_id=' + config.clientId + '&redirect_uri=' + redirectUrl + '&scope=' + scope + '&state=' + this._state);
            //        break;
            //    case 'step2':
            //        // TODO: Validate so state in cookie matches the state in query param.
            //        var code = readCookie('jStorage-github-code');

            //        var addr = 'https://github.com/login/oauth/access_token';
            //        //var addr = "https://githubservice.jstorage.flowertwig.org/api/Values";
            //        var data = 'client_id=' + config.clientId + '&client_secret=' + config.clientSecret + '&code=' + code + '&redirect_uri=' + redirectUrl;

            //        //var s = document.createElement('script'),
            //        //    h = document.getElementsByTagName('head')[0];
            //        //s.src = addr;
            //        //h.appendChild(s);

            //        //document.getElementsByName('client_id')[0].value = config.clientId;
            //        //document.getElementsByName('client_secret')[0].value = config.clientSecret;
            //        //document.getElementsByName('code')[0].value = code;
            //        //document.getElementsByName('redirect_uri')[0].value = redirectUrl;

            //        githubRequest('POST', addr, false, data, function() {
            //            console.log(arguments);
            //        });

            //        //request('https://github.com/login/oauth/access_token?client_id=' + config.clientId + '&client_secret=' + config.clientSecret + '&code=' + code + '&redirect_uri=' + redirectUrl);
            //        //https://github.com/login/oauth/access_token?client_id=df3a0f28472a4ad20f39&client_secret=f3029af7a93f39319b580dea5afbce693bb56876&code=d58c49123ecdaa0ba73c&redirect_uri=https://dev.jstorage.flowertwig.org/tests/github.html
            //            //access_token=9c32c17fe2225a36cc04a0f8975b8cd64a4210e0&scope=public_repo&token_type=bearer
            //        //writeCookie('jStorage-github-step', 'step3');
            //        break;
            //    case 'step3':
            //        //eraseCookie('jStorage-github-step');
            //        //eraseCookie('jStorage-github-state');
            //        //eraseCookie('jStorage-github-code');
            //        break;
            //}
        }
    };
})(jStorage);<|endoftext|>


<commit_before>var gamejs = require('gamejs');
var WIDTH  = 640
var HEIGHT = 480

/**
 * Class representing a building on the stage. Buildings have HP (only
 * one for now) and can be destroyed upon taking damage.
 *
 * TODO:
 *  - Implement a "draw" function which draws the Building differently
 *    depending on relative HP.
 *  - Implement a "damage" function which decreases the amount of HP
 *    available.
 *  - Implement a "destroy" function to update variables, collision mask,
 *    etc. when the building's HP has reached zero.
 */
function Building(x, y) {
	this.x = x;
	this.y = y;
	this.width = 64;
	this.height = 64;
	this.image = gamejs.transform.scale(gamejs.image.load("resources/cottage1.png"), [this.width, this.height]);
	this.hp = 1;

	this.draw = function(surface) {
		//var rect = new gamejs.Rect(this.x, this.y, this.width, this.height)
		//gamejs.draw.rect(surface, this.color, rect, 0);
		surface.blit(this.image, [this.x, this.y]);
	}

	this.destroy = function() {
		this.hp = 0;
		this.image = gamejs.transform.scale(gamejs.image.load("resources/cottage0.png"), [this.width, this.height]);
	}

	return this;
}

/**
 * Class representing a milk chocolate "grenade" to be tossed by Discord.
 *
 * TODO:
 *  - Implement a "draw" function which draws the Grenade as it moves
 *    along its path.
 *  - Implement a "launch" function which follows a parabolic path to a
 *    destination point.
 *  - Implement a "destroy" function which called whenever the Grenade
 *    is no more (either detonated or fire-breathed).
 *  - Implement a "detonate" function which causes the draw function to
 *    make a detonation animation and finds nearby Buildings on the Stage
 *    to damage.
 */
function Grenade(x, y) {
	this.x = x;
	this.y = y;
	this.y_dest = Math.random() * HEIGHT;
	this.y_sp_toss = -3;
	this.x_sp_toss = 3 * Math.random();
	this.detonated = false;

	this.image = [gamejs.transform.scale(gamejs.image.load("resources/milk_grenade_01.png"), [16, 16]),
	              gamejs.transform.scale(gamejs.image.load("resources/milk_grenade_02.png"), [16, 16]),
	              gamejs.transform.scale(gamejs.image.load("resources/milk_grenade_03.png"), [16, 16]),
	              gamejs.transform.scale(gamejs.image.load("resources/milk_grenade_04.png"), [16, 16]),
	              gamejs.transform.scale(gamejs.image.load("resources/milk_grenade_05.png"), [16, 16]),
	              gamejs.transform.scale(gamejs.image.load("resources/milk_grenade_06.png"), [16, 16])
	             ];
	this.crater = gamejs.transform.scale(gamejs.image.load("resources/cottage0.png"), [64,64]);

	this.delay = 90;
	this.movetime = 0;

	this.getframe = function() {
		var frame = Math.floor((this.movetime/this.delay)) % this.image.length;
		return frame;
	}

	this.draw = function (surface) {
		var image = this.image[this.getframe()]
		if (this.detonated)
			image = this.crater;

		surface.blit(image, [this.x, this.y]);
	}

	this.update = function(msDuration) {
		if (!this.detonated) {
			this.movetime = this.movetime + msDuration;
			var y_speed = this.y_sp_toss + (this.movetime * 0.001);
			this.y = this.y + y_speed;
			this.x = this.x + this.x_sp_toss;

			if (this.y > this.y_dest)
				this.detonated = true;
		}
	}
}

/**
 * Class representing the villian in this game. He shouldn't need to be
 * a graphic which periodically spawns Grenades.
 *
 * DONE:
 *  - Implement a "draw" function which draws Discord on his throne.
 *
 * TODO:
 *  - Start a worker thread which causes him to periodically throw
 *    a new Grenade.
 *  - Animate Discord and the chocolate milk cloud
 */
function Discord(stage) {
	this.stage = stage;
	this.x = 75;
	this.y = 80;
	this.grenade = null;
	this.image = [gamejs.transform.scale(gamejs.image.load("resources/throne_00.png"), [96, 96]),
	              gamejs.transform.scale(gamejs.image.load("resources/throne_01.png"), [96, 96]),
	              gamejs.transform.scale(gamejs.image.load("resources/throne_02.png"), [96, 96]),
	              gamejs.transform.scale(gamejs.image.load("resources/throne_03.png"), [96, 96])
	             ];

	this.cloud = [gamejs.transform.scale(gamejs.image.load("resources/cloud_00.png"), [64, 64]),
	              gamejs.transform.scale(gamejs.image.load("resources/cloud_01.png"), [64, 64]),
	              gamejs.transform.scale(gamejs.image.load("resources/cloud_02.png"), [64, 64]),
	              gamejs.transform.scale(gamejs.image.load("resources/cloud_03.png"), [64, 64]),
	              gamejs.transform.scale(gamejs.image.load("resources/cloud_04.png"), [64, 64])
	             ];

	this.toss = function() {
		this.grenade = new Grenade(this.x+60, this.y+35);
	}

	this.draw = function(surface) {
		//var rect = new gamejs.Rect(this.x, this.y, 75, 125)
		//gamejs.draw.rect(surface, "#00AAFF", rect, 0);
		this.grenade.draw(surface);
		var image = this.image[3];
		surface.blit(this.cloud[0], [this.x - 20, this.y - 20]);
		surface.blit(image, [this.x, this.y]);
	}

	this.update = function(msDuration) {
		if (this.grenade != null)
			this.grenade.update(msDuration);
		if (this.grenade.detonated)
			this.toss();
	}
}

/**
 * Class representing the player.
 *
 * DONE:
 *  - Move around the screen in response to arrow keys
 *
 * TODO:
 *  - Collide with Buildings present on the Stage.
 *  - Produce Dragons Breath when the spacebar is pressed
 */
function Player(stage) {
	this.stage = stage;
	this.x = 100;
	this.y = 50;
	this.x_speed = 0;
	this.y_speed = 0;
	this.image = [gamejs.transform.scale(gamejs.image.load("resources/spike_run_N_01.png"), [48, 48]),
	              gamejs.transform.scale(gamejs.image.load("resources/spike_run_N_02.png"), [48, 48]),
	              gamejs.transform.scale(gamejs.image.load("resources/spike_run_N_03.png"), [48, 48]),
	              gamejs.transform.scale(gamejs.image.load("resources/spike_run_N_04.png"), [48, 48])
	             ];
	this.delay = 50;
	this.movetime = 0;

	this.getframe = function() {
		var frame = Math.floor((this.movetime/this.delay)) % this.image.length;
		return frame;
	}

	this.draw = function (surface) {
		//gamejs.draw.circle(surface, "#00AAFF", [this.x,this.y], 50, 0);
		surface.blit(this.image[this.getframe()], [this.x, this.y]);
	}

	this.notify = function(event) {
		if (event.type === gamejs.event.KEY_UP || event.type === gamejs.event.KEY_DOWN) {
			var speed = 0.1;
			if (event.type === gamejs.event.KEY_UP)
				speed = 0;

			switch (event.key) {
				case gamejs.event.K_LEFT:  this.x_speed = -speed;  break;
				case gamejs.event.K_RIGHT: this.x_speed = speed; break;
				case gamejs.event.K_UP:    this.y_speed = -speed; break;
				case gamejs.event.K_DOWN:  this.y_speed = speed;  break;

				case gamejs.event.K_TAB:
					if (event.type === gamejs.event.KEY_DOWN)
						this.stage.destroy();
					break;
				default:
			}
		}
	}

	this.update = function(msDuration) {
		this.x = Math.round(this.x + this.x_speed * msDuration);
		this.y = Math.round(this.y + this.y_speed * msDuration);

		if (this.x_speed != 0 || this.y_speed != 0)
			this.movetime = this.movetime + msDuration;
	}

	return this;
}

/**
 * The stage on which the game takes place -- Ponyville.
 *
 * DONE:
 *  - Populate with Player
 *  - Populate with Buildings
 *
 * TODO:
 *  - Populate with Discord
 */
function Stage() {
	this.color = "#FFFFFF";
	this.player = new Player(this);
	this.discord = new Discord(this);
	this.buildings = [new Building(470,300), new Building(250,400), new Building(180, 325),
	                  new Building(500,400), new Building(38, 350), new Building(575, 275)];

	this.discord.toss();

	this.destroy = function(event) {
		var i = Math.floor(Math.random()*this.buildings.length);
		this.buildings[i].destroy();
	}

	this.notify = function(event) {
		this.player.notify(event);
	}

	this.update = function(msDuration) {
		this.player.update(msDuration);
		this.discord.update(msDuration);
	}

	this.draw = function(surface) {
		surface.fill(this.color);
		this.discord.draw(surface);
		for (var i = 0; i < this.buildings.length; i++)
			this.buildings[i].draw(surface);
		this.player.draw(surface);
	}
}

function main() {

	var display = gamejs.display.setMode([WIDTH, HEIGHT]);
	var stage = new Stage()

	function tick(msDuration) {
		var events = gamejs.event.get();
		events.forEach(function(event) {stage.notify(event)});

		stage.update(msDuration);
		stage.draw(display);
		return;
	};

	gamejs.time.fpsCallback(tick, this, 26);
}

gamejs.preload(["resources/cloud_00.png", "resources/cloud_01.png", "resources/cloud_02.png", "resources/cloud_03.png",
"resources/cloud_04.png",
"resources/throne_00.png", "resources/throne_01.png", "resources/throne_02.png",
"resources/throne_03.png", "resources/milk_grenade_01.png", "resources/milk_grenade_02.png",
"resources/milk_grenade_03.png", "resources/milk_grenade_04.png", "resources/milk_grenade_05.png",
"resources/milk_grenade_06.png", "resources/cottage0.png", "resources/cottage1.png",
"resources/spike_run_N_01.png", "resources/spike_run_N_02.png", "resources/spike_run_N_03.png",
"resources/spike_run_N_04.png"]);
gamejs.ready(main);
<commit_msg>Animate cloud<commit_after>var gamejs = require('gamejs');
var WIDTH  = 640
var HEIGHT = 480

/**
 * Class representing a building on the stage. Buildings have HP (only
 * one for now) and can be destroyed upon taking damage.
 *
 * TODO:
 *  - Implement a "draw" function which draws the Building differently
 *    depending on relative HP.
 *  - Implement a "damage" function which decreases the amount of HP
 *    available.
 *  - Implement a "destroy" function to update variables, collision mask,
 *    etc. when the building's HP has reached zero.
 */
function Building(x, y) {
	this.x = x;
	this.y = y;
	this.width = 64;
	this.height = 64;
	this.image = gamejs.transform.scale(gamejs.image.load("resources/cottage1.png"), [this.width, this.height]);
	this.hp = 1;

	this.draw = function(surface) {
		//var rect = new gamejs.Rect(this.x, this.y, this.width, this.height)
		//gamejs.draw.rect(surface, this.color, rect, 0);
		surface.blit(this.image, [this.x, this.y]);
	}

	this.destroy = function() {
		this.hp = 0;
		this.image = gamejs.transform.scale(gamejs.image.load("resources/cottage0.png"), [this.width, this.height]);
	}

	return this;
}

/**
 * Class representing a milk chocolate "grenade" to be tossed by Discord.
 *
 * TODO:
 *  - Implement a "draw" function which draws the Grenade as it moves
 *    along its path.
 *  - Implement a "launch" function which follows a parabolic path to a
 *    destination point.
 *  - Implement a "destroy" function which called whenever the Grenade
 *    is no more (either detonated or fire-breathed).
 *  - Implement a "detonate" function which causes the draw function to
 *    make a detonation animation and finds nearby Buildings on the Stage
 *    to damage.
 */
function Grenade(x, y) {
	this.x = x;
	this.y = y;
	this.y_dest = Math.random() * HEIGHT;
	this.y_sp_toss = -3;
	this.x_sp_toss = 3 * Math.random();
	this.detonated = false;

	this.image = [gamejs.transform.scale(gamejs.image.load("resources/milk_grenade_01.png"), [16, 16]),
	              gamejs.transform.scale(gamejs.image.load("resources/milk_grenade_02.png"), [16, 16]),
	              gamejs.transform.scale(gamejs.image.load("resources/milk_grenade_03.png"), [16, 16]),
	              gamejs.transform.scale(gamejs.image.load("resources/milk_grenade_04.png"), [16, 16]),
	              gamejs.transform.scale(gamejs.image.load("resources/milk_grenade_05.png"), [16, 16]),
	              gamejs.transform.scale(gamejs.image.load("resources/milk_grenade_06.png"), [16, 16])
	             ];
	this.crater = gamejs.transform.scale(gamejs.image.load("resources/cottage0.png"), [64,64]);

	this.delay = 90;
	this.movetime = 0;

	this.getframe = function() {
		var frame = Math.floor((this.movetime/this.delay)) % this.image.length;
		return frame;
	}

	this.draw = function (surface) {
		var image = this.image[this.getframe()]
		if (this.detonated)
			image = this.crater;

		surface.blit(image, [this.x, this.y]);
	}

	this.update = function(msDuration) {
		if (!this.detonated) {
			this.movetime = this.movetime + msDuration;
			var y_speed = this.y_sp_toss + (this.movetime * 0.001);
			this.y = this.y + y_speed;
			this.x = this.x + this.x_sp_toss;

			if (this.y > this.y_dest)
				this.detonated = true;
		}
	}
}

/**
 * Class representing the villian in this game. He shouldn't need to be
 * a graphic which periodically spawns Grenades.
 *
 * DONE:
 *  - Implement a "draw" function which draws Discord on his throne.
 *
 * TODO:
 *  - Start a worker thread which causes him to periodically throw
 *    a new Grenade.
 *  - Animate Discord and the chocolate milk cloud
 */
function Discord(stage) {
	this.stage = stage;
	this.x = 75;
	this.y = 80;
	this.grenade = null;
	this.image = [gamejs.transform.scale(gamejs.image.load("resources/throne_00.png"), [96, 96]),
	              gamejs.transform.scale(gamejs.image.load("resources/throne_01.png"), [96, 96]),
	              gamejs.transform.scale(gamejs.image.load("resources/throne_02.png"), [96, 96]),
	              gamejs.transform.scale(gamejs.image.load("resources/throne_03.png"), [96, 96])
	             ];
	this.cloud = new Cloud(this.x - 20, this.y - 20);

	this.toss = function() {
		this.grenade = new Grenade(this.x+60, this.y+35);
	}

	this.draw = function(surface) {
		this.cloud.draw(surface);
		var image = this.image[3];
		surface.blit(image, [this.x, this.y]);
		this.grenade.draw(surface);
	}

	this.update = function(msDuration) {
		this.cloud.update(msDuration);
		if (this.grenade != null)
			this.grenade.update(msDuration);
		if (this.grenade.detonated)
			this.toss();
	}
}

function Cloud(x, y) {
	this.x = x;
	this.y = y;
	this.image = [gamejs.transform.scale(gamejs.image.load("resources/cloud_00.png"), [64, 64]),
	              gamejs.transform.scale(gamejs.image.load("resources/cloud_01.png"), [64, 64]),
	              gamejs.transform.scale(gamejs.image.load("resources/cloud_02.png"), [64, 64]),
	              gamejs.transform.scale(gamejs.image.load("resources/cloud_03.png"), [64, 64]),
	              gamejs.transform.scale(gamejs.image.load("resources/cloud_04.png"), [64, 64])
	             ];
	this.movetime = 0;
	this.delay = 100;

	this.getframe = function() {
		var frame = Math.floor((this.movetime/this.delay)) % this.image.length;
		return frame;
	}

	this.draw = function(surface) {
		surface.blit(this.image[this.getframe()], [this.x, this.y]);
	}

	this.update = function(msDuration) {
		this.movetime = this.movetime + msDuration;
	}
}

/**
 * Class representing the player.
 *
 * DONE:
 *  - Move around the screen in response to arrow keys
 *
 * TODO:
 *  - Collide with Buildings present on the Stage.
 *  - Produce Dragons Breath when the spacebar is pressed
 */
function Player(stage) {
	this.stage = stage;
	this.x = 100;
	this.y = 50;
	this.x_speed = 0;
	this.y_speed = 0;
	this.image = [gamejs.transform.scale(gamejs.image.load("resources/spike_run_N_01.png"), [48, 48]),
	              gamejs.transform.scale(gamejs.image.load("resources/spike_run_N_02.png"), [48, 48]),
	              gamejs.transform.scale(gamejs.image.load("resources/spike_run_N_03.png"), [48, 48]),
	              gamejs.transform.scale(gamejs.image.load("resources/spike_run_N_04.png"), [48, 48])
	             ];
	this.delay = 50;
	this.movetime = 0;

	this.getframe = function() {
		var frame = Math.floor((this.movetime/this.delay)) % this.image.length;
		return frame;
	}

	this.draw = function (surface) {
		//gamejs.draw.circle(surface, "#00AAFF", [this.x,this.y], 50, 0);
		surface.blit(this.image[this.getframe()], [this.x, this.y]);
	}

	this.notify = function(event) {
		if (event.type === gamejs.event.KEY_UP || event.type === gamejs.event.KEY_DOWN) {
			var speed = 0.1;
			if (event.type === gamejs.event.KEY_UP)
				speed = 0;

			switch (event.key) {
				case gamejs.event.K_LEFT:  this.x_speed = -speed;  break;
				case gamejs.event.K_RIGHT: this.x_speed = speed; break;
				case gamejs.event.K_UP:    this.y_speed = -speed; break;
				case gamejs.event.K_DOWN:  this.y_speed = speed;  break;

				case gamejs.event.K_TAB:
					if (event.type === gamejs.event.KEY_DOWN)
						this.stage.destroy();
					break;
				default:
			}
		}
	}

	this.update = function(msDuration) {
		this.x = Math.round(this.x + this.x_speed * msDuration);
		this.y = Math.round(this.y + this.y_speed * msDuration);

		if (this.x_speed != 0 || this.y_speed != 0)
			this.movetime = this.movetime + msDuration;
	}

	return this;
}

/**
 * The stage on which the game takes place -- Ponyville.
 *
 * DONE:
 *  - Populate with Player
 *  - Populate with Buildings
 *
 * TODO:
 *  - Populate with Discord
 */
function Stage() {
	this.color = "#FFFFFF";
	this.player = new Player(this);
	this.discord = new Discord(this);
	this.buildings = [new Building(470,300), new Building(250,400), new Building(180, 325),
	                  new Building(500,400), new Building(38, 350), new Building(575, 275)];

	this.discord.toss();

	this.destroy = function(event) {
		var i = Math.floor(Math.random()*this.buildings.length);
		this.buildings[i].destroy();
	}

	this.notify = function(event) {
		this.player.notify(event);
	}

	this.update = function(msDuration) {
		this.player.update(msDuration);
		this.discord.update(msDuration);
	}

	this.draw = function(surface) {
		surface.fill(this.color);
		this.discord.draw(surface);
		for (var i = 0; i < this.buildings.length; i++)
			this.buildings[i].draw(surface);
		this.player.draw(surface);
	}
}

function main() {

	var display = gamejs.display.setMode([WIDTH, HEIGHT]);
	var stage = new Stage()

	function tick(msDuration) {
		var events = gamejs.event.get();
		events.forEach(function(event) {stage.notify(event)});

		stage.update(msDuration);
		stage.draw(display);
		return;
	};

	gamejs.time.fpsCallback(tick, this, 26);
}

gamejs.preload(["resources/cloud_00.png", "resources/cloud_01.png", "resources/cloud_02.png", "resources/cloud_03.png",
"resources/cloud_04.png",
"resources/throne_00.png", "resources/throne_01.png", "resources/throne_02.png",
"resources/throne_03.png", "resources/milk_grenade_01.png", "resources/milk_grenade_02.png",
"resources/milk_grenade_03.png", "resources/milk_grenade_04.png", "resources/milk_grenade_05.png",
"resources/milk_grenade_06.png", "resources/cottage0.png", "resources/cottage1.png",
"resources/spike_run_N_01.png", "resources/spike_run_N_02.png", "resources/spike_run_N_03.png",
"resources/spike_run_N_04.png"]);
gamejs.ready(main);
<|endoftext|>


<commit_before>var fs = require('fs')

var semver = require('semver')
var merge = require('merge')

var InvalidDependencyError = require('./invalid_dependency_error')
var MissingDependenciesError = require('./missing_dependencies_error')
var UnsavedDependenciesError = require('./unsaved_dependencies_error')

function getInstalledDependencies (nodeModulesPath) {
  var installedDependencies = {}
  fs.readdirSync(nodeModulesPath).forEach(function (directory) {
    if (directory[0] !== '.') {
      installedDependencies[directory] = JSON.parse(
        fs.readFileSync('./node_modules/' + directory + '/package.json')
      ).version
    }
  })
  return installedDependencies
}

function removeDependenciesPresent (required, installed) {
  Object.keys(required).forEach(function (dependency) {
    if (installed[dependency]) {
      var requiredVersion = required[dependency]
      var installedVersion = installed[dependency]
      var isValidVersion = semver.satisfies(
        installedVersion, requiredVersion
      )
      if (isValidVersion || requiredVersion.indexOf('git') >= 0) {
        delete required[dependency]
        delete installed[dependency]
      } else {
        throw new InvalidDependencyError(
          dependency, requiredVersion, installedVersion
        )
      }
    }
  })
}

function getRequiredDependencies (packageInfo) {
  var dependencies = packageInfo.dependencies
  var devDependencies = {}
  if (['development', 'test'].indexOf(process.env.NODE_ENV) >= 0) {
    devDependencies = packageInfo.devDependencies
  }
  return merge(
    {}, dependencies, devDependencies
  )
}

module.exports = function checkModules (packageInfo, nodeModulesPath) {
  var installedDependencies = getInstalledDependencies(nodeModulesPath)
  var requiredDependencies = getRequiredDependencies(packageInfo)

  removeDependenciesPresent(requiredDependencies, installedDependencies)

  var missingDependencies = Object.keys(requiredDependencies)
  if (missingDependencies.length > 0) {
    throw new MissingDependenciesError(missingDependencies)
  }

  var unsavedDependencies = Object.keys(installedDependencies)
  if (unsavedDependencies.length > 0) {
    throw new UnsavedDependenciesError(unsavedDependencies)
  }
}
<commit_msg>Handle local dependencies<commit_after>var fs = require('fs')

var semver = require('semver')
var merge = require('merge')

var InvalidDependencyError = require('./invalid_dependency_error')
var MissingDependenciesError = require('./missing_dependencies_error')
var UnsavedDependenciesError = require('./unsaved_dependencies_error')

function getInstalledDependencies (nodeModulesPath) {
  var installedDependencies = {}
  fs.readdirSync(nodeModulesPath).forEach(function (directory) {
    if (directory[0] !== '.') {
      installedDependencies[directory] = JSON.parse(
        fs.readFileSync('./node_modules/' + directory + '/package.json')
      ).version
    }
  })
  return installedDependencies
}

function isGitDependency (version) {
  return version.indexOf('git') >= 0
}

function isLocalDependency (version) {
  return version.trim()[0] === '.'
}

function versionsMatch (installedVersion, requiredVersion) {
  var isValidVersion = semver.satisfies(
    installedVersion, requiredVersion
  )
  return isValidVersion || isGitDependency(requiredVersion) ||
    isLocalDependency(requiredVersion)
}

function removeDependenciesPresent (required, installed) {
  Object.keys(required).forEach(function (dependency) {
    if (installed[dependency]) {
      var requiredVersion = required[dependency]
      var installedVersion = installed[dependency]
      if (versionsMatch(installedVersion, requiredVersion)) {
        delete required[dependency]
        delete installed[dependency]
      } else {
        throw new InvalidDependencyError(
          dependency, requiredVersion, installedVersion
        )
      }
    }
  })
}

function getRequiredDependencies (packageInfo) {
  var dependencies = packageInfo.dependencies
  var devDependencies = {}
  if (['development', 'test'].indexOf(process.env.NODE_ENV) >= 0) {
    devDependencies = packageInfo.devDependencies
  }
  return merge(
    {}, dependencies, devDependencies
  )
}

module.exports = function checkModules (packageInfo, nodeModulesPath) {
  var installedDependencies = getInstalledDependencies(nodeModulesPath)
  var requiredDependencies = getRequiredDependencies(packageInfo)

  removeDependenciesPresent(requiredDependencies, installedDependencies)

  var missingDependencies = Object.keys(requiredDependencies)
  if (missingDependencies.length > 0) {
    throw new MissingDependenciesError(missingDependencies)
  }

  var unsavedDependencies = Object.keys(installedDependencies)
  if (unsavedDependencies.length > 0) {
    throw new UnsavedDependenciesError(unsavedDependencies)
  }
}
<|endoftext|>


<commit_before>var CloudCmd, Util, DOM, io;

(function(CloudCmd, Util, DOM) {
    'use strict';

    CloudCmd.Config = ConfigProto;

    function ConfigProto() {
        var Loading     = true,
            Key         = CloudCmd.Key,
            Images      = DOM.Images,
            Events      = DOM.Events,
            showLoad    = Images.showLoad.bind(DOM, {
                top: true
            }),
            Element,
            INPUT       = 'INPUT',
            CONFIG,
            Template,
            Notify      = DOM.Notify,
            Config      = this;

        function init() {
            Loading     = true;

            showLoad();
            Util.exec.series([
                CloudCmd.View,
                function(callback) {
                    Loading = false;
                    Util.exec(callback);
                    DOM.loadSocket(initSocket);
                },
                Config.show
            ]);
        }

        function getHost() {
            var l       = location,
                href    = l.origin || l.protocol + '//' + l.host;

            return href;
        }

        function initSocket(error) {
            var socket,
                href            = getHost(),
                FIVE_SECONDS    = 5000,
                save    = function(data) {
                    socket.send(data);
                };

            if (!error) {
                socket  = io.connect(href + '/config', {
                    'max reconnection attempts' : Math.pow(2, 32),
                    'reconnection limit'        : FIVE_SECONDS
                });

                socket.on('connect', function() {
                    Config.save = save;
                });

                socket.on('message', function(data) {
                    onSave(data);
                });

                socket.on('log', function(msg) {
                    Util.log(msg);
                });

                socket.on('disconnect', function() {
                    Config.save = saveHttp;
                });

                socket.on('err', function(error) {
                    Util.log(error);
                });
            }
        }

        Config.save             = saveHttp;

        this.show               = function() {
            var prefix  = CloudCmd.PREFIX,
                exec    = Util.exec,
                funcs   = [
                    exec.with(DOM.Files.get, 'config-tmpl'),
                    exec.with(DOM.load.css, prefix + '/css/config.css')
                ];

            if (!Loading) {
                showLoad();
                exec.parallel(funcs, fillTemplate);
            }
        };

        function fillTemplate(error, template) {
            if (!Template)
                Template = template;

            DOM.Files.get('config', function(error, config) {
                var div, data, inputs, inputFirst,
                    focus, obj;

                CONFIG  = config;
                obj     = Util.copyObj(CONFIG);
                changeConfig(obj);
                data    = Util.render(Template, obj);
                div     = DOM.load({
                    name        : 'div',
                    className   : 'config',
                    inner       : data,
                    attribute   : {
                        'data-name': 'js-config'
                    }
                });

                Element = div;

                inputs      = DOM.getByTag(INPUT, div);
                inputFirst  = inputs[0];

                if (inputFirst) {
                    onAuthChange(inputFirst.checked);

                    focus   = function() {
                        inputFirst.focus();
                    };
                }

                Util.forEach(inputs, function(input) {
                    Events.addKey(input, onKey)
                          .add('change', input, function(event) {
                              onChange(event.target);
                          });
                });

                CloudCmd.View.show(div, {
                    autoSize: true,
                    afterShow: focus
                });
            });
        }

        this.hide               = function() {
            CloudCmd.View.hide();
        };

        function changeConfig(config) {
            Object.keys(config).forEach(function(name) {
                var item    = config[name],
                    isBool  = Util.type.boolean(item);

                if (isBool)
                    config[name] = setState(item);
            });
        }

        function setState(state) {
            var ret = '';

            if (state)
                ret = ' checked';

            return ret;
        }

        function onChange(el) {
            var data,
                obj     = {},
                name    = el.id,
                type    = el.type;

            data        = getValue(el);

            if (type === 'checkbox')
                if (el.id === 'localStorage')
                    onLocalStorageChange();
                else if (el.id === 'diff')
                    onLSChange(el);
                else if (el.id === 'buffer')
                    onLSChange(el);
                else if (el.id === 'dirStorage')
                    onLSChange(el);
                else if (el.id === 'auth')
                    onAuthChange(data);

            if (name === 'notifications') {
                if (data && !Notify.check())
                    Notify.request();
            }

            obj[name] = data;

            Config.save(obj);
        }

        function onSave(obj) {
            Object.keys(obj).forEach(function(name) {
                var data = obj[name];

                CONFIG[name] = data;
                setValue(name, data);
            });

            DOM.Files.set('config', CONFIG);
        }

        function saveHttp(obj) {
            var RESTful = DOM.RESTful;

            RESTful.Config.write(obj, function() {
                onSave(obj);
            });
        }

        function getValue(el) {
            var data,
                type    = el.type;

            switch(type) {
            case 'checkbox':
                data = el.checked;

                break;
            case 'number':
                data = el.value - 0;
                break;
            default:
                data = el.value;
                break;
            }

            return data;
        }

        function setValue(name, value) {
            var el      = DOM.getById(name, Element),
                type    = el.type;

            switch(type) {
            case 'checkbox':
                el.checked = value;
                break;

            default:
                el.value    = value;
                break;
            }
        }

        function onLocalStorageChange() {
            var names           = ['diff', 'buffer', 'dirStorage', 'localStorage'],
                elements        = names.map(function(name) {
                    return DOM.getById(name);
                }),

                el              = {},
                msg             = 'Diff, Buffer and Directory Storage do not work without localStorage',
                isChecked;

            elements.forEach(function(element) {
                var name    = element.id;

                el[name]    = element;

                if (element.checked)
                    isChecked = true;
            });

            if (isChecked && !el.localStorage.checked) {
                alert(msg);

                elements.forEach(function(element) {
                    if (element.checked) {
                        element.checked = false;

                        onChange(element);
                    }

                    return element;
                });
            }
        }

        function onLSChange(el) {
            var elLocalStorage  = DOM.getById('localStorage', Element),
                msg             = el.id + ' depends on localStorage';

            if (el.checked && !elLocalStorage.checked) {
                alert(msg);
                elLocalStorage.checked = true;
            }
        }

        function onAuthChange(checked) {
            var elUsername      = DOM.getById('username', Element),
                elPassword      = DOM.getById('password', Element);

            elUsername.disabled =
            elPassword.disabled = !checked;
        }

        function onKey(event) {
            var keyCode = event.keyCode,
                ESC     = Key.ESC;

            if (keyCode === ESC)
                Config.hide();
        }

        init();
    }

})(CloudCmd, Util, DOM);
<commit_msg>feature(config) log -> alert<commit_after>var CloudCmd, Util, DOM, io;

(function(CloudCmd, Util, DOM) {
    'use strict';

    CloudCmd.Config = ConfigProto;

    function ConfigProto() {
        var Loading     = true,
            Key         = CloudCmd.Key,
            Images      = DOM.Images,
            Events      = DOM.Events,
            showLoad    = Images.showLoad.bind(DOM, {
                top: true
            }),
            Element,
            INPUT       = 'INPUT',
            CONFIG,
            Template,
            Notify      = DOM.Notify,
            Config      = this;

        function init() {
            Loading     = true;

            showLoad();
            Util.exec.series([
                CloudCmd.View,
                function(callback) {
                    Loading = false;
                    Util.exec(callback);
                    DOM.loadSocket(initSocket);
                },
                Config.show
            ]);
        }

        function getHost() {
            var l       = location,
                href    = l.origin || l.protocol + '//' + l.host;

            return href;
        }

        function initSocket(error) {
            var socket,
                href            = getHost(),
                FIVE_SECONDS    = 5000,
                save    = function(data) {
                    socket.send(data);
                };

            if (!error) {
                socket  = io.connect(href + '/config', {
                    'max reconnection attempts' : Math.pow(2, 32),
                    'reconnection limit'        : FIVE_SECONDS
                });

                socket.on('connect', function() {
                    Config.save = save;
                });

                socket.on('message', function(data) {
                    onSave(data);
                });

                socket.on('log', function(msg) {
                    Util.log(msg);
                });

                socket.on('disconnect', function() {
                    Config.save = saveHttp;
                });

                socket.on('err', function(error) {
                    DOM.Dialog.alert(error);
                });
            }
        }

        Config.save             = saveHttp;

        this.show               = function() {
            var prefix  = CloudCmd.PREFIX,
                exec    = Util.exec,
                funcs   = [
                    exec.with(DOM.Files.get, 'config-tmpl'),
                    exec.with(DOM.load.css, prefix + '/css/config.css')
                ];

            if (!Loading) {
                showLoad();
                exec.parallel(funcs, fillTemplate);
            }
        };

        function fillTemplate(error, template) {
            if (!Template)
                Template = template;

            DOM.Files.get('config', function(error, config) {
                var div, data, inputs, inputFirst,
                    focus, obj;

                CONFIG  = config;
                obj     = Util.copyObj(CONFIG);
                changeConfig(obj);
                data    = Util.render(Template, obj);
                div     = DOM.load({
                    name        : 'div',
                    className   : 'config',
                    inner       : data,
                    attribute   : {
                        'data-name': 'js-config'
                    }
                });

                Element = div;

                inputs      = DOM.getByTag(INPUT, div);
                inputFirst  = inputs[0];

                if (inputFirst) {
                    onAuthChange(inputFirst.checked);

                    focus   = function() {
                        inputFirst.focus();
                    };
                }

                Util.forEach(inputs, function(input) {
                    Events.addKey(input, onKey)
                          .add('change', input, function(event) {
                              onChange(event.target);
                          });
                });

                CloudCmd.View.show(div, {
                    autoSize: true,
                    afterShow: focus
                });
            });
        }

        this.hide               = function() {
            CloudCmd.View.hide();
        };

        function changeConfig(config) {
            Object.keys(config).forEach(function(name) {
                var item    = config[name],
                    isBool  = Util.type.boolean(item);

                if (isBool)
                    config[name] = setState(item);
            });
        }

        function setState(state) {
            var ret = '';

            if (state)
                ret = ' checked';

            return ret;
        }

        function onChange(el) {
            var data,
                obj     = {},
                name    = el.id,
                type    = el.type;

            data        = getValue(el);

            if (type === 'checkbox')
                if (el.id === 'localStorage')
                    onLocalStorageChange();
                else if (el.id === 'diff')
                    onLSChange(el);
                else if (el.id === 'buffer')
                    onLSChange(el);
                else if (el.id === 'dirStorage')
                    onLSChange(el);
                else if (el.id === 'auth')
                    onAuthChange(data);

            if (name === 'notifications') {
                if (data && !Notify.check())
                    Notify.request();
            }

            obj[name] = data;

            Config.save(obj);
        }

        function onSave(obj) {
            Object.keys(obj).forEach(function(name) {
                var data = obj[name];

                CONFIG[name] = data;
                setValue(name, data);
            });

            DOM.Files.set('config', CONFIG);
        }

        function saveHttp(obj) {
            var RESTful = DOM.RESTful;

            RESTful.Config.write(obj, function() {
                onSave(obj);
            });
        }

        function getValue(el) {
            var data,
                type    = el.type;

            switch(type) {
            case 'checkbox':
                data = el.checked;

                break;
            case 'number':
                data = el.value - 0;
                break;
            default:
                data = el.value;
                break;
            }

            return data;
        }

        function setValue(name, value) {
            var el      = DOM.getById(name, Element),
                type    = el.type;

            switch(type) {
            case 'checkbox':
                el.checked = value;
                break;

            default:
                el.value    = value;
                break;
            }
        }

        function onLocalStorageChange() {
            var names           = ['diff', 'buffer', 'dirStorage', 'localStorage'],
                elements        = names.map(function(name) {
                    return DOM.getById(name);
                }),

                el              = {},
                msg             = 'Diff, Buffer and Directory Storage do not work without localStorage',
                isChecked;

            elements.forEach(function(element) {
                var name    = element.id;

                el[name]    = element;

                if (element.checked)
                    isChecked = true;
            });

            if (isChecked && !el.localStorage.checked) {
                alert(msg);

                elements.forEach(function(element) {
                    if (element.checked) {
                        element.checked = false;

                        onChange(element);
                    }

                    return element;
                });
            }
        }

        function onLSChange(el) {
            var elLocalStorage  = DOM.getById('localStorage', Element),
                msg             = el.id + ' depends on localStorage';

            if (el.checked && !elLocalStorage.checked) {
                alert(msg);
                elLocalStorage.checked = true;
            }
        }

        function onAuthChange(checked) {
            var elUsername      = DOM.getById('username', Element),
                elPassword      = DOM.getById('password', Element);

            elUsername.disabled =
            elPassword.disabled = !checked;
        }

        function onKey(event) {
            var keyCode = event.keyCode,
                ESC     = Key.ESC;

            if (keyCode === ESC)
                Config.hide();
        }

        init();
    }

})(CloudCmd, Util, DOM);
<|endoftext|>