<commit_before>(function() {
  'use strict';

  function _requirePartial(rvPartial, callback) {
    var src = rvPartial.getAttribute('src') || false,
        target = rvPartial.getAttribute('target');

    if (!target) {
      return callback(false);
    }

    if (src) {
      window.Ractive.getHtml(src)
        .then(function(template) {
          template = _applyAbsolutePath(template, src);

          callback(target, template);
        });

      return;
    }

    callback(target, rvPartial.innerHTML);
  }

  function _fetchPartials(element, parent) {
    return new window.Ractive.Promise(function(fulfil) {
      var partials = {},
          rvPartials = element.querySelectorAll('rv-partial'),
          count = rvPartials.length,
          partialName;

      for (partialName in parent.partials) {
        partials[partialName] = parent.partials[partialName];
      }

      if (!count) {
        return fulfil(partials);
      }

      Array.prototype.map.call(rvPartials, function(rvPartial) {
        _requirePartial(rvPartial, function(target, template) {
          if (target) {
            partials[target] = template;
          }

          --count;
          if (count < 1) {
            fulfil(partials);
          }
        });
      });
    });
  }

  function _fetchDataBinding(element, parent) {
    var data = {},
        binds = {},
        attr,
        name;

    for (var i = 0; i < element.attributes.length; i++) {
      attr = element.attributes[i];
      if (attr.name.indexOf('data-bind-') === 0) {
        name = attr.name.substr(10, attr.name.length - 10);
        data[name] = parent.get(attr.value);
        binds[name] = attr.value;
      }
      else if (attr.name.indexOf('data-') === 0) {
        name = attr.name.substr(5, attr.name.length - 5);
        data[name] = attr.value;
      }
    }

    return {
      data: data,
      binds: binds
    };
  }

  function _applyAbsolutePath(template, src) {
    var newSrc = src.split('/'),
        a = document.createElement('a'),
        host = window.location.protocol + '//' + window.location.host;

    return template.replace(/[^-]src="(.*?)"/g, function(match, elementSrc) {
      if (
        elementSrc.indexOf('{') === 0 ||
        elementSrc.indexOf('/') === 0 ||
        elementSrc.indexOf('http://') === 0 ||
        elementSrc.indexOf('https://') === 0
      ) {
        return match;
      }

      newSrc.pop();
      newSrc.push(elementSrc);

      a.href = newSrc.join('/');

      return match.replace(elementSrc, a.href.substr(host.length, a.href.length - host.length));
    });
  }

  function _createObserver(parent, view, bind, keypath) {
    return parent.observe(keypath, function(value) {
      view.set(bind, value);
    });
  }

  function _requireElement(parent, element, callback, forceNoScript, forceNoCSS) {
    forceNoScript = forceNoScript || false;

    var src = element.getAttribute('src'),
        name = element.getAttribute('name'),
        noScript = forceNoScript || element.getAttribute('no-script') == 'true',
        noCSS = forceNoCSS || element.getAttribute('no-css') == 'true';

    if (!src) {
      throw new Error('nv-require needs to have a "src" attrbute');
    }

    if (!name) {
      throw new Error('nv-require needs to have a "name" attrbute');
    }

    element.setAttribute('loaded', 'true');

    if (!window.Ractive.templates[name]) {

      if (!noScript) {
        return window.Ractive.require(src + '.js').then(function() {
          _requireElement(parent, element, callback, true, noCSS);
        });
      }

      if (!noCSS) {
        return window.Ractive.require(src + '.css').then(function() {
          _requireElement(parent, element, callback, noScript, true);
        });
      }

      window.Ractive.getHtml(src + '.html')
        .then(function(template) {
          template = _applyAbsolutePath(template, src);

          window.Ractive.templates[name] = template;

          _requireElement(parent, element, callback);
        });

      return;
    }

    var template = window.Ractive.templates[name],
        databinding = _fetchDataBinding(element, parent);

    _fetchPartials(element, parent)
      .then(function(partials) {

        element.innerHTML = '';

        window.Ractive.fireController(name, function Component(config) {
          config = config || {};

          config.el = config.el || element;
          config.template = config.template || template;
          if (config.partials) {
            var partial;

            for (partial in config.partials) {
              partials[partial] = config.partials[partial];
            }
          }

          config.partials = partials;
          config.parentRequire = parent;

          var ractive = new window.Ractive(config),
              observers = [],
              bind;

          for (bind in databinding.binds) {
            observers.push(_createObserver(parent, ractive, bind, databinding.binds[bind]));
          }

          ractive.on('teardown', function() {
            ractive.parentRequire = null;

            var i;

            for (i = 0; i < observers.length; i++) {
              observers[i].cancel();
            }

            observers = null;

            if (parent && parent.childrenRequire) {
              for (i = 0; i < parent.childrenRequire.length; i++) {
                if (parent.childrenRequire[i] === ractive) {
                  parent.childrenRequire.splice(i, 1);
                  break;
                }
              }
            }

            if (ractive.childrenRequire) {
              for (i = ractive.childrenRequire.length - 1; i >= 0; i--) {
                ractive.childrenRequire[i].teardown();
              }
            }
          });

          ractive.childrenRequire = [];
          parent.childrenRequire.push(ractive);

          return ractive;
        }, databinding.data, element, {
          template: template,
          partials: partials
        });

        if (callback) {
          callback();
        }
      });
  }

  function _inScope(element, parent) {
    if (element.parentNode != parent) {
      if (element.parentNode.tagName.toLowerCase() == 'rv-require') {
        return false;
      }

      return _inScope(element.parentNode, parent);
    }

    return true;
  }

  function _inject(name, file, elementConstructor, callback) {
    name = name.split('/').pop();

    if (window.Ractive.injects.indexOf(name) > -1) {
      return callback();
    }

    var element = elementConstructor();
    element.onload = callback;

    window.Ractive.injects.push(name);
    document.getElementsByTagName('head')[0].appendChild(element);
  }

  function _injectJS(name, file, callback) {
    _inject(name, file, function() {
      var script = document.createElement('script');
      script.type = 'text/javascript';
      script.src = file;

      return script;
    }, callback);
  }

  function _injectCSS(name, file, callback) {
    _inject(name, file, function() {
      var link = document.createElement('link');
      link.rel = 'stylesheet';
      link.href = file;

      return link;
    }, callback);
  }

  window.Ractive.templates = window.Ractive.templates || {};
  window.Ractive.injects = window.Ractive.injects || [];

  window.Ractive.require = function(name, file) {
    var extension = null;

    if (!name) {
      throw new Error('You have to specify a file/name');
    }

    file = file || name;

    extension = (file.split('.').pop() || '').toLowerCase();
    if (extension == 'js') {
      return new window.Ractive.Promise(function(fulfil) {
        _injectJS(name, file, fulfil);
      });
    }
    else if (extension == 'css') {
      return new window.Ractive.Promise(function(fulfil) {
        _injectCSS(name, file, fulfil);
      });
    }
  };

  window.Ractive.prototype.require = function(name) {
    name = name || null;

    var _this = this;

    this.childrenRequire = this.childrenRequire || [];

    return new window.Ractive.Promise(function(fulfil) {

      var elements = name ?
            _this.findAll('rv-require[src][ondemand="' + name + '"]:not([loaded="true"])') :
            _this.findAll('rv-require[src]:not([loaded="true"]):not([ondemand])'),
          count = elements.length;

      if (count < 1) {
        return fulfil();
      }

      elements.forEach(function(element) {

        if (_inScope(element, _this.el)) {
          _requireElement(_this, element, function() {
            --count;
            if (count < 1) {
              fulfil();
            }
          });
        }
      });

    });
  };

})();
<commit_msg>fix(require): decrease count when not in scope<commit_after>(function() {
  'use strict';

  function _requirePartial(rvPartial, callback) {
    var src = rvPartial.getAttribute('src') || false,
        target = rvPartial.getAttribute('target');

    if (!target) {
      return callback(false);
    }

    if (src) {
      window.Ractive.getHtml(src)
        .then(function(template) {
          template = _applyAbsolutePath(template, src);

          callback(target, template);
        });

      return;
    }

    callback(target, rvPartial.innerHTML);
  }

  function _fetchPartials(element, parent) {
    return new window.Ractive.Promise(function(fulfil) {
      var partials = {},
          rvPartials = element.querySelectorAll('rv-partial'),
          count = rvPartials.length,
          partialName;

      for (partialName in parent.partials) {
        partials[partialName] = parent.partials[partialName];
      }

      if (!count) {
        return fulfil(partials);
      }

      Array.prototype.map.call(rvPartials, function(rvPartial) {
        _requirePartial(rvPartial, function(target, template) {
          if (target) {
            partials[target] = template;
          }

          --count;
          if (count < 1) {
            fulfil(partials);
          }
        });
      });
    });
  }

  function _fetchDataBinding(element, parent) {
    var data = {},
        binds = {},
        attr,
        name;

    for (var i = 0; i < element.attributes.length; i++) {
      attr = element.attributes[i];
      if (attr.name.indexOf('data-bind-') === 0) {
        name = attr.name.substr(10, attr.name.length - 10);
        data[name] = parent.get(attr.value);
        binds[name] = attr.value;
      }
      else if (attr.name.indexOf('data-') === 0) {
        name = attr.name.substr(5, attr.name.length - 5);
        data[name] = attr.value;
      }
    }

    return {
      data: data,
      binds: binds
    };
  }

  function _applyAbsolutePath(template, src) {
    var newSrc = src.split('/'),
        a = document.createElement('a'),
        host = window.location.protocol + '//' + window.location.host;

    return template.replace(/[^-]src="(.*?)"/g, function(match, elementSrc) {
      if (
        elementSrc.indexOf('{') === 0 ||
        elementSrc.indexOf('/') === 0 ||
        elementSrc.indexOf('http://') === 0 ||
        elementSrc.indexOf('https://') === 0
      ) {
        return match;
      }

      newSrc.pop();
      newSrc.push(elementSrc);

      a.href = newSrc.join('/');

      return match.replace(elementSrc, a.href.substr(host.length, a.href.length - host.length));
    });
  }

  function _createObserver(parent, view, bind, keypath) {
    return parent.observe(keypath, function(value) {
      view.set(bind, value);
    });
  }

  function _requireElement(parent, element, callback, forceNoScript, forceNoCSS) {
    forceNoScript = forceNoScript || false;

    var src = element.getAttribute('src'),
        name = element.getAttribute('name'),
        noScript = forceNoScript || element.getAttribute('no-script') == 'true',
        noCSS = forceNoCSS || element.getAttribute('no-css') == 'true';

    if (!src) {
      throw new Error('nv-require needs to have a "src" attrbute');
    }

    if (!name) {
      throw new Error('nv-require needs to have a "name" attrbute');
    }

    element.setAttribute('loaded', 'true');

    if (!window.Ractive.templates[name]) {

      if (!noScript) {
        return window.Ractive.require(src + '.js').then(function() {
          _requireElement(parent, element, callback, true, noCSS);
        });
      }

      if (!noCSS) {
        return window.Ractive.require(src + '.css').then(function() {
          _requireElement(parent, element, callback, noScript, true);
        });
      }

      window.Ractive.getHtml(src + '.html')
        .then(function(template) {
          template = _applyAbsolutePath(template, src);

          window.Ractive.templates[name] = template;

          _requireElement(parent, element, callback);
        });

      return;
    }

    var template = window.Ractive.templates[name],
        databinding = _fetchDataBinding(element, parent);

    _fetchPartials(element, parent)
      .then(function(partials) {

        element.innerHTML = '';

        window.Ractive.fireController(name, function Component(config) {
          config = config || {};

          config.el = config.el || element;
          config.template = config.template || template;
          if (config.partials) {
            var partial;

            for (partial in config.partials) {
              partials[partial] = config.partials[partial];
            }
          }

          config.partials = partials;
          config.parentRequire = parent;

          var ractive = new window.Ractive(config),
              observers = [],
              bind;

          for (bind in databinding.binds) {
            observers.push(_createObserver(parent, ractive, bind, databinding.binds[bind]));
          }

          ractive.on('teardown', function() {
            ractive.parentRequire = null;

            var i;

            for (i = 0; i < observers.length; i++) {
              observers[i].cancel();
            }

            observers = null;

            if (parent && parent.childrenRequire) {
              for (i = 0; i < parent.childrenRequire.length; i++) {
                if (parent.childrenRequire[i] === ractive) {
                  parent.childrenRequire.splice(i, 1);
                  break;
                }
              }
            }

            if (ractive.childrenRequire) {
              for (i = ractive.childrenRequire.length - 1; i >= 0; i--) {
                ractive.childrenRequire[i].teardown();
              }
            }
          });

          ractive.childrenRequire = [];
          parent.childrenRequire.push(ractive);

          return ractive;
        }, databinding.data, element, {
          template: template,
          partials: partials
        });

        if (callback) {
          callback();
        }
      });
  }

  function _inScope(element, parent) {
    if (element.parentNode != parent) {
      if (element.parentNode.tagName.toLowerCase() == 'rv-require') {
        return false;
      }

      return _inScope(element.parentNode, parent);
    }

    return true;
  }

  function _inject(name, file, elementConstructor, callback) {
    name = name.split('/').pop();

    if (window.Ractive.injects.indexOf(name) > -1) {
      return callback();
    }

    var element = elementConstructor();
    element.onload = callback;

    window.Ractive.injects.push(name);
    document.getElementsByTagName('head')[0].appendChild(element);
  }

  function _injectJS(name, file, callback) {
    _inject(name, file, function() {
      var script = document.createElement('script');
      script.type = 'text/javascript';
      script.src = file;

      return script;
    }, callback);
  }

  function _injectCSS(name, file, callback) {
    _inject(name, file, function() {
      var link = document.createElement('link');
      link.rel = 'stylesheet';
      link.href = file;

      return link;
    }, callback);
  }

  window.Ractive.templates = window.Ractive.templates || {};
  window.Ractive.injects = window.Ractive.injects || [];

  window.Ractive.require = function(name, file) {
    var extension = null;

    if (!name) {
      throw new Error('You have to specify a file/name');
    }

    file = file || name;

    extension = (file.split('.').pop() || '').toLowerCase();
    if (extension == 'js') {
      return new window.Ractive.Promise(function(fulfil) {
        _injectJS(name, file, fulfil);
      });
    }
    else if (extension == 'css') {
      return new window.Ractive.Promise(function(fulfil) {
        _injectCSS(name, file, fulfil);
      });
    }
  };

  window.Ractive.prototype.require = function(name) {
    name = name || null;

    var _this = this;

    this.childrenRequire = this.childrenRequire || [];

    return new window.Ractive.Promise(function(fulfil) {

      var elements = name ?
            _this.findAll('rv-require[src][ondemand="' + name + '"]:not([loaded="true"])') :
            _this.findAll('rv-require[src]:not([loaded="true"]):not([ondemand])'),
          count = elements.length;

      if (count < 1) {
        return fulfil();
      }

      elements.forEach(function(element) {

        if (_inScope(element, _this.el)) {
          _requireElement(_this, element, function() {
            --count;
            if (count < 1) {
              fulfil();
            }
          });
        }
        else {
          --count;
          if (count < 1) {
            fulfil();
          }
        }
      });

    });
  };

})();
<|endoftext|>


<commit_before>'use strict';

var extend = require('extend'),
    fs = require('fs-extra'),
    cmd = require('child_process'),
    glob = require('glob'),

    WORK_PATH = 'work';

function _cleanWorkspace() {
  if(fs.existsSync(WORK_PATH)) {
    console.log('remove path ' + WORK_PATH);
    fs.removeSync(WORK_PATH);
  }
}

function _error(error, callback) {
  console.error('\n\nERROR!\n');
  console.error(error);
  console.error('\n\n');

  _cleanWorkspace();

  if(callback) {
    callback(false);
  }

  return false;
}

function _success(callback) {
  _cleanWorkspace();

  console.log('\n\nALL IS DONE!\n\n');

  if(callback) {
    callback(true);
  }

  return true;
}

module.exports = function websiteRelease(config, callback) {
  config = extend(true, {
    USER_AGENT: '',
    USER_AGENT_EMAIL: '',
    SECRET: '',
    MEMORYOVERFLOW_REPO: '',
    MEMORYOVERFLOW_PATH: '',
    WEBSITE_REPO: '',
    WEBSITE_PATH: '',
    THEMACHINE_PATH: '',
    COMMIT_LABEL: '',
    commitID: '',
    commitUrl: ''
  }, config);

  _cleanWorkspace();

  config.MEMORYOVERFLOW_PATH = WORK_PATH + '/' + config.MEMORYOVERFLOW_PATH;
  config.WEBSITE_PATH = WORK_PATH + '/' + config.WEBSITE_PATH;
  config.THEMACHINE_PATH = config.MEMORYOVERFLOW_PATH + '/' + config.THEMACHINE_PATH;

  console.log('\ngit clone --depth 1 ' + config.MEMORYOVERFLOW_REPO + ' ' + config.MEMORYOVERFLOW_PATH + '...');

  cmd.exec('git clone --depth 1 ' + config.MEMORYOVERFLOW_REPO + ' ' + config.MEMORYOVERFLOW_PATH, function(error) {
    if(error) {
      return _error(error, callback);
    }

    console.log('\ninstall The Machine...');

    cmd.exec('npm install', {
      cwd: config.THEMACHINE_PATH
    }, function(error) {
      if(error) {
        return _error(error, callback);
      }

      console.log('\nexecute The Machine...');

      cmd.exec('npm run-script generate', {
        cwd: config.THEMACHINE_PATH
      }, function(error) {
        if(error) {
          return _error(error, callback);
        }

        console.log('\ngit clone ' + config.WEBSITE_REPO + ' ' + config.WEBSITE_PATH + '...');

        cmd.exec('git clone ' + config.WEBSITE_REPO + ' ' + config.WEBSITE_PATH, function(error) {
          if(error) {
            return _error(error, callback);
          }

          console.log('\ncopy ' + config.MEMORYOVERFLOW_PATH + '/website' + ' ' + config.WEBSITE_PATH + '...');

          fs.copySync(config.WEBSITE_PATH + '/README.md', config.WEBSITE_PATH + '/README.tmp');

          fs.copySync(config.MEMORYOVERFLOW_PATH + '/website', config.WEBSITE_PATH);

          fs.move(config.WEBSITE_PATH + '/README.tmp', config.WEBSITE_PATH + '/README.md', {
            clobber: true
          }, function(error) {
            if(error) {
              return _error(error, callback);
            }

            console.log('\nremove EJS files...');
            var ejsFiles = glob.sync(config.WEBSITE_PATH + '/**/*.ejs' , {});
            console.log(ejsFiles);

            for(var i = 0, len = ejsFiles.length; i < len; i++) {
              fs.removeSync(ejsFiles[i]);
            }

            console.log('\ngit config user.name "' + config.USER_AGENT + '"');

            cmd.exec('git config user.name "' + config.USER_AGENT + '"', {
              cwd: config.WEBSITE_PATH
            }, function(error) {
              if(error) {
                return _error(error, callback);
              }

              console.log('\ngit config user.email "' + config.USER_AGENT_EMAIL + '"');

              cmd.exec('git config user.email "' + config.USER_AGENT_EMAIL + '"', {
                cwd: config.WEBSITE_PATH
              }, function(error) {
                if(error) {
                  return _error(error, callback);
                }

                console.log('\ngit add -A');

                cmd.exec('git add -A', {
                  cwd: config.WEBSITE_PATH
                }, function(error, stdout) {
                  if(error) {
                    return _error(error, callback);
                  }

                  cmd.exec('git status', {
                    cwd: config.WEBSITE_PATH
                  }, function(error, stdout) {
                    if(error) {
                      return _error(error, callback);
                    }

                    var status = stdout.split('\n');
                    if(status.length && status[1].trim() == 'nothing to commit, working directory clean') {
                      console.log('NOTHING TO COMMIT');
                      return _success(callback);
                    }

                    var commitAuthor = ' --author="' + config.USER_AGENT + ' <' + config.USER_AGENT_EMAIL + '>"',
                        commitLabel = config.COMMIT_LABEL
                          .replace('{commitID}', config.commitID)
                          .replace('{commitUrl}', config.commitUrl)
                          .split('\n')
                          .map(function(line) {
                            return ' -m "' + line + '"';
                          })
                          .join('');

                    console.log('\ngit commit' + commitAuthor + commitLabel);

                    cmd.exec('git commit' + commitAuthor + commitLabel, {
                      cwd: config.WEBSITE_PATH
                    }, function(error) {
                      if(error) {
                        return _error(error, callback);
                      }

                      var pushRepo = config.WEBSITE_REPO.replace('https://', 'https://' + config.USER_AGENT + ':' + config.SECRET + '@');

                      console.log('\ngit push ' + pushRepo.replace(config.SECRET, 'SECRET') + ' gh-pages');

                      cmd.exec('git push ' + pushRepo + ' gh-pages', {
                        cwd: config.WEBSITE_PATH
                      }, function(error) {
                        if(error) {
                          return _error(error, callback);
                        }

                        return _success(callback);

                      });
                    });
                  });
                });
              });
            });
          });
        });
      });
    });
  });
};
<commit_msg>fix(website release): use the new website generator<commit_after>'use strict';

var extend = require('extend'),
    path = require('path'),
    fs = require('fs-extra'),
    cmd = require('child_process'),
    glob = require('glob'),

    WORK_PATH = 'work';

function _cleanWorkspace() {
  if(fs.existsSync(WORK_PATH)) {
    console.log('remove path ' + WORK_PATH);
    fs.removeSync(WORK_PATH);
  }
}

function _error(error, callback) {
  console.error('\n\nERROR!\n');
  console.error(error);
  console.error('\n\n');

  _cleanWorkspace();

  if(callback) {
    callback(false);
  }

  return false;
}

function _success(callback) {
  _cleanWorkspace();

  console.log('\n\nALL IS DONE!\n\n');

  if(callback) {
    callback(true);
  }

  return true;
}

module.exports = function websiteRelease(config, callback) {
  config = extend(true, {
    USER_AGENT: '',
    USER_AGENT_EMAIL: '',
    SECRET: '',
    MEMORYOVERFLOW_REPO: '',
    MEMORYOVERFLOW_PATH: '',
    WEBSITE_REPO: '',
    WEBSITE_PATH: '',
    THEMACHINE_PATH: '',
    COMMIT_LABEL: '',
    commitID: '',
    commitUrl: ''
  }, config);

  _cleanWorkspace();

  config.MEMORYOVERFLOW_PATH = WORK_PATH + '/' + config.MEMORYOVERFLOW_PATH;
  config.WEBSITE_PATH = WORK_PATH + '/' + config.WEBSITE_PATH;
  config.THEMACHINE_PATH = config.MEMORYOVERFLOW_PATH + '/' + config.THEMACHINE_PATH;
  config.WEBSITE_ABSOLUTE_PATH = path.join(__dirname, '..', config.WEBSITE_PATH);

  console.log('\ngit clone --depth 1 ' + config.MEMORYOVERFLOW_REPO + ' ' + config.MEMORYOVERFLOW_PATH + '...');

  cmd.exec('git clone --depth 1 ' + config.MEMORYOVERFLOW_REPO + ' ' + config.MEMORYOVERFLOW_PATH, function(error) {
    if(error) {
      return _error(error, callback);
    }

    console.log('\ninstall The Machine...');

    cmd.exec('npm install', {
      cwd: config.THEMACHINE_PATH
    }, function(error) {
      if(error) {
        return _error(error, callback);
      }

      console.log('\nCreate the .env...');

      fs.writeFileSync(config.THEMACHINE_PATH + '/.env', 'WEBSITE_TARGET=' + config.WEBSITE_ABSOLUTE_PATH);

      console.log('\ngit clone ' + config.WEBSITE_REPO + ' ' + config.WEBSITE_PATH + '...');

      cmd.exec('git clone ' + config.WEBSITE_REPO + ' ' + config.WEBSITE_PATH, function(error) {
        if(error) {
          return _error(error, callback);
        }

        console.log('\nexecute The Machine...');

        cmd.exec('npm run generate', {
          cwd: config.THEMACHINE_PATH
        }, function(error) {
          if(error) {
            return _error(error, callback);
          }

          console.log('\ngit config user.name "' + config.USER_AGENT + '"');

          cmd.exec('git config user.name "' + config.USER_AGENT + '"', {
            cwd: config.WEBSITE_PATH
          }, function(error) {
            if(error) {
              return _error(error, callback);
            }

            console.log('\ngit config user.email "' + config.USER_AGENT_EMAIL + '"');

            cmd.exec('git config user.email "' + config.USER_AGENT_EMAIL + '"', {
              cwd: config.WEBSITE_PATH
            }, function(error) {
              if(error) {
                return _error(error, callback);
              }

              console.log('\ngit add -A');

              cmd.exec('git add -A', {
                cwd: config.WEBSITE_PATH
              }, function(error, stdout) {
                if(error) {
                  return _error(error, callback);
                }

                cmd.exec('git status', {
                  cwd: config.WEBSITE_PATH
                }, function(error, stdout) {
                  if(error) {
                    return _error(error, callback);
                  }

                  var status = stdout.split('\n');
                  if(status.length && status[1].trim() == 'nothing to commit, working directory clean') {
                    console.log('NOTHING TO COMMIT');
                    return _success(callback);
                  }

                  var commitAuthor = ' --author="' + config.USER_AGENT + ' <' + config.USER_AGENT_EMAIL + '>"',
                      commitLabel = config.COMMIT_LABEL
                        .replace('{commitID}', config.commitID)
                        .replace('{commitUrl}', config.commitUrl)
                        .split('\n')
                        .map(function(line) {
                          return ' -m "' + line + '"';
                        })
                        .join('');

                  console.log('\ngit commit' + commitAuthor + commitLabel);

                  cmd.exec('git commit' + commitAuthor + commitLabel, {
                    cwd: config.WEBSITE_PATH
                  }, function(error) {
                    if(error) {
                      return _error(error, callback);
                    }

                    var pushRepo = config.WEBSITE_REPO.replace('https://', 'https://' + config.USER_AGENT + ':' + config.SECRET + '@');

                    console.log('\ngit push ' + pushRepo.replace(config.SECRET, 'SECRET') + ' gh-pages');

                    cmd.exec('git push ' + pushRepo + ' gh-pages', {
                      cwd: config.WEBSITE_PATH
                    }, function(error) {
                      if(error) {
                        return _error(error, callback);
                      }

                      return _success(callback);
                    });
                  });
                });
              });
            });
          });
        });
      });
    });
  });
};
<|endoftext|>


<commit_before>var extend = require('extend'),
    fs = require('fs-extra'),
    cmd = require('child_process'),
    glob = require('glob'),

    WORK_PATH = 'work';

function _cleanWorkspace() {
  if(fs.existsSync(WORK_PATH)) {
    console.log('remove path ' + WORK_PATH);
    fs.removeSync(WORK_PATH);
  }
}

function _error(error, callback) {
  console.error('\n\nERROR!\n');
  console.error(error);
  console.error('\n\n');

  _cleanWorkspace();

  if(callback) {
    callback(false);
  }

  return false;
}

function _success(callback) {
  _cleanWorkspace();

  console.log('\n\nALL IS DONE!\n\n');

  if(callback) {
    callback(true);
  }

  return true;
}

module.exports = function websiteRelease(config, callback) {
  config = extend(true, {
    USER_AGENT: '',
    SECRET: '',
    MEMORYOVERFLOW_REPO: '',
    MEMORYOVERFLOW_PATH: '',
    WEBSITE_REPO: '',
    WEBSITE_PATH: '',
    THEMACHINE_PATH: '',
    COMMIT_LABEL: '',
    commitID: '',
    commitUrl: ''
  }, config);

  _cleanWorkspace();

  config.MEMORYOVERFLOW_PATH = WORK_PATH + '/' + config.MEMORYOVERFLOW_PATH;
  config.WEBSITE_PATH = WORK_PATH + '/' + config.WEBSITE_PATH;
  config.THEMACHINE_PATH = config.MEMORYOVERFLOW_PATH + '/' + config.THEMACHINE_PATH;

  console.log('\ngit clone --depth 1 ' + config.MEMORYOVERFLOW_REPO + ' ' + config.MEMORYOVERFLOW_PATH + '...');

  cmd.exec('git clone --depth 1 ' + config.MEMORYOVERFLOW_REPO + ' ' + config.MEMORYOVERFLOW_PATH, function(error) {
    if(error) {
      return _error(error, callback);
    }

    console.log('\ninstall The Machine...');

    cmd.exec('npm install', {
      cwd: config.THEMACHINE_PATH
    }, function(error) {
      if(error) {
        return _error(error, callback);
      }

      console.log('\nexecute The Machine...');

      cmd.exec('npm run-script generate', {
        cwd: config.THEMACHINE_PATH
      }, function(error) {
        if(error) {
          return _error(error, callback);
        }

        console.log('\ngit clone ' + config.WEBSITE_REPO + ' ' + config.WEBSITE_PATH + '...');

        cmd.exec('git clone ' + config.WEBSITE_REPO + ' ' + config.WEBSITE_PATH, function(error) {
          if(error) {
            return _error(error, callback);
          }

          console.log('\ncopy ' + config.MEMORYOVERFLOW_PATH + '/website' + ' ' + config.WEBSITE_PATH + '...');

          fs.copySync(config.WEBSITE_PATH + '/README.md', config.WEBSITE_PATH + '/README.tmp');

          fs.copySync(config.MEMORYOVERFLOW_PATH + '/website', config.WEBSITE_PATH);

          fs.move(config.WEBSITE_PATH + '/README.tmp', config.WEBSITE_PATH + '/README.md', {
            clobber: true
          }, function(error) {
            if(error) {
              return _error(error, callback);
            }

            console.log('\nremove EJS files...');
            var ejsFiles = glob.sync(config.WEBSITE_PATH + '/**/*.ejs' , {});
            console.log(ejsFiles);

            for(var i = 0, len = ejsFiles.length; i < len; i++) {
              fs.removeSync(ejsFiles[i]);
            }

            console.log('\ngit config user.name "' + config.USER_AGENT + '"');

            cmd.exec('git config user.name "' + config.USER_AGENT + '"', {
              cwd: config.WEBSITE_PATH
            }, function(error) {
              if(error) {
                return _error(error, callback);
              }

              console.log('\ngit config user.email "' + config.USER_AGENT_EMAIL + '"');

              cmd.exec('git config user.email "' + config.USER_AGENT_EMAIL + '"', {
                cwd: config.WEBSITE_PATH
              }, function(error) {
                if(error) {
                  return _error(error, callback);
                }

                console.log('\ngit add -A');

                cmd.exec('git add -A', {
                  cwd: config.WEBSITE_PATH
                }, function(error, stdout) {
                  if(error) {
                    return _error(error, callback);
                  }

                  cmd.exec('git status', {
                    cwd: config.WEBSITE_PATH
                  }, function(error, stdout) {
                    if(error) {
                      return _error(error, callback);
                    }

                    var status = stdout.split('\n');
                    if(status.length && status[1].trim() == 'nothing to commit, working directory clean') {
                      console.log('NOTHING TO COMMIT');
                      return _success(callback);
                    }

                    var commitAuthor = ' --author="' + config.USER_AGENT + ' <' + config.USER_AGENT_EMAIL + '>"',
                        commitLabel = config.COMMIT_LABEL
                          .replace('{commitID}', config.commitID)
                          .replace('{commitUrl}', config.commitUrl)
                          .split('\n')
                          .map(function(line) {
                            return ' -m "' + line + '"';
                          })
                          .join('');

                    console.log('\ngit commit' + commitAuthor + commitLabel);

                    cmd.exec('git commit' + commitAuthor + commitLabel, {
                      cwd: config.WEBSITE_PATH
                    }, function(error) {
                      if(error) {
                        return _error(error, callback);
                      }

                      var pushRepo = config.WEBSITE_REPO.replace('https://', 'https://' + config.USER_AGENT + ':' + config.SECRET + '@');

                      console.log('\ngit push ' + pushRepo + ' gh-pages');

                      cmd.exec('git push ' + pushRepo + ' gh-pages', {
                        cwd: config.WEBSITE_PATH
                      }, function(error) {
                        if(error) {
                          return _error(error, callback);
                        }

                        return _success(callback);

                      });
                    });
                  });
                });
              });
            });
          });
        });
      });
    });
  });
};
<commit_msg>fix(website release): remove SECRET from logs<commit_after>'use strict';

var extend = require('extend'),
    fs = require('fs-extra'),
    cmd = require('child_process'),
    glob = require('glob'),

    WORK_PATH = 'work';

function _cleanWorkspace() {
  if(fs.existsSync(WORK_PATH)) {
    console.log('remove path ' + WORK_PATH);
    fs.removeSync(WORK_PATH);
  }
}

function _error(error, callback) {
  console.error('\n\nERROR!\n');
  console.error(error);
  console.error('\n\n');

  _cleanWorkspace();

  if(callback) {
    callback(false);
  }

  return false;
}

function _success(callback) {
  _cleanWorkspace();

  console.log('\n\nALL IS DONE!\n\n');

  if(callback) {
    callback(true);
  }

  return true;
}

module.exports = function websiteRelease(config, callback) {
  config = extend(true, {
    USER_AGENT: '',
    USER_AGENT_EMAIL: '',
    SECRET: '',
    MEMORYOVERFLOW_REPO: '',
    MEMORYOVERFLOW_PATH: '',
    WEBSITE_REPO: '',
    WEBSITE_PATH: '',
    THEMACHINE_PATH: '',
    COMMIT_LABEL: '',
    commitID: '',
    commitUrl: ''
  }, config);

  _cleanWorkspace();

  config.MEMORYOVERFLOW_PATH = WORK_PATH + '/' + config.MEMORYOVERFLOW_PATH;
  config.WEBSITE_PATH = WORK_PATH + '/' + config.WEBSITE_PATH;
  config.THEMACHINE_PATH = config.MEMORYOVERFLOW_PATH + '/' + config.THEMACHINE_PATH;

  console.log('\ngit clone --depth 1 ' + config.MEMORYOVERFLOW_REPO + ' ' + config.MEMORYOVERFLOW_PATH + '...');

  cmd.exec('git clone --depth 1 ' + config.MEMORYOVERFLOW_REPO + ' ' + config.MEMORYOVERFLOW_PATH, function(error) {
    if(error) {
      return _error(error, callback);
    }

    console.log('\ninstall The Machine...');

    cmd.exec('npm install', {
      cwd: config.THEMACHINE_PATH
    }, function(error) {
      if(error) {
        return _error(error, callback);
      }

      console.log('\nexecute The Machine...');

      cmd.exec('npm run-script generate', {
        cwd: config.THEMACHINE_PATH
      }, function(error) {
        if(error) {
          return _error(error, callback);
        }

        console.log('\ngit clone ' + config.WEBSITE_REPO + ' ' + config.WEBSITE_PATH + '...');

        cmd.exec('git clone ' + config.WEBSITE_REPO + ' ' + config.WEBSITE_PATH, function(error) {
          if(error) {
            return _error(error, callback);
          }

          console.log('\ncopy ' + config.MEMORYOVERFLOW_PATH + '/website' + ' ' + config.WEBSITE_PATH + '...');

          fs.copySync(config.WEBSITE_PATH + '/README.md', config.WEBSITE_PATH + '/README.tmp');

          fs.copySync(config.MEMORYOVERFLOW_PATH + '/website', config.WEBSITE_PATH);

          fs.move(config.WEBSITE_PATH + '/README.tmp', config.WEBSITE_PATH + '/README.md', {
            clobber: true
          }, function(error) {
            if(error) {
              return _error(error, callback);
            }

            console.log('\nremove EJS files...');
            var ejsFiles = glob.sync(config.WEBSITE_PATH + '/**/*.ejs' , {});
            console.log(ejsFiles);

            for(var i = 0, len = ejsFiles.length; i < len; i++) {
              fs.removeSync(ejsFiles[i]);
            }

            console.log('\ngit config user.name "' + config.USER_AGENT + '"');

            cmd.exec('git config user.name "' + config.USER_AGENT + '"', {
              cwd: config.WEBSITE_PATH
            }, function(error) {
              if(error) {
                return _error(error, callback);
              }

              console.log('\ngit config user.email "' + config.USER_AGENT_EMAIL + '"');

              cmd.exec('git config user.email "' + config.USER_AGENT_EMAIL + '"', {
                cwd: config.WEBSITE_PATH
              }, function(error) {
                if(error) {
                  return _error(error, callback);
                }

                console.log('\ngit add -A');

                cmd.exec('git add -A', {
                  cwd: config.WEBSITE_PATH
                }, function(error, stdout) {
                  if(error) {
                    return _error(error, callback);
                  }

                  cmd.exec('git status', {
                    cwd: config.WEBSITE_PATH
                  }, function(error, stdout) {
                    if(error) {
                      return _error(error, callback);
                    }

                    var status = stdout.split('\n');
                    if(status.length && status[1].trim() == 'nothing to commit, working directory clean') {
                      console.log('NOTHING TO COMMIT');
                      return _success(callback);
                    }

                    var commitAuthor = ' --author="' + config.USER_AGENT + ' <' + config.USER_AGENT_EMAIL + '>"',
                        commitLabel = config.COMMIT_LABEL
                          .replace('{commitID}', config.commitID)
                          .replace('{commitUrl}', config.commitUrl)
                          .split('\n')
                          .map(function(line) {
                            return ' -m "' + line + '"';
                          })
                          .join('');

                    console.log('\ngit commit' + commitAuthor + commitLabel);

                    cmd.exec('git commit' + commitAuthor + commitLabel, {
                      cwd: config.WEBSITE_PATH
                    }, function(error) {
                      if(error) {
                        return _error(error, callback);
                      }

                      var pushRepo = config.WEBSITE_REPO.replace('https://', 'https://' + config.USER_AGENT + ':' + config.SECRET + '@');

                      console.log('\ngit push ' + pushRepo.replace(SECRET, 'SECRET') + ' gh-pages');

                      cmd.exec('git push ' + pushRepo + ' gh-pages', {
                        cwd: config.WEBSITE_PATH
                      }, function(error) {
                        if(error) {
                          return _error(error, callback);
                        }

                        return _success(callback);

                      });
                    });
                  });
                });
              });
            });
          });
        });
      });
    });
  });
};
<|endoftext|>


<commit_before>import {
  get,
  computed
} from '@ember/object';
import Controller, {
  inject as controller
} from '@ember/controller';
import searchMatch from 'ember-inspector/utils/search-match';
import {
  notEmpty,
} from '@ember/object/computed';
import {
  isEmpty
} from '@ember/utils';

import {
  schedule
} from '@ember/runloop';

import ComponentViewItem from 'ember-inspector/models/component-view-item';

/**
 * Takes the `viewTree` from `view-debug`'s `sendTree()` method, and recursively
 * flattens it into an array of `ComponentViewItem` objects
 * @param {string} searchValue The value of the search box
 * @param {*} treeNode The node in the viewTree
 * @param {ComponentViewItem} parent The parent `ComponentViewItem`
 * @param {number} parentCount The component hierarchy depth
 * @param {boolean} parentMatched Whether the parent node is initially set to display
 * @param {Array<ComponentViewItem>} list The accumulator, gets mutated in each call
 */
const flattenSearchTree = (
  searchValue,
  treeNode,
  parent,
  parentCount,
  parentMatched,
  list
) => {
  let activeSearch = !isEmpty(searchValue);
  let searchMatched = activeSearch ?
    searchMatch(get(treeNode, 'value.name'), searchValue) :
    true;

  let viewItem = ComponentViewItem.create({
    view: treeNode.value,
    parent,
    parentCount,
    searchMatched,
    activeSearch,
    expanded: !activeSearch,
    hasChildren: treeNode.children.length > 0,
  });

  // remember if there is no active search, searchMatched will be true
  let shouldAddItem = searchMatched || parentMatched;
  if (shouldAddItem) {
    list.push(viewItem);
  }

  let newParentCount = shouldAddItem ? parentCount + 1 : 0;

  treeNode.children.forEach(child => {
    flattenSearchTree(
      searchValue,
      child,
      viewItem,
      newParentCount,
      shouldAddItem,
      list
    );
  });
  return list;
};

export default Controller.extend({
  application: controller(),
  queryParams: ['pinnedObjectId'],

  /**
   * The entry in the component tree corresponding to the pinnedObjectId
   * will be selected
   */
  pinnedObjectId: null,
  inspectingViews: false,
  components: true,
  options: {
    components: true,
  },
  viewTreeLoaded: false,

  /**
   * Bound to the search field to filter the component list.
   *
   * @property searchValue
   * @type {String}
   * @default ''
   */
  searchValue: '',

  activeSearch: notEmpty('searchValue'),

  /**
   * The final list that the `{{vertical-collection}}` renders
   * is created in three stages:
   * 1. The `viewArray` is recalculated When the controller's `viewTree` is set by the route, or when
   *    a user updates the search box.
   * 2. The `filteredArray` CP takes the `viewArray` and caches the expanded state for each item.
   *    This keeps the tree from suddenly re-expanding if the `viewTree` updates after users have
   *    collapsed some nodes.
   * 3. Once the list is rendered, when users expand/collapse a node that action directly
   *    toggles the `expanded` property on each item, which makes `visible` recompute.
   *
   * This could probably happen in one big function, but for the time being its split in the
   * interest of clarity rather than performance (even if the extra CPs might avoid doing some extra
   * work when users expand/contract tree nodes)
   */
  displayedList: computed('filteredArray.@each.visible', function () {
    return this.get('filteredArray').filterBy('visible');
  }),

  filteredArray: computed('viewArray.[]', function () {
    let viewArray = this.get('viewArray');
    let expandedStateCache = this.get('expandedStateCache');
    viewArray.forEach(viewItem => {
      let cachedExpansion = expandedStateCache[viewItem.view.objectId];
      if (cachedExpansion !== undefined) {
        viewItem.set('expanded', cachedExpansion);
      } else {
        expandedStateCache[viewItem.view.objectId] = viewItem.expanded;
      }
    });

    return viewArray;
  }),

  viewArray: computed('viewTree', 'searchValue', function () {
    let tree = this.get('viewTree');
    if (!tree) {
      return [];
    }
    return flattenSearchTree(this.get('searchValue'), tree, null, 0, false, []);
  }),

  expandedStateCache: null, //set on init

  init() {
    this._super(...arguments);
    this.set('expandedStateCache', {});
  },

  /**
   * Expands the component tree so that entry for the given view will
   * be shown.  Recursively expands the entry's parents up to the root.
   * @param {*} objectId The id of the ember view to show
   */
  expandToNode(objectId) {
    let node = this.get('filteredArray').find(item => item.get('id') === objectId);
    if (node) {
      node.expandParents();
    }
  },

  /**
   * This method is basically a trick to get the `{{vertical-collection}}` in the vicinity
   * of the item that's been selected.  We can't directly scroll to the element but we
   * can guess at how far down the list the item is. Then we can manually set the scrollTop
   * of the virtual scroll.
   */
  scrollTreeToItem(objectId) {
    let selectedItemIndex = this.get('displayedList').findIndex(item => item.view.objectId === objectId);

    if (selectedItemIndex) {
      const averageItemHeight = 22;
      schedule('afterRender', () => {
        document.querySelector('.js-component-tree').scrollTop = averageItemHeight * selectedItemIndex;
      });
    }
  },

  actions: {
    previewLayer({
      view: {
        objectId,
        elementId,
        renderNodeId
      }
    }) {
      // We are passing all of objectId, elementId, and renderNodeId to support post-glimmer 1, post-glimmer 2, and root for
      // post-glimmer 2
      this.get('port').send('view:previewLayer', {
        objectId,
        renderNodeId,
        elementId,
      });
    },

    hidePreview() {
      this.get('port').send('view:hidePreview');
    },

    toggleViewInspection() {
      this.get('port').send('view:inspectViews', {
        inspect: !this.get('inspectingViews'),
      });
    },

    sendObjectToConsole(objectId) {
      this.get('port').send('objectInspector:sendToConsole', {
        objectId,
      });
    },

    /**
     * Expand or collapse all component nodes
     * @param {Boolean} expanded If true, expanded, if false, collapsed
     */
    expandOrCollapseAll(expanded) {
      this.expandedStateCache = {};
      this.get('filteredArray').forEach((item) => {
        item.set('expanded', expanded);
        this.expandedStateCache[item.view.objectId] = expanded;
      });
    },

    toggleExpanded(item) {
      item.toggleProperty('expanded');
      this.expandedStateCache[item.view.objectId] = item.get('expanded');
    },

    inspect(objectId) {
      if (objectId) {
        this.set('pinnedObjectId', objectId);
        this.expandToNode(objectId);
        this.scrollTreeToItem(objectId);
        this.get('port').send('objectInspector:inspectById', {
          objectId,
        });
      }
    },

    /**
     * Scrolls the main page to put the selected element into view
     */
    scrollToElement(elementId) {
      this.get('port').send('view:scrollToElement', {
        elementId
      });
    },

    inspectElement({
      objectId,
      elementId
    }) {
      this.get('port').send('view:inspectElement', {
        objectId,
        elementId,
      });
    },
  },
});
<commit_msg>Only scroll to an element when it's out of view (#867)<commit_after>import {
  get,
  computed
} from '@ember/object';
import Controller, {
  inject as controller
} from '@ember/controller';
import searchMatch from 'ember-inspector/utils/search-match';
import {
  notEmpty,
} from '@ember/object/computed';
import {
  isEmpty
} from '@ember/utils';

import {
  schedule
} from '@ember/runloop';

import ComponentViewItem from 'ember-inspector/models/component-view-item';

/**
 * Takes the `viewTree` from `view-debug`'s `sendTree()` method, and recursively
 * flattens it into an array of `ComponentViewItem` objects
 * @param {string} searchValue The value of the search box
 * @param {*} treeNode The node in the viewTree
 * @param {ComponentViewItem} parent The parent `ComponentViewItem`
 * @param {number} parentCount The component hierarchy depth
 * @param {boolean} parentMatched Whether the parent node is initially set to display
 * @param {Array<ComponentViewItem>} list The accumulator, gets mutated in each call
 */
const flattenSearchTree = (
  searchValue,
  treeNode,
  parent,
  parentCount,
  parentMatched,
  list
) => {
  let activeSearch = !isEmpty(searchValue);
  let searchMatched = activeSearch ?
    searchMatch(get(treeNode, 'value.name'), searchValue) :
    true;

  let viewItem = ComponentViewItem.create({
    view: treeNode.value,
    parent,
    parentCount,
    searchMatched,
    activeSearch,
    expanded: !activeSearch,
    hasChildren: treeNode.children.length > 0,
  });

  // remember if there is no active search, searchMatched will be true
  let shouldAddItem = searchMatched || parentMatched;
  if (shouldAddItem) {
    list.push(viewItem);
  }

  let newParentCount = shouldAddItem ? parentCount + 1 : 0;

  treeNode.children.forEach(child => {
    flattenSearchTree(
      searchValue,
      child,
      viewItem,
      newParentCount,
      shouldAddItem,
      list
    );
  });
  return list;
};

export default Controller.extend({
  application: controller(),
  queryParams: ['pinnedObjectId'],

  /**
   * The entry in the component tree corresponding to the pinnedObjectId
   * will be selected
   */
  pinnedObjectId: null,
  inspectingViews: false,
  components: true,
  options: {
    components: true,
  },
  viewTreeLoaded: false,

  /**
   * Bound to the search field to filter the component list.
   *
   * @property searchValue
   * @type {String}
   * @default ''
   */
  searchValue: '',

  activeSearch: notEmpty('searchValue'),

  /**
   * The final list that the `{{vertical-collection}}` renders
   * is created in three stages:
   * 1. The `viewArray` is recalculated When the controller's `viewTree` is set by the route, or when
   *    a user updates the search box.
   * 2. The `filteredArray` CP takes the `viewArray` and caches the expanded state for each item.
   *    This keeps the tree from suddenly re-expanding if the `viewTree` updates after users have
   *    collapsed some nodes.
   * 3. Once the list is rendered, when users expand/collapse a node that action directly
   *    toggles the `expanded` property on each item, which makes `visible` recompute.
   *
   * This could probably happen in one big function, but for the time being its split in the
   * interest of clarity rather than performance (even if the extra CPs might avoid doing some extra
   * work when users expand/contract tree nodes)
   */
  displayedList: computed('filteredArray.@each.visible', function () {
    return this.get('filteredArray').filterBy('visible');
  }),

  filteredArray: computed('viewArray.[]', function () {
    let viewArray = this.get('viewArray');
    let expandedStateCache = this.get('expandedStateCache');
    viewArray.forEach(viewItem => {
      let cachedExpansion = expandedStateCache[viewItem.view.objectId];
      if (cachedExpansion !== undefined) {
        viewItem.set('expanded', cachedExpansion);
      } else {
        expandedStateCache[viewItem.view.objectId] = viewItem.expanded;
      }
    });

    return viewArray;
  }),

  viewArray: computed('viewTree', 'searchValue', function () {
    let tree = this.get('viewTree');
    if (!tree) {
      return [];
    }
    return flattenSearchTree(this.get('searchValue'), tree, null, 0, false, []);
  }),

  expandedStateCache: null, //set on init

  init() {
    this._super(...arguments);
    this.set('expandedStateCache', {});
  },

  /**
   * Expands the component tree so that entry for the given view will
   * be shown.  Recursively expands the entry's parents up to the root.
   * @param {*} objectId The id of the ember view to show
   */
  expandToNode(objectId) {
    let node = this.get('filteredArray').find(item => item.get('id') === objectId);
    if (node) {
      node.expandParents();
    }
  },

  /**
   * This method is basically a trick to get the `{{vertical-collection}}` in the vicinity
   * of the item that's been selected.  We can't directly scroll to the element but we
   * can guess at how far down the list the item is. Then we can manually set the scrollTop
   * of the virtual scroll.
   */
  scrollTreeToItem(objectId) {
    let selectedItemIndex = this.get('displayedList').findIndex(item => item.view.objectId === objectId);

    if (!selectedItemIndex) {
      return;
    }

    const averageItemHeight = 25;
    const targetScrollTop = averageItemHeight * selectedItemIndex;
    const componentTreeEl = document.querySelector('.js-component-tree');
    const height = componentTreeEl.offsetHeight;

    // Only scroll to item if not already in view
    if (targetScrollTop < componentTreeEl.scrollTop || targetScrollTop > componentTreeEl.scrollTop + height) {
      schedule('afterRender', () => {
        componentTreeEl.scrollTop = targetScrollTop - (height / 2);
      });
    }
  },

  actions: {
    previewLayer({
      view: {
        objectId,
        elementId,
        renderNodeId
      }
    }) {
      // We are passing all of objectId, elementId, and renderNodeId to support post-glimmer 1, post-glimmer 2, and root for
      // post-glimmer 2
      this.get('port').send('view:previewLayer', {
        objectId,
        renderNodeId,
        elementId,
      });
    },

    hidePreview() {
      this.get('port').send('view:hidePreview');
    },

    toggleViewInspection() {
      this.get('port').send('view:inspectViews', {
        inspect: !this.get('inspectingViews'),
      });
    },

    sendObjectToConsole(objectId) {
      this.get('port').send('objectInspector:sendToConsole', {
        objectId,
      });
    },

    /**
     * Expand or collapse all component nodes
     * @param {Boolean} expanded If true, expanded, if false, collapsed
     */
    expandOrCollapseAll(expanded) {
      this.expandedStateCache = {};
      this.get('filteredArray').forEach((item) => {
        item.set('expanded', expanded);
        this.expandedStateCache[item.view.objectId] = expanded;
      });
    },

    toggleExpanded(item) {
      item.toggleProperty('expanded');
      this.expandedStateCache[item.view.objectId] = item.get('expanded');
    },

    inspect(objectId) {
      if (objectId) {
        this.set('pinnedObjectId', objectId);
        this.expandToNode(objectId);
        this.scrollTreeToItem(objectId);
        this.get('port').send('objectInspector:inspectById', {
          objectId,
        });
      }
    },

    /**
     * Scrolls the main page to put the selected element into view
     */
    scrollToElement(elementId) {
      this.get('port').send('view:scrollToElement', {
        elementId
      });
    },

    inspectElement({
      objectId,
      elementId
    }) {
      this.get('port').send('view:inspectElement', {
        objectId,
        elementId,
      });
    },
  },
});
<|endoftext|>


<commit_before>import Ember from 'ember';
import editManager from './edit_manager';
import modal from './modal';
import capabilities from './capabilities';
import app_state from './app_state';
import i18n from './i18n';
import speecher from './speecher';

var scanner = Ember.Object.extend({
  setup: function(controller) {
    this.controller = controller;
  },
  start: function(options) {
    if(Ember.$("header #speak").length === 0) {
      console.debug("scanning currently only works in speak mode...");
      scanner.stop();
      return;
    }
    var rows = [];
    options = options || this.last_options || {};
    this.last_options = options;
    options.scan_mode = options.scan_mode || "row";
    options.interval = options.interval || 1000;

    if(modal.is_open() && !modal.is_open('highlight')) {
      return;
    } else {
      var row = {
        children: [],
        dom: Ember.$("header"),
        label: i18n.t('header', "Header")
      };
      Ember.$("header #speak").find("button:visible,#button_list,a.btn").each(function() {
        var id_labels = {
          'home_button': i18n.t('home', "Home"),
          'back_button': i18n.t('back', "Back"),
          'button_list': i18n.t('speak', "Speak"),
          'speak_options': i18n.t('options', "Speak Options"),
          'backspace_button': i18n.t('backspace', "Backspace"),
          'clear_button': i18n.t('clear', "Clear")
        };
        var $elem = Ember.$(this);
        if($elem.attr('id') != 'speak_options') {
          var label = id_labels[$elem.attr('id')] || "";
          row.children.push({
            dom: $elem,
            label: label
          });
        }
      });

      var menu = {
        dom: Ember.$("#identity a.btn"),
        label: i18n.t('menu', "Menu"),
        children: []
      };
      Ember.$("#identity .dropdown-menu a").each(function() {
        var $option = Ember.$(this);
        menu.children.push({
          dom: $option,
          label: $option.text()
        });
      });
      console.log(menu);
      row.children.push(menu);

      // TODO: figure out sidebar, when teaser is visible and also when the
      // whole sidebar is visible, including toggling between the two
  //     if(Ember.$("#sidebar_tease:visible").length) {
  //       row.children.push({
  //         dom: Ember.$("#sidebar_tease")
  //       });
  //     }
      rows.push(row);
      if(options.scan_mode == 'row' || options.scan_mode == 'button') {
        var grid = editManager.controller.get('model.grid');
        for(var idx = 0; idx < grid.rows; idx++) {
          row = {
            children: [],
            dom: Ember.$(),
            label: i18n.t('row_n', "Row %{n}", {n: (idx + 1)})
          };
          for(var jdx = 0; jdx < grid.columns; jdx++) {
            var $button = Ember.$(".button[data-id='" + grid.order[idx][jdx] + "']:not(.hidden_button)");
            if($button.length) {
              var button = editManager.find_button(grid.order[idx][jdx]);
              var label = (button && (button.get('vocalization') || button.get('label'))) || "";
              row.dom = row.dom.add($button);
              row.children.push({
                dom: $button,
                label: label,
                sound: button && button.get('sound')
              });
            }
          }
          if(row.children.length > 0) {
            rows.push(row);
          }
        }
      } else if(options.scan_mode == 'column') {
        var grid = editManager.controller.get('model.grid');
        for(var idx = 0; idx < grid.columns; idx++) {
          var column = {
            children: [],
            dom: Ember.$(),
            label: i18n.t('column_n', "Column %{n}", {n: (idx + 1)})
          };
          for(var jdx = 0; jdx < grid.rows; jdx++) {
            var $button = Ember.$(".button[data-id='" + grid.order[jdx][idx] + "']:not(.hidden_button)");
            if($button.length) {
              var button = editManager.find_button(grid.order[idx][jdx]);
              var label = (button && (button.get('vocalization') || button.get('label'))) || "";
              column.dom = column.dom.add($button);
              column.children.push({
                dom: $button,
                label: label,
                sound: button && button.get('sound')
              });
            }
          }
          if(column.children.length > 0) {
            rows.push(column);
          }
        }
      } else if(options.scan_mode == 'region') {
        var rows_per_chunk = options.rows_per_chunk || 3;
        var columns_per_chunk = options.columns_per_chunk || 3;
        var sub_scan = options.sub_scan_mode || 'horizontal';
        var grid = editManager.controller.get('model.grid');
        var vertical_chunks = options.vertical_chunks || Math.ceil(grid.rows / rows_per_chunk);
        var horizontal_chunks = options.horizontal_chunks || Math.ceil(grid.columns / columns_per_chunk);
        if(rows_per_chunk < grid.rows / vertical_chunks) {
          rows_per_chunk = Math.ceil(grid.rows / vertical_chunks);
        }
        if(columns_per_chunk < grid.columns / horizontal_chunks) {
          columns_per_chunk = Math.ceil(grid.columns / horizontal_chunks);
        }
        if(sub_scan == 'vertical' || true) {
          for(var idx = 0; idx < horizontal_chunks; idx++) {
            for(var jdx = 0; jdx < vertical_chunks; jdx++) {
              var chunk = {
                children: [],
                dom: Ember.$(),
                label: i18n.t('region_n', "Region %{n}", {n: ((idx * vertical_chunks) + jdx + 1)})
              };
              for(var kdx = 0; kdx < columns_per_chunk; kdx++) {
                for(var ldx = 0; ldx < rows_per_chunk; ldx++) {
                  var r = grid.order[(jdx * rows_per_chunk) + ldx];
                  if(r) {
                    var id = r[(idx * columns_per_chunk) + kdx];
                    if(id) {
                      var $button = Ember.$(".button[data-id='" + id + "']:not(.hidden_button)");
                      if($button.length) {
                        var button = editManager.find_button(id);
                        var label = (button && (button.get('vocalization') || button.get('label'))) || "";
                        chunk.dom = chunk.dom.add($button);
                        chunk.children.push({
                          dom: $button,
                          label: label,
                          sound: button && button.get('sound')
                        });
                      }
                    }
                  }
                }
              }
              if(chunk.children.length > 0) {
                rows.push(chunk);
              }
            }
          }
        } else {
          for(var idx = 0; idx < vertical_chunks; idx++) {
            for(var jdx = 0; jdx < horizontal_chunks; jdx++) {
              var chunk = {
                children: [],
                dom: Ember.$(),
                label: i18n.t('region_n', "Region %{n}", {n: ((idx * horizontal_chunks) + jdx + 1)})
              };
              for(var kdx = 0; kdx < rows_per_chunk; kdx++) {
                for(var ldx = 0; ldx < columns_per_chunk; ldx++) {
                  var r = grid.order[(idx * rows_per_chunk) + kdx];
                  if(r) {
                    var id = r[(jdx * columns_per_chunk) + ldx];
                    if(id) {
                      var $button = Ember.$(".button[data-id='" + id + "']:not(.hidden_button)");
                      if($button.length) {
                        var button = editManager.find_button(id);
                        var label = (button && (button.get('vocalization') || button.get('label'))) || "";
                        chunk.dom = chunk.dom.add($button);
                        chunk.children.push({
                          dom: $button,
                          label: label,
                          sound: button && button.get('sound')
                        });
                      }
                    }
                  }
                }
              }
              if(chunk.children.length > 0) {
                rows.push(chunk);
              }
            }
          }
        }
      }
      if(options.scan_mode == 'button') {
        var new_rows = [];
        rows.forEach(function(row) {
          if(row.children) {
            row.children.forEach(function(elem) {
              new_rows.push(elem);
            });
          } else {
            new_rows.push(row);
          }
        });
        rows = new_rows;
      }
    }
    this.scanning = true;
    this.scan_elements(rows, options);
  },
  reset: function() {
    Ember.run.cancel(scanner.interval);
    this.start();
  },
  stop: function()  {
    Ember.run.cancel(scanner.interval);
    this.scanning = false;
    modal.close_highlight();
  },
  scan_elements: function(elements, options) {
    this.elements = elements;
    this.options = options;
    this.element_index = 0;
    this.next_element();
  },
  pick: function() {
    var elem = scanner.current_element;
    if(!modal.highlight_controller || !elem) { return; }
    if(!elem.higher_level && elem.children && elem.children.length == 1) {
      elem = elem.children[0];
    }

    if(elem.dom.hasClass('btn') && elem.dom.closest("#identity").length > 0) {
      var e = Ember.$.Event( "click" );
      e.pass_through = true;
      Ember.$(elem.dom).trigger(e);
    }

    if(elem.higher_level) {
      scanner.elements = elem.higher_level;
      scanner.element_index = elem.higher_level_index;
      Ember.run.cancel(scanner.interval);
      scanner.interval = Ember.run.later(function() {
        scanner.next_element();
      });
    } else if(elem.children) {
      var parent = Ember.$.extend({higher_level: scanner.elements, higher_level_index: scanner.element_index}, elem);
      scanner.elements = elem.children.concat([parent]);
      scanner.element_index = 0;
      Ember.run.cancel(scanner.interval);
      scanner.interval = Ember.run.later(function() {
        scanner.next_element();
      });
    } else {
      if(elem.dom.hasClass('button') && elem.dom.attr('data-id')) {
        var id = elem.dom.attr('data-id');
        var button = editManager.find_button(id);
        var app = app_state.controller;
        var board = app.get('board.model');
        app.activateButton(button, {image: button.get('image'), sound: button.get('sound'), board: board});
      } else if(elem.dom.hasClass('button_list')) {
        elem.dom.select();
      } else {
        var e = Ember.$.Event( "click" );
        e.pass_through = true;
        Ember.$(elem.dom).trigger(e);
      }
      Ember.run.later(function() {
        scanner.reset();
      });
    }
  },
  next: function() {
    Ember.run.cancel(scanner.interval);
    scanner.element_index = scanner.element_index + 1;
    if(scanner.element_index >= scanner.elements.length) {
      scanner.element_index = 0;
    }
    scanner.next_element();
  },
  next_element: function() {
    var elem = this.elements[this.element_index];
    scanner.current_element = elem;
    var options = scanner.options;
    options.prevent_close = true;
    options.overlay = false;
    options.select_anywhere = true;
    if(scanner.options && scanner.options.focus_overlay) {
      options.overlay = true;
      options.clear_overlay = false;
    }

    if(this.options && this.options.audio) {
      if(elem && elem.sound) {
        speecher.speak_audio(elem.sound, 'text', false, {interrupt: false});
      } else if(elem && elem.label) {
        speecher.speak_text(elem.label, false, {interrupt: false});
      }
    }
    if(capabilities.mobile && app_state.get('speak_mode') && Ember.$("#hidden_input:focus").length === 0) {
      modal['default'].warning(i18n.t('tap_first', "Your switch may not be completely enabled. Tap somewhere on the screen to finish enabling it."), true);
    }
    modal.highlight(elem.dom, options).then(function() {
      scanner.pick();
    }, function() { });
    scanner.interval = Ember.run.later(function() {
      if(scanner.current_element == elem) {
        scanner.next();
      }
    }, this.options.interval);
  }
}).create();

export default scanner;<commit_msg>fix modal call<commit_after>import Ember from 'ember';
import editManager from './edit_manager';
import modal from './modal';
import capabilities from './capabilities';
import app_state from './app_state';
import i18n from './i18n';
import speecher from './speecher';

var scanner = Ember.Object.extend({
  setup: function(controller) {
    this.controller = controller;
  },
  start: function(options) {
    if(Ember.$("header #speak").length === 0) {
      console.debug("scanning currently only works in speak mode...");
      scanner.stop();
      return;
    }
    var rows = [];
    options = options || this.last_options || {};
    this.last_options = options;
    options.scan_mode = options.scan_mode || "row";
    options.interval = options.interval || 1000;

    if(modal.is_open() && !modal.is_open('highlight')) {
      return;
    } else {
      var row = {
        children: [],
        dom: Ember.$("header"),
        label: i18n.t('header', "Header")
      };
      Ember.$("header #speak").find("button:visible,#button_list,a.btn").each(function() {
        var id_labels = {
          'home_button': i18n.t('home', "Home"),
          'back_button': i18n.t('back', "Back"),
          'button_list': i18n.t('speak', "Speak"),
          'speak_options': i18n.t('options', "Speak Options"),
          'backspace_button': i18n.t('backspace', "Backspace"),
          'clear_button': i18n.t('clear', "Clear")
        };
        var $elem = Ember.$(this);
        if($elem.attr('id') != 'speak_options') {
          var label = id_labels[$elem.attr('id')] || "";
          row.children.push({
            dom: $elem,
            label: label
          });
        }
      });

      var menu = {
        dom: Ember.$("#identity a.btn"),
        label: i18n.t('menu', "Menu"),
        children: []
      };
      Ember.$("#identity .dropdown-menu a").each(function() {
        var $option = Ember.$(this);
        menu.children.push({
          dom: $option,
          label: $option.text()
        });
      });
      console.log(menu);
      row.children.push(menu);

      // TODO: figure out sidebar, when teaser is visible and also when the
      // whole sidebar is visible, including toggling between the two
  //     if(Ember.$("#sidebar_tease:visible").length) {
  //       row.children.push({
  //         dom: Ember.$("#sidebar_tease")
  //       });
  //     }
      rows.push(row);
      if(options.scan_mode == 'row' || options.scan_mode == 'button') {
        var grid = editManager.controller.get('model.grid');
        for(var idx = 0; idx < grid.rows; idx++) {
          row = {
            children: [],
            dom: Ember.$(),
            label: i18n.t('row_n', "Row %{n}", {n: (idx + 1)})
          };
          for(var jdx = 0; jdx < grid.columns; jdx++) {
            var $button = Ember.$(".button[data-id='" + grid.order[idx][jdx] + "']:not(.hidden_button)");
            if($button.length) {
              var button = editManager.find_button(grid.order[idx][jdx]);
              var label = (button && (button.get('vocalization') || button.get('label'))) || "";
              row.dom = row.dom.add($button);
              row.children.push({
                dom: $button,
                label: label,
                sound: button && button.get('sound')
              });
            }
          }
          if(row.children.length > 0) {
            rows.push(row);
          }
        }
      } else if(options.scan_mode == 'column') {
        var grid = editManager.controller.get('model.grid');
        for(var idx = 0; idx < grid.columns; idx++) {
          var column = {
            children: [],
            dom: Ember.$(),
            label: i18n.t('column_n', "Column %{n}", {n: (idx + 1)})
          };
          for(var jdx = 0; jdx < grid.rows; jdx++) {
            var $button = Ember.$(".button[data-id='" + grid.order[jdx][idx] + "']:not(.hidden_button)");
            if($button.length) {
              var button = editManager.find_button(grid.order[idx][jdx]);
              var label = (button && (button.get('vocalization') || button.get('label'))) || "";
              column.dom = column.dom.add($button);
              column.children.push({
                dom: $button,
                label: label,
                sound: button && button.get('sound')
              });
            }
          }
          if(column.children.length > 0) {
            rows.push(column);
          }
        }
      } else if(options.scan_mode == 'region') {
        var rows_per_chunk = options.rows_per_chunk || 3;
        var columns_per_chunk = options.columns_per_chunk || 3;
        var sub_scan = options.sub_scan_mode || 'horizontal';
        var grid = editManager.controller.get('model.grid');
        var vertical_chunks = options.vertical_chunks || Math.ceil(grid.rows / rows_per_chunk);
        var horizontal_chunks = options.horizontal_chunks || Math.ceil(grid.columns / columns_per_chunk);
        if(rows_per_chunk < grid.rows / vertical_chunks) {
          rows_per_chunk = Math.ceil(grid.rows / vertical_chunks);
        }
        if(columns_per_chunk < grid.columns / horizontal_chunks) {
          columns_per_chunk = Math.ceil(grid.columns / horizontal_chunks);
        }
        if(sub_scan == 'vertical' || true) {
          for(var idx = 0; idx < horizontal_chunks; idx++) {
            for(var jdx = 0; jdx < vertical_chunks; jdx++) {
              var chunk = {
                children: [],
                dom: Ember.$(),
                label: i18n.t('region_n', "Region %{n}", {n: ((idx * vertical_chunks) + jdx + 1)})
              };
              for(var kdx = 0; kdx < columns_per_chunk; kdx++) {
                for(var ldx = 0; ldx < rows_per_chunk; ldx++) {
                  var r = grid.order[(jdx * rows_per_chunk) + ldx];
                  if(r) {
                    var id = r[(idx * columns_per_chunk) + kdx];
                    if(id) {
                      var $button = Ember.$(".button[data-id='" + id + "']:not(.hidden_button)");
                      if($button.length) {
                        var button = editManager.find_button(id);
                        var label = (button && (button.get('vocalization') || button.get('label'))) || "";
                        chunk.dom = chunk.dom.add($button);
                        chunk.children.push({
                          dom: $button,
                          label: label,
                          sound: button && button.get('sound')
                        });
                      }
                    }
                  }
                }
              }
              if(chunk.children.length > 0) {
                rows.push(chunk);
              }
            }
          }
        } else {
          for(var idx = 0; idx < vertical_chunks; idx++) {
            for(var jdx = 0; jdx < horizontal_chunks; jdx++) {
              var chunk = {
                children: [],
                dom: Ember.$(),
                label: i18n.t('region_n', "Region %{n}", {n: ((idx * horizontal_chunks) + jdx + 1)})
              };
              for(var kdx = 0; kdx < rows_per_chunk; kdx++) {
                for(var ldx = 0; ldx < columns_per_chunk; ldx++) {
                  var r = grid.order[(idx * rows_per_chunk) + kdx];
                  if(r) {
                    var id = r[(jdx * columns_per_chunk) + ldx];
                    if(id) {
                      var $button = Ember.$(".button[data-id='" + id + "']:not(.hidden_button)");
                      if($button.length) {
                        var button = editManager.find_button(id);
                        var label = (button && (button.get('vocalization') || button.get('label'))) || "";
                        chunk.dom = chunk.dom.add($button);
                        chunk.children.push({
                          dom: $button,
                          label: label,
                          sound: button && button.get('sound')
                        });
                      }
                    }
                  }
                }
              }
              if(chunk.children.length > 0) {
                rows.push(chunk);
              }
            }
          }
        }
      }
      if(options.scan_mode == 'button') {
        var new_rows = [];
        rows.forEach(function(row) {
          if(row.children) {
            row.children.forEach(function(elem) {
              new_rows.push(elem);
            });
          } else {
            new_rows.push(row);
          }
        });
        rows = new_rows;
      }
    }
    this.scanning = true;
    this.scan_elements(rows, options);
  },
  reset: function() {
    Ember.run.cancel(scanner.interval);
    this.start();
  },
  stop: function()  {
    Ember.run.cancel(scanner.interval);
    this.scanning = false;
    modal.close_highlight();
  },
  scan_elements: function(elements, options) {
    this.elements = elements;
    this.options = options;
    this.element_index = 0;
    this.next_element();
  },
  pick: function() {
    var elem = scanner.current_element;
    if(!modal.highlight_controller || !elem) { return; }
    if(!elem.higher_level && elem.children && elem.children.length == 1) {
      elem = elem.children[0];
    }

    if(elem.dom.hasClass('btn') && elem.dom.closest("#identity").length > 0) {
      var e = Ember.$.Event( "click" );
      e.pass_through = true;
      Ember.$(elem.dom).trigger(e);
    }

    if(elem.higher_level) {
      scanner.elements = elem.higher_level;
      scanner.element_index = elem.higher_level_index;
      Ember.run.cancel(scanner.interval);
      scanner.interval = Ember.run.later(function() {
        scanner.next_element();
      });
    } else if(elem.children) {
      var parent = Ember.$.extend({higher_level: scanner.elements, higher_level_index: scanner.element_index}, elem);
      scanner.elements = elem.children.concat([parent]);
      scanner.element_index = 0;
      Ember.run.cancel(scanner.interval);
      scanner.interval = Ember.run.later(function() {
        scanner.next_element();
      });
    } else {
      if(elem.dom.hasClass('button') && elem.dom.attr('data-id')) {
        var id = elem.dom.attr('data-id');
        var button = editManager.find_button(id);
        var app = app_state.controller;
        var board = app.get('board.model');
        app.activateButton(button, {image: button.get('image'), sound: button.get('sound'), board: board});
      } else if(elem.dom.hasClass('button_list')) {
        elem.dom.select();
      } else {
        var e = Ember.$.Event( "click" );
        e.pass_through = true;
        Ember.$(elem.dom).trigger(e);
      }
      Ember.run.later(function() {
        scanner.reset();
      });
    }
  },
  next: function() {
    Ember.run.cancel(scanner.interval);
    scanner.element_index = scanner.element_index + 1;
    if(scanner.element_index >= scanner.elements.length) {
      scanner.element_index = 0;
    }
    scanner.next_element();
  },
  next_element: function() {
    var elem = this.elements[this.element_index];
    scanner.current_element = elem;
    var options = scanner.options;
    options.prevent_close = true;
    options.overlay = false;
    options.select_anywhere = true;
    if(scanner.options && scanner.options.focus_overlay) {
      options.overlay = true;
      options.clear_overlay = false;
    }

    if(this.options && this.options.audio) {
      if(elem && elem.sound) {
        speecher.speak_audio(elem.sound, 'text', false, {interrupt: false});
      } else if(elem && elem.label) {
        speecher.speak_text(elem.label, false, {interrupt: false});
      }
    }
    if(capabilities.mobile && app_state.get('speak_mode') && Ember.$("#hidden_input:focus").length === 0) {
      modal.warning(i18n.t('tap_first', "Your switch may not be completely enabled. Tap somewhere on the screen to finish enabling it."), true);
    }
    modal.highlight(elem.dom, options).then(function() {
      scanner.pick();
    }, function() { });
    scanner.interval = Ember.run.later(function() {
      if(scanner.current_element == elem) {
        scanner.next();
      }
    }, this.options.interval);
  }
}).create();

export default scanner;<|endoftext|>


<commit_before>/* global angular */

(function (angular) {

  'use strict';

  angular.module('myApp.global')

  .factory('breadcrumbService', ['$routeParams', '$timeout', 'apiService', function($routeParams, $timeout, api) {

    var pub = {};

    var breadcrumbs = {
      project: { showing: false, value: {} },
      task: { showing: false, value: {} }
    };

    var removeProject = function () {
      breadcrumbs.project.showing = false;
      breadcrumbs.project.value = {};
    };

    var removeTask = function () {
      breadcrumbs.task.showing = false;
      breadcrumbs.task.value = {};
    };

    var removeAll = function () {
      removeProject();
      removeTask();
    };

    var addProject = function (projectId) {
      var id = projectId || $routeParams.id;
      api.get('projects/', id, function (task) {
        $timeout(function () {
          breadcrumbs.project.showing = true;
          breadcrumbs.project.value = task;
        },0);
      });
    };

    var addTask = function () {
      var id = $routeParams.id;
      api.get('tasks/', id, function (task) {
        if (!breadcrumbs.project.showing) {
          addProject(task.projectId);
        }
        $timeout(function () {
          breadcrumbs.task.showing = true;
          breadcrumbs.task.value = task;
        },0);
      });
    };

    var _add = function (type) {
      if (!type) {
        removeAll();
        return;
      } else if (type === 'projects') {
        removeTask();
        addProject();
      } else {
        addTask();
      }
    };

    pub.add = _add;
    pub.breadcrumbs = breadcrumbs;

    return pub;
  }]);

})(angular);
<commit_msg>getting rid of timeout<commit_after>/* global angular */

(function (angular) {

  'use strict';

  angular.module('myApp.global')

  .factory('breadcrumbService', ['$routeParams', '$timeout', 'apiService', function($routeParams, $timeout, api) {

    var pub = {};

    var breadcrumbs = {
      project: { showing: false, value: {} },
      task: { showing: false, value: {} }
    };

    var removeProject = function () {
      breadcrumbs.project.showing = false;
      breadcrumbs.project.value = {};
    };

    var removeTask = function () {
      breadcrumbs.task.showing = false;
      breadcrumbs.task.value = {};
    };

    var removeAll = function () {
      removeProject();
      removeTask();
    };

    var addProject = function (projectId) {
      var id = projectId || $routeParams.id;
      api.get('projects/', id, function (task) {
        breadcrumbs.project.showing = true;
        breadcrumbs.project.value = task;
      });
    };

    var addTask = function () {
      var id = $routeParams.id;
      api.get('tasks/', id, function (task) {
        if (!breadcrumbs.project.showing) {
          addProject(task.projectId);
        }
        breadcrumbs.task.showing = true;
        breadcrumbs.task.value = task;
      });
    };

    var _add = function (type) {
      if (!type) {
        removeAll();
        return;
      } else if (type === 'projects') {
        removeTask();
        addProject();
      } else {
        addTask();
      }
    };

    pub.add = _add;
    pub.breadcrumbs = breadcrumbs;

    return pub;
  }]);

})(angular);
<|endoftext|>


<commit_before>/* Source file for storing devices saved by user */

var gearBagDB = new MooSQL({
   // DB Name
   dbName: 'GearBag',
   // DB Version
   dbVersion: '1.0',
   // Description
   dbDesc: 'Saved devices in gear bag',
   // Est. size (value from example)
   dbSize: 15
});

/* According to the MooSQL docs, this should run when the database is ready.
 * However, this event would never fire in test. Instead, the callback function
 * used for this event listener is run when the domready event is fired.
 *
 * This happen at the bottom of this file.
 */
gearBagDB.addEvent('databaseReady', function() {
   gearBagDB.exec("SELECT * FROM 'Device'", init_saved_devices);
});

gearBagDB.addEvent('databaseCreated', function() {
   console.log("created db");

   var table_schema = {
      id: "INTEGER PRIMARY KEY AUTOINCREMENT",
      title: "TEXT",
      img_url: "TEXT"
   };

   gearBagDB.create("Device", table_schema);
});

/* Called when new device is dropped into gear bag */
function add_device(device_title, device_image) {
   gearBagDB.insert("Device", {title: device_title, img_url: device_image});
}

/* Gets all devices in database and injects them into the gear bag */
function init_saved_devices(transaction, result) {
   var i = 0;
   var results_to_html = [];
   var stored_device;

   /* Iterates over result set. Each |item| is an object with title and img */
   if (result && result.rows) {
      while (i < result.rows.length) {
         stored_device = result.rows.item(i++);

         results_to_html.push("

" + stored_device.title
          + "

")
      }

      $('drop_device').innerHTML = results_to_html.join("\n");
   }
}

/* Executes on page load */
window.addEvent("domready", function() {
   gearBagDB.exec("SELECT * FROM 'Device'", init_saved_devices);
});
<commit_msg>Fixed bug that doesn't show helper text<commit_after>/* Source file for storing devices saved by user */

var gearBagDB = new MooSQL({
   // DB Name
   dbName: 'GearBag',
   // DB Version
   dbVersion: '1.0',
   // Description
   dbDesc: 'Saved devices in gear bag',
   // Est. size (value from example)
   dbSize: 15
});

/* According to the MooSQL docs, this should run when the database is ready.
 * However, this event would never fire in test. Instead, the callback function
 * used for this event listener is run when the domready event is fired.
 *
 * This happen at the bottom of this file.
 */
gearBagDB.addEvent('databaseReady', function() {
   gearBagDB.exec("SELECT * FROM 'Device'", init_saved_devices);
});

gearBagDB.addEvent('databaseCreated', function() {
   console.log("created db");

   var table_schema = {
      id: "INTEGER PRIMARY KEY AUTOINCREMENT",
      title: "TEXT",
      img_url: "TEXT"
   };

   gearBagDB.create("Device", table_schema);
});

/* Called when new device is dropped into gear bag */
function add_device(device_title, device_image) {
   gearBagDB.insert("Device", {title: device_title, img_url: device_image});
}

/* Gets all devices in database and injects them into the gear bag */
function init_saved_devices(transaction, result) {
   var i = 0;
   var results_to_html = [];
   var stored_device;

   /* Iterates over result set. Each |item| is an object with title and img */
   if (result && result.rows && result.rows.length > 0) {
      while (i < result.rows.length) {
         stored_device = result.rows.item(i++);

         results_to_html.push("

" + stored_device.title
          + "

")
      }

      $('drop_device').innerHTML = results_to_html.join("\n");
   }
}

/* Executes on page load */
window.addEvent("domready", function() {
   gearBagDB.exec("SELECT * FROM 'Device'", init_saved_devices);
});
<|endoftext|>