<commit_before>/**
 * @class CM_Layout_Abstract
 * @extends CM_View_Abstract
 */
var CM_Layout_Abstract = CM_View_Abstract.extend({

  /** @type String */
  _class: 'CM_Layout_Abstract',

  /** @type jQuery|Null */
  _$pagePlaceholder: null,

  /** @type PromiseThrottled|Null */
  _loadPageThrottled: promiseThrottler(function(path) {
    var layout = this;
    layout._chargeSpinnerTimeout();

    return this.ajaxModal('loadPage', {path: path})
      .then(function(response) {
        layout._clearSpinnerTimeout();
        if (response.redirectExternal) {
          cm.router.route(response.redirectExternal);
          return;
        }
        var view = layout._injectView(response);
        var reload = (layout.getClass() != response.layoutClass);
        if (reload) {
          window.location.replace(response.url);
          return;
        }
        layout._removePagePlaceholder(view.$el);
        layout._updateHistory(path, response.url);
        layout._onPageSetup(response.title, response.menuEntryHashList, response.jsTracking);
        view._ready();
        cm.event.trigger('navigate:end', {page: view, path: path});
        return view;
      })
      .catch(function(error) {
        layout._clearSpinnerTimeout();
        if (!(error instanceof Promise.CancellationError)) {
          layout._errorPagePlaceholder(error);
          layout._onPageError();
          throw error;
        }
      });
  }, {cancelLeading: true}),

  /** @type {Number} timeout ID */
  _timeoutLoading: null,

  /**
   * @returns {CM_View_Abstract|null}
   */
  findPage: function() {
    return this.findChild('CM_Page_Abstract');
  },

  /**
   * @returns {CM_View_Abstract}
   */
  getPage: function() {
    var page = this.findPage();
    if (!page) {
      throw new CM_Exception('Layout doesn\'t have a page');
    }
    return page;
  },

  /**
   * @param {String} path
   * @return Promise
   */
  loadPage: function(path) {
    cm.event.trigger('navigate', path); // deprecated
    cm.event.trigger('navigate:start', {path: path});
    return this._loadPageThrottled(path);
  },

  /**
   * @param {jQuery} $el
   */
  scrollTo: function($el) {
    var pageOffsetTop = 0;
    var page = cm.findView('CM_Page_Abstract');
    if (page) {
      pageOffsetTop = page.$el.offset().top;
    }
    $(document).scrollTop($el.offset().top - pageOffsetTop);
  },

  _onPageTeardown: function() {
    $(document).scrollTop(0);
    $('.floatbox-layer').floatIn();
  },

  /**
   * @param {String} title
   * @param {String[]} menuEntryHashList
   * @param {String} [jsTracking]
   */
  _onPageSetup: function(title, menuEntryHashList, jsTracking) {
    cm.window.title.setText(title);
    $('[data-menu-entry-hash]').removeClass('active');
    var menuEntrySelectors = _.map(menuEntryHashList, function(menuEntryHash) {
      return '[data-menu-entry-hash=' + menuEntryHash + ']';
    });
    $(menuEntrySelectors.join(',')).addClass('active');
    if (jsTracking) {
      new Function(jsTracking).call(this);
    }
    if (window.location.hash) {
      var hash = window.location.hash.substring(1);
      var $anchor = $('#' + hash).add('[name=' + hash + ']');
      if ($anchor.length) {
        this.scrollTo($anchor);
      }
    }
  },

  _onPageError: function() {
    $('[data-menu-entry-hash]').removeClass('active');
  },

  _createPagePlaceholder: function() {
    if (!this._$pagePlaceholder) {
      this._$pagePlaceholder = $('
');
      this.getPage().replaceWithHtml(this._$pagePlaceholder);
      this._onPageTeardown();
    } else {
      this._$pagePlaceholder.removeClass('error').html('');
    }
  },

  /**
   * @returns {jQuery}
   */
  _getPagePlaceholder: function() {
    if (!this._$pagePlaceholder) {
      this._createPagePlaceholder();
    }
    return this._$pagePlaceholder;
  },

  /**
   * @param {Element|String|jQuery} el
   */
  _removePagePlaceholder: function(el) {
    this._getPagePlaceholder().replaceWith(el);
    this._$pagePlaceholder = null;
  },

  /**
   * @param {Error} error
   */
  _errorPagePlaceholder: function(error) {
    this._getPagePlaceholder().addClass('error').html('
' + error.message + '
');
  },

  _chargeSpinnerTimeout: function() {
    this._clearSpinnerTimeout();
    this._timeoutLoading = this.setTimeout(function() {
      this._getPagePlaceholder().html('
');
    }, 750);
  },

  _clearSpinnerTimeout: function() {
    clearTimeout(this._timeoutLoading);
  },

  /**
   * @param {String} requestPath
   * @param {String} responseUrl
   */
  _updateHistory: function(requestPath, responseUrl) {
    var responseFragment = responseUrl.substr(cm.getUrl().length);
    if (requestPath === responseFragment + window.location.hash) {
      responseFragment = requestPath;
    }
    window.history.replaceState(null, null, responseFragment);
  }

});
<commit_msg>Fix loadPageThrottled<commit_after>/**
 * @class CM_Layout_Abstract
 * @extends CM_View_Abstract
 */
var CM_Layout_Abstract = CM_View_Abstract.extend({

  /** @type String */
  _class: 'CM_Layout_Abstract',

  /** @type jQuery|Null */
  _$pagePlaceholder: null,

  /** @type PromiseThrottled|Null */
  _loadPageThrottled: promiseThrottler(function(path) {
    var layout = this;
    layout._chargeSpinnerTimeout();

    return this.ajaxModal('loadPage', {path: path})
      .finally(function() {
        layout._clearSpinnerTimeout();
      })
      .then(function(response) {
        if (response.redirectExternal) {
          cm.router.route(response.redirectExternal);
          return;
        }
        var view = layout._injectView(response);
        var reload = (layout.getClass() != response.layoutClass);
        if (reload) {
          window.location.replace(response.url);
          return;
        }
        layout._removePagePlaceholder(view.$el);
        layout._updateHistory(path, response.url);
        layout._onPageSetup(response.title, response.menuEntryHashList, response.jsTracking);
        view._ready();
        cm.event.trigger('navigate:end', {page: view, path: path});
        return view;
      })
      .catch(function(error) {
        if (!(error instanceof Promise.CancellationError)) {
          layout._errorPagePlaceholder(error);
          layout._onPageError();
          throw error;
        }
      });
  }, {cancelLeading: true}),

  /** @type {Number} timeout ID */
  _timeoutLoading: null,

  /**
   * @returns {CM_View_Abstract|null}
   */
  findPage: function() {
    return this.findChild('CM_Page_Abstract');
  },

  /**
   * @returns {CM_View_Abstract}
   */
  getPage: function() {
    var page = this.findPage();
    if (!page) {
      throw new CM_Exception('Layout doesn\'t have a page');
    }
    return page;
  },

  /**
   * @param {String} path
   * @return Promise
   */
  loadPage: function(path) {
    cm.event.trigger('navigate', path); // deprecated
    cm.event.trigger('navigate:start', {path: path});
    return this._loadPageThrottled(path);
  },

  /**
   * @param {jQuery} $el
   */
  scrollTo: function($el) {
    var pageOffsetTop = 0;
    var page = cm.findView('CM_Page_Abstract');
    if (page) {
      pageOffsetTop = page.$el.offset().top;
    }
    $(document).scrollTop($el.offset().top - pageOffsetTop);
  },

  _onPageTeardown: function() {
    $(document).scrollTop(0);
    $('.floatbox-layer').floatIn();
  },

  /**
   * @param {String} title
   * @param {String[]} menuEntryHashList
   * @param {String} [jsTracking]
   */
  _onPageSetup: function(title, menuEntryHashList, jsTracking) {
    cm.window.title.setText(title);
    $('[data-menu-entry-hash]').removeClass('active');
    var menuEntrySelectors = _.map(menuEntryHashList, function(menuEntryHash) {
      return '[data-menu-entry-hash=' + menuEntryHash + ']';
    });
    $(menuEntrySelectors.join(',')).addClass('active');
    if (jsTracking) {
      new Function(jsTracking).call(this);
    }
    if (window.location.hash) {
      var hash = window.location.hash.substring(1);
      var $anchor = $('#' + hash).add('[name=' + hash + ']');
      if ($anchor.length) {
        this.scrollTo($anchor);
      }
    }
  },

  _onPageError: function() {
    $('[data-menu-entry-hash]').removeClass('active');
  },

  _createPagePlaceholder: function() {
    if (!this._$pagePlaceholder) {
      this._$pagePlaceholder = $('
');
      this.getPage().replaceWithHtml(this._$pagePlaceholder);
      this._onPageTeardown();
    } else {
      this._$pagePlaceholder.removeClass('error').html('');
    }
  },

  /**
   * @returns {jQuery}
   */
  _getPagePlaceholder: function() {
    if (!this._$pagePlaceholder) {
      this._createPagePlaceholder();
    }
    return this._$pagePlaceholder;
  },

  /**
   * @param {Element|String|jQuery} el
   */
  _removePagePlaceholder: function(el) {
    this._getPagePlaceholder().replaceWith(el);
    this._$pagePlaceholder = null;
  },

  /**
   * @param {Error} error
   */
  _errorPagePlaceholder: function(error) {
    this._getPagePlaceholder().addClass('error').html('
' + error.message + '
');
  },

  _chargeSpinnerTimeout: function() {
    this._clearSpinnerTimeout();
    this._timeoutLoading = this.setTimeout(function() {
      this._getPagePlaceholder().html('
');
    }, 750);
  },

  _clearSpinnerTimeout: function() {
    clearTimeout(this._timeoutLoading);
  },

  /**
   * @param {String} requestPath
   * @param {String} responseUrl
   */
  _updateHistory: function(requestPath, responseUrl) {
    var responseFragment = responseUrl.substr(cm.getUrl().length);
    if (requestPath === responseFragment + window.location.hash) {
      responseFragment = requestPath;
    }
    window.history.replaceState(null, null, responseFragment);
  }

});
<|endoftext|>


<commit_before>// This file contains needed polyfills mainly for IE11
if (!Math.sign) {
    Math.sign = function(a) {
        return a > 0.0 ? 1.0 : a < 0.0 ? -1.0 : 0.0;
    };
}

if (!Math.log2) {
    Math.log2 = function(x) {
        return Math.log(x) * Math.LOG2E;
    };
}

if (!Math.log10) {
    Math.log10 = function(x) {
        return Math.log(x) * Math.LOG10E;
    };
}

if (!String.prototype.endsWith) {
    String.prototype.endsWith = function(str) {
        return this.slice(-str.length) === str;
    };
}

if (!String.prototype.startsWith) {
    String.prototype.startsWith = function(str) {
        return this.slice(0, str.length) === str;
    };
}

if (!Float32Array.prototype.slice) {
    var _slicePolyfill = function(start, end) {
        return new this.constructor(this.subarray(start, end));
    };

    Int8Array.prototype.slice = _slicePolyfill;
    Uint8Array.prototype.slice = _slicePolyfill;
    Uint8ClampedArray.prototype.slice = _slicePolyfill;
    Int16Array.prototype.slice = _slicePolyfill;
    Uint16Array.prototype.slice = _slicePolyfill;
    Int32Array.prototype.slice = _slicePolyfill;
    Uint32Array.prototype.slice = _slicePolyfill;
    Float32Array.prototype.slice = _slicePolyfill;
    Float64Array.prototype.slice = _slicePolyfill;
}

(function() {
    // prevent crash on old browser
    if (!window.Set) return;
    // IE11 does not support Set with constructing arguments. May 2017.
    var setTest = new window.Set(['test']);
    var hasConstructorParameterSupport = setTest.has('test');
    if (hasConstructorParameterSupport) return;

    var nativeSetConstructor = window.Set;
    window.Set = function(init) {
        var set = new nativeSetConstructor();
        if (init) {
            for (var i = 0; i < init.length; ++i) {
                set.add(init[i]);
            }
        }
        return set;
    };
})();
<commit_msg>Fixes exception on old browsers.<commit_after>(function() {
    // prevent crash on old browser
    if (!window.Set || !window.Map || !window.Uint8ClampedArray) return;

    // This file contains needed polyfills mainly for IE11
    if (!Math.sign) {
        Math.sign = function(a) {
            return a > 0.0 ? 1.0 : a < 0.0 ? -1.0 : 0.0;
        };
    }

    if (!Math.log2) {
        Math.log2 = function(x) {
            return Math.log(x) * Math.LOG2E;
        };
    }

    if (!Math.log10) {
        Math.log10 = function(x) {
            return Math.log(x) * Math.LOG10E;
        };
    }

    if (!String.prototype.endsWith) {
        String.prototype.endsWith = function(str) {
            return this.slice(-str.length) === str;
        };
    }

    if (!String.prototype.startsWith) {
        String.prototype.startsWith = function(str) {
            return this.slice(0, str.length) === str;
        };
    }

    if (!Float32Array.prototype.slice) {
        var _slicePolyfill = function(start, end) {
            return new this.constructor(this.subarray(start, end));
        };

        Int8Array.prototype.slice = _slicePolyfill;
        Uint8Array.prototype.slice = _slicePolyfill;
        Uint8ClampedArray.prototype.slice = _slicePolyfill;
        Int16Array.prototype.slice = _slicePolyfill;
        Uint16Array.prototype.slice = _slicePolyfill;
        Int32Array.prototype.slice = _slicePolyfill;
        Uint32Array.prototype.slice = _slicePolyfill;
        Float32Array.prototype.slice = _slicePolyfill;
        Float64Array.prototype.slice = _slicePolyfill;
    }

    // IE11 does not support Set with constructing arguments. May 2017.
    var setTest = new window.Set(['test']);
    var hasConstructorParameterSupport = setTest.has('test');
    if (hasConstructorParameterSupport) return;
    var nativeSetConstructor = window.Set;
    window.Set = function(init) {
        var set = new nativeSetConstructor();
        if (init) {
            for (var i = 0; i < init.length; ++i) {
                set.add(init[i]);
            }
        }
        return set;
    };
})();
<|endoftext|>


<commit_before>/* global crel */

let MODAL;

function qsa(selector) {
    return Array.from(document.querySelectorAll(selector));
}

function insertBefore(newNode, target) {
    target.parentElement.insertBefore(newNode, target);
}

function buildModal() {
    let closeBtn1, closeBtn2;
    const modalNode = crel(
        'div', {id: 'sparqlit-modal',
                'class': 'modal fade',
                tabindex: -1,
                role: 'dialog',
                'aria-labelledby': 'Sparql-it Modal Dialog'},
        crel('div', {'class': 'modal-dialog', role: 'document'},
             crel('div', {'class': 'modal-content'},
                  crel('div', {'class': 'modal-header'},
                       closeBtn1 = crel('button', {type: 'button',
                                                   'class': 'close',
                                                   'data-dismiss': 'modal',
                                                   'arial-label': 'Close'},
                                        crel('span', {'aria-hidden': true}, 'x')),
                       crel('h4', {'class': 'modal-title',
                                   'id': 'sparql-modal-title'},
                            'SPARQL-it !')),
                  crel('div', {'class': 'modal-body'},
                       crel('pre', {id: 'sparqlit-modal-pre'})),
                  crel('div', {'class': 'modal-footer'},
                       crel('a', {'class': 'btn btn-info',
                                  id: 'sparqlit-modal-exec',
                                  href: '#',
                                  target: '_blank'}, 'Execute'),
                       crel('a', {'class': 'btn btn-info',
                                  id: 'sparqlit-modal-edit',
                                  href: 'http://data.bnf.fr/sparql',
                                  target: '_blank'}, 'Edit'),
                       crel('a', {'class': 'btn btn-info',
                                  id: 'sparqlit-modal-dl',
                                  href: '#',
                                  target: '_blank'}, 'Download (JSON)'),
                       closeBtn2 = crel('button', {type: 'button',
                                                   'class': 'btn btn-default',
                                                   'data-dismiss': 'modal'},
                                        'Close')))));
    closeBtn1.onclick = hideModal;
    closeBtn2.onclick = hideModal;
    return modalNode;
}

function modalBackdrop() {
    return crel('div', {'id': 'sparqlit-modal-backdrop',
                        'class': 'modal-backdrop fade'});
}

function sparqlUrl(query, mimetype) {
    return `http://data.bnf.fr/sparql?query=${encodeURIComponent(query)}&format=${encodeURIComponent(mimetype || 'text/html')}`;
}

function executeQueryLink(query) {
    const a = crel('a', {'class': 'sparql-link',
                         target: '_blank',
                         title: 'Execute Query',
                         href: '#'},
                   crel('i', {'class': 'glyphicon glyphicon-search'}),
                   'SPARQL-it');
    a.onclick = (evt) => {showModal(query); evt.preventDefault();};
    return a;
}

function sparqlLink(query) {
    return crel('span', {'class': 'sparql-it'},
                executeQueryLink(query));
}

function authorityMainInfos(pageUri) {
    return `
PREFIX foaf: <http://xmlns.com/foaf/0.1/>

SELECT ?uri ?predicate ?object WHERE {

  {
    BIND (<${pageUri}> AS ?uri)
    ?uri ?predicate ?object.
  }

  UNION

  {
    <${pageUri}> foaf:focus ?uri.
    ?uri ?predicate ?object.
  }

}`;
}

function authorDocsQuery(role, authorUri) {
    return `
PREFIX bnfroles: <http://data.bnf.fr/vocabulary/roles/>
PREFIX rdarelationships: <http://rdvocab.info/RDARelationshipsWEMI/>
PREFIX dcterms: <http://purl.org/dc/terms/>

SELECT DISTINCT ?manif ?work ?title ?date ?type ?gallica WHERE {

  ?expr bnfroles:${role} <${authorUri}#foaf:Person>.
  ?formerexpr owl:sameAs ?expr.
  ?manif rdarelationships:expressionManifested ?formerexpr ;
         dcterms:title ?title
  OPTIONAL {
    ?manif rdarelationships:workManifested ?work.
  }

  OPTIONAL {
    ?manif rdarelationships:electronicReproduction ?gallica.
  }

  OPTIONAL {
    ?manif dcterms:date ?date.
  }

  OPTIONAL {
    ?formerexpr dcterms:type ?type.
  }

}`;
}

function workDocsQuery(workUri) {
    return `
PREFIX rdarelationships: <http://rdvocab.info/RDARelationshipsWEMI/>
PREFIX dcterms: <http://purl.org/dc/terms/>

SELECT DISTINCT ?manif ?title ?date ?type ?gallica WHERE {

  ?manif rdarelationships:workManifested <${workUri}#frbr:Work> ;
         rdarelationships:expressionManifested ?formerexpr ;
         dcterms:title ?title

  OPTIONAL {
    ?manif rdarelationships:electronicReproduction ?gallica.
  }

  OPTIONAL {
    ?manif dcterms:date ?date.
  }

  OPTIONAL {
    ?formerexpr dcterms:type ?type.
  }
}`;
}

function relatedAuthorsQuery(authorUri) {
    return `
PREFIX dcterms: <http://purl.org/dc/terms/>
PREFIX bnfroles: <http://data.bnf.fr/vocabulary/roles/>
PREFIX foaf: <http://xmlns.com/foaf/0.1/>

SELECT ?role1 ?manif ?title ?role2 ?author2 ?author2name WHERE {
  ?expr dcterms:contributor <${authorUri}#foaf:Person> ;
        ?role1 <${authorUri}#foaf:Person> ;
        ?role2 ?author2.

  ?author2 a foaf:Person;
       foaf:name ?author2name.

  ?formerexpr owl:sameAs ?expr.
  ?manif rdarelationships:expressionManifested ?formerexpr ;
         dcterms:title ?title.

  FILTER(regex(?role1, 'http://data.bnf.fr/vocabulary/roles'))
  FILTER(regex(?role2, 'http://data.bnf.fr/vocabulary/roles'))
  FILTER(?author2 != <${authorUri}#foaf:Person>)
}`;
}

function placesQuery() {
    return `
PREFIX skos: <http://www.w3.org/2004/02/skos/core#>
PREFIX foaf: <http://xmlns.com/foaf/0.1/>
PREFIX geo: <http://www.w3.org/2003/01/geo/wgs84_pos#>

SELECT ?geo ?label ?lat ?lng ?geonames WHERE {

  ?geoconcept skos:closeMatch ?rameau ;
    skos:prefLabel ?label ;
    foaf:focus ?geo.

  OPTIONAL {
    ?geoconcept skos:exactMatch ?geonames.
    FILTER(regex(?geonames, 'http://sws.geonames.org/'))
  }

  ?geo geo:lat ?lat ;
       geo:long ?lng.

  ?rameau a skos:Concept.
}
`;
}

function studyDocsQuery(authorityUri) {
    return `
PREFIX rdarelationships: <http://rdvocab.info/RDARelationshipsWEMI/>
PREFIX dcterms: <http://purl.org/dc/terms/>

SELECT DISTINCT ?manif ?work ?title ?date ?type ?gallica WHERE {

  ?formerexpr owl:sameAs ?expr.

  ?manif dcterms:subject <${authorityUri}> ;
         rdarelationships:expressionManifested ?formerexpr ;
         dcterms:title ?title.

  OPTIONAL {
    ?manif rdarelationships:workManifested ?work.
  }

  OPTIONAL {
    ?manif rdarelationships:electronicReproduction ?gallica.
  }

  OPTIONAL {
    ?manif dcterms:date ?date.
  }

  OPTIONAL {
    ?formerexpr dcterms:type ?type.
  }

}`;
}

function authorWorksQuery(authorUri) {
    return `
PREFIX dcterms: <http://purl.org/dc/terms/>
PREFIX rdarelationships: <http://rdvocab.info/RDARelationshipsWEMI/>

SELECT ?work ?title (COUNT(?manif) as ?count) WHERE {
 ?work dcterms:creator <${authorUri}#foaf:Person>;
    dcterms:title ?title.

  ?manif rdarelationships:workManifested ?work.
}
GROUP BY ?work ?title
ORDER BY DESC(?count)
`;
}

function storeQuery(query) {
    if (window.localStorage !== undefined) {
        localStorage.setItem('queryVal_main', JSON.stringify({val: query}));
    }
}

function hackMainInfos(pageUri) {
    const h1 = document.querySelector('h1');
    insertBefore(sparqlLink(authorityMainInfos(pageUri)), h1.firstChild);
}

function hackAuthorDocumentSections(authorUri) {
    qsa('.dtmanifs > h3 > a:first-child').forEach(link => {
        const role = link.href.split('/').pop();
        const h3 = link.parentElement;
        h3.insertBefore(sparqlLink(authorDocsQuery(role, authorUri)), link);
    });
}

function hackRelatedAuthors(authorUri) {
    qsa(`.bloc-contenu a[href="http://data.bnf.fr/linked-authors/${authorUri.slice(32, 40)}"]`).forEach(a => {
        insertBefore(sparqlLink(relatedAuthorsQuery(authorUri)), a);
    });
}

function hackAuthorTitle(authorUri) {
    const div = document.querySelector('#allmanifs');
    insertBefore(sparqlLink(authorWorksQuery(authorUri)), div.firstChild);
}

function hackAuthorPage(pageUri) {
    hackMainInfos(pageUri);
    hackAuthorDocumentSections(pageUri);
    hackRelatedAuthors(pageUri);
    hackStudiesSection(pageUri);
    hackAuthorTitle(pageUri);
}

function hackWorkDocumentSections(workUri) {
    qsa('.dtmanifs > h3').forEach(h3 => {
        h3.insertBefore(sparqlLink(workDocsQuery(workUri)), h3.firstChild);
    });
}

function hackStudiesSection(pageUri) {
    qsa('.bloc-contenu a[href$=studies').forEach(link => {
        insertBefore(sparqlLink(studyDocsQuery(pageUri)), link);
    });
}

function hackGeoDocumentSections(geoUri) {
    qsa('.dtmanifs > h3').forEach(h3 => {
        h3.insertBefore(sparqlLink(studyDocsQuery(geoUri)), h3.firstChild);
    });
}

function hackWorkPage(pageUri) {
    hackMainInfos(pageUri);
    hackWorkDocumentSections(pageUri);
    hackStudiesSection(pageUri);
}

function hackGeoPage(pageUri) {
    hackMainInfos(pageUri);
    hackGeoDocumentSections(pageUri);
}

function hackHomePage() {
    const mapBlock = document.querySelector('#map-geo');
    insertBefore(sparqlLink(placesQuery()), mapBlock);
}

function guessPageType() {
    const ogtype = document.querySelector('meta[property="og:type"]');
    if (ogtype && ogtype.content) {
        return ogtype.content;
    }
    const path = document.location.pathname;
    if (path === '/') {
        return 'home';
    } else if (path.startsWith('/date')) {
        return 'date';
    } else if (path.startsWith('/3') || path.startsWith('/4')) {
        const divtype = document.querySelector('#fullpage div[itemtype]'),
              itemtype = divtype ? divtype.getAttribute('itemtype') : null;
        if (itemtype === 'http://schema.org/Periodical') {
            return 'periodical';
        } else if (itemtype === 'http://schema.org/TheaterEvent') {
            return 'performance';
        }
    }
    return null;
}

function showModal(query) {
    if (!MODAL) {
        MODAL = buildModal();
        document.body.appendChild(MODAL);
    }
    const backdrop = modalBackdrop();
    document.body.appendChild(backdrop);
    backdrop.classList.add('in');
    MODAL.style.display = 'block';
    MODAL.classList.add('fade', 'in');
    MODAL.querySelector('#sparqlit-modal-pre').textContent = query;
    MODAL.querySelector('#sparqlit-modal-exec').href = sparqlUrl(query);
    MODAL.querySelector('#sparqlit-modal-dl').href = sparqlUrl(query, 'application/sparql-results+json');
    MODAL.querySelector('#sparqlit-modal-edit').onclick = () => storeQuery(query);
}

function hideModal() {
    const backdrop = document.querySelector('#sparqlit-modal-backdrop');
    backdrop.parentNode.removeChild(backdrop);
    MODAL.classList.remove('fade', 'in');
    MODAL.style.display = 'none';
}

let permalink = document.querySelector('link[rel=bookmark]'),
    pageUri = (permalink === null) ? null : permalink.href;

switch(guessPageType()) {
case 'author':
    hackAuthorPage(pageUri);
    break;
case 'book':
    hackWorkPage(pageUri);
    break;
case 'geopoint':
    hackGeoPage(pageUri);
    break;
case 'home':
    if (document.location.pathname === '/') {
        hackHomePage();
    }
// case 'geopoint'
// case 'article'
// case 'date'
// case 'periodical'
// case 'performace'
}
<commit_msg>hack periodical page<commit_after>/* global crel */

let MODAL;

function qsa(selector) {
    return Array.from(document.querySelectorAll(selector));
}

function insertBefore(newNode, target) {
    target.parentElement.insertBefore(newNode, target);
}

function buildModal() {
    let closeBtn1, closeBtn2;
    const modalNode = crel(
        'div', {id: 'sparqlit-modal',
                'class': 'modal fade',
                tabindex: -1,
                role: 'dialog',
                'aria-labelledby': 'Sparql-it Modal Dialog'},
        crel('div', {'class': 'modal-dialog', role: 'document'},
             crel('div', {'class': 'modal-content'},
                  crel('div', {'class': 'modal-header'},
                       closeBtn1 = crel('button', {type: 'button',
                                                   'class': 'close',
                                                   'data-dismiss': 'modal',
                                                   'arial-label': 'Close'},
                                        crel('span', {'aria-hidden': true}, 'x')),
                       crel('h4', {'class': 'modal-title',
                                   'id': 'sparql-modal-title'},
                            'SPARQL-it !')),
                  crel('div', {'class': 'modal-body'},
                       crel('pre', {id: 'sparqlit-modal-pre'})),
                  crel('div', {'class': 'modal-footer'},
                       crel('a', {'class': 'btn btn-info',
                                  id: 'sparqlit-modal-exec',
                                  href: '#',
                                  target: '_blank'}, 'Execute'),
                       crel('a', {'class': 'btn btn-info',
                                  id: 'sparqlit-modal-edit',
                                  href: 'http://data.bnf.fr/sparql',
                                  target: '_blank'}, 'Edit'),
                       crel('a', {'class': 'btn btn-info',
                                  id: 'sparqlit-modal-dl',
                                  href: '#',
                                  target: '_blank'}, 'Download (JSON)'),
                       closeBtn2 = crel('button', {type: 'button',
                                                   'class': 'btn btn-default',
                                                   'data-dismiss': 'modal'},
                                        'Close')))));
    closeBtn1.onclick = hideModal;
    closeBtn2.onclick = hideModal;
    return modalNode;
}

function modalBackdrop() {
    return crel('div', {'id': 'sparqlit-modal-backdrop',
                        'class': 'modal-backdrop fade'});
}

function sparqlUrl(query, mimetype) {
    return `http://data.bnf.fr/sparql?query=${encodeURIComponent(query)}&format=${encodeURIComponent(mimetype || 'text/html')}`;
}

function executeQueryLink(query) {
    const a = crel('a', {'class': 'sparql-link',
                         target: '_blank',
                         title: 'Execute Query',
                         href: '#'},
                   crel('i', {'class': 'glyphicon glyphicon-search'}),
                   'SPARQL-it');
    a.onclick = (evt) => {showModal(query); evt.preventDefault();};
    return a;
}

function sparqlLink(query) {
    return crel('span', {'class': 'sparql-it'},
                executeQueryLink(query));
}

function authorityMainInfos(pageUri) {
    return `
PREFIX foaf: <http://xmlns.com/foaf/0.1/>

SELECT ?uri ?predicate ?object WHERE {

  {
    BIND (<${pageUri}> AS ?uri)
    ?uri ?predicate ?object.
  }

  UNION

  {
    <${pageUri}> foaf:focus ?uri.
    ?uri ?predicate ?object.
  }

}`;
}

function authorDocsQuery(role, authorUri) {
    return `
PREFIX bnfroles: <http://data.bnf.fr/vocabulary/roles/>
PREFIX rdarelationships: <http://rdvocab.info/RDARelationshipsWEMI/>
PREFIX dcterms: <http://purl.org/dc/terms/>

SELECT DISTINCT ?manif ?work ?title ?date ?type ?gallica WHERE {

  ?expr bnfroles:${role} <${authorUri}#foaf:Person>.
  ?formerexpr owl:sameAs ?expr.
  ?manif rdarelationships:expressionManifested ?formerexpr ;
         dcterms:title ?title
  OPTIONAL {
    ?manif rdarelationships:workManifested ?work.
  }

  OPTIONAL {
    ?manif rdarelationships:electronicReproduction ?gallica.
  }

  OPTIONAL {
    ?manif dcterms:date ?date.
  }

  OPTIONAL {
    ?formerexpr dcterms:type ?type.
  }

}`;
}

function workDocsQuery(workUri) {
    return `
PREFIX rdarelationships: <http://rdvocab.info/RDARelationshipsWEMI/>
PREFIX dcterms: <http://purl.org/dc/terms/>

SELECT DISTINCT ?manif ?title ?date ?type ?gallica WHERE {

  ?manif rdarelationships:workManifested <${workUri}#frbr:Work> ;
         rdarelationships:expressionManifested ?formerexpr ;
         dcterms:title ?title

  OPTIONAL {
    ?manif rdarelationships:electronicReproduction ?gallica.
  }

  OPTIONAL {
    ?manif dcterms:date ?date.
  }

  OPTIONAL {
    ?formerexpr dcterms:type ?type.
  }
}`;
}

function relatedAuthorsQuery(authorUri) {
    return `
PREFIX dcterms: <http://purl.org/dc/terms/>
PREFIX bnfroles: <http://data.bnf.fr/vocabulary/roles/>
PREFIX foaf: <http://xmlns.com/foaf/0.1/>

SELECT ?role1 ?manif ?title ?role2 ?author2 ?author2name WHERE {
  ?expr dcterms:contributor <${authorUri}#foaf:Person> ;
        ?role1 <${authorUri}#foaf:Person> ;
        ?role2 ?author2.

  ?author2 a foaf:Person;
       foaf:name ?author2name.

  ?formerexpr owl:sameAs ?expr.
  ?manif rdarelationships:expressionManifested ?formerexpr ;
         dcterms:title ?title.

  FILTER(regex(?role1, 'http://data.bnf.fr/vocabulary/roles'))
  FILTER(regex(?role2, 'http://data.bnf.fr/vocabulary/roles'))
  FILTER(?author2 != <${authorUri}#foaf:Person>)
}`;
}

function placesQuery() {
    return `
PREFIX skos: <http://www.w3.org/2004/02/skos/core#>
PREFIX foaf: <http://xmlns.com/foaf/0.1/>
PREFIX geo: <http://www.w3.org/2003/01/geo/wgs84_pos#>

SELECT ?geo ?label ?lat ?lng ?geonames WHERE {

  ?geoconcept skos:closeMatch ?rameau ;
    skos:prefLabel ?label ;
    foaf:focus ?geo.

  OPTIONAL {
    ?geoconcept skos:exactMatch ?geonames.
    FILTER(regex(?geonames, 'http://sws.geonames.org/'))
  }

  ?geo geo:lat ?lat ;
       geo:long ?lng.

  ?rameau a skos:Concept.
}
`;
}

function studyDocsQuery(authorityUri) {
    return `
PREFIX rdarelationships: <http://rdvocab.info/RDARelationshipsWEMI/>
PREFIX dcterms: <http://purl.org/dc/terms/>

SELECT DISTINCT ?manif ?work ?title ?date ?type ?gallica WHERE {

  ?formerexpr owl:sameAs ?expr.

  ?manif dcterms:subject <${authorityUri}> ;
         rdarelationships:expressionManifested ?formerexpr ;
         dcterms:title ?title.

  OPTIONAL {
    ?manif rdarelationships:workManifested ?work.
  }

  OPTIONAL {
    ?manif rdarelationships:electronicReproduction ?gallica.
  }

  OPTIONAL {
    ?manif dcterms:date ?date.
  }

  OPTIONAL {
    ?formerexpr dcterms:type ?type.
  }

}`;
}

function authorWorksQuery(authorUri) {
    return `
PREFIX dcterms: <http://purl.org/dc/terms/>
PREFIX rdarelationships: <http://rdvocab.info/RDARelationshipsWEMI/>

SELECT ?work ?title (COUNT(?manif) as ?count) WHERE {
 ?work dcterms:creator <${authorUri}#foaf:Person>;
    dcterms:title ?title.

  ?manif rdarelationships:workManifested ?work.
}
GROUP BY ?work ?title
ORDER BY DESC(?count)
`;
}

function storeQuery(query) {
    if (window.localStorage !== undefined) {
        localStorage.setItem('queryVal_main', JSON.stringify({val: query}));
    }
}

function hackMainInfos(pageUri) {
    const h1 = document.querySelector('h1');
    insertBefore(sparqlLink(authorityMainInfos(pageUri)), h1.firstChild);
}

function hackAuthorDocumentSections(authorUri) {
    qsa('.dtmanifs > h3 > a:first-child').forEach(link => {
        const role = link.href.split('/').pop();
        const h3 = link.parentElement;
        h3.insertBefore(sparqlLink(authorDocsQuery(role, authorUri)), link);
    });
}

function hackRelatedAuthors(authorUri) {
    qsa(`.bloc-contenu a[href="http://data.bnf.fr/linked-authors/${authorUri.slice(32, 40)}"]`).forEach(a => {
        insertBefore(sparqlLink(relatedAuthorsQuery(authorUri)), a);
    });
}

function hackAuthorTitle(authorUri) {
    const div = document.querySelector('#allmanifs');
    insertBefore(sparqlLink(authorWorksQuery(authorUri)), div.firstChild);
}

function hackAuthorPage(pageUri) {
    hackMainInfos(pageUri);
    hackAuthorDocumentSections(pageUri);
    hackRelatedAuthors(pageUri);
    hackStudiesSection(pageUri);
    hackAuthorTitle(pageUri);
}

function hackWorkDocumentSections(workUri) {
    qsa('.dtmanifs > h3').forEach(h3 => {
        h3.insertBefore(sparqlLink(workDocsQuery(workUri)), h3.firstChild);
    });
}

function hackStudiesSection(pageUri) {
    qsa('.bloc-contenu a[href$=studies').forEach(link => {
        insertBefore(sparqlLink(studyDocsQuery(pageUri)), link);
    });
}

function hackGeoDocumentSections(geoUri) {
    qsa('.dtmanifs > h3').forEach(h3 => {
        h3.insertBefore(sparqlLink(studyDocsQuery(geoUri)), h3.firstChild);
    });
}

function hackWorkPage(pageUri) {
    hackMainInfos(pageUri);
    hackWorkDocumentSections(pageUri);
    hackStudiesSection(pageUri);
}

function hackGeoPage(pageUri) {
    hackMainInfos(pageUri);
    hackGeoDocumentSections(pageUri);
}

function hackPeriodicalPage(pageUri) {
    hackMainInfos(pageUri);
}

function hackHomePage() {
    const mapBlock = document.querySelector('#map-geo');
    insertBefore(sparqlLink(placesQuery()), mapBlock);
}

function guessPageType() {
    const ogtype = document.querySelector('meta[property="og:type"]');
    if (ogtype && ogtype.content) {
        return ogtype.content;
    }
    const path = document.location.pathname;
    if (path === '/') {
        return 'home';
    } else if (path.startsWith('/date')) {
        return 'date';
    } else if (path.startsWith('/3') || path.startsWith('/4')) {
        const divtype = document.querySelector('#fullpage div[itemtype]'),
              itemtype = divtype ? divtype.getAttribute('itemtype') : null;
        if (itemtype === 'http://schema.org/Periodical') {
            return 'periodical';
        } else if (itemtype === 'http://schema.org/TheaterEvent') {
            return 'performance';
        }
    }
    return null;
}

function showModal(query) {
    if (!MODAL) {
        MODAL = buildModal();
        document.body.appendChild(MODAL);
    }
    const backdrop = modalBackdrop();
    document.body.appendChild(backdrop);
    backdrop.classList.add('in');
    MODAL.style.display = 'block';
    MODAL.classList.add('fade', 'in');
    MODAL.querySelector('#sparqlit-modal-pre').textContent = query;
    MODAL.querySelector('#sparqlit-modal-exec').href = sparqlUrl(query);
    MODAL.querySelector('#sparqlit-modal-dl').href = sparqlUrl(query, 'application/sparql-results+json');
    MODAL.querySelector('#sparqlit-modal-edit').onclick = () => storeQuery(query);
}

function hideModal() {
    const backdrop = document.querySelector('#sparqlit-modal-backdrop');
    backdrop.parentNode.removeChild(backdrop);
    MODAL.classList.remove('fade', 'in');
    MODAL.style.display = 'none';
}

let permalink = document.querySelector('link[rel=bookmark]'),
    pageUri = (permalink === null) ? null : permalink.href;

switch(guessPageType()) {
case 'author':
    hackAuthorPage(pageUri);
    break;
case 'book':
    hackWorkPage(pageUri);
    break;
case 'geopoint':
    hackGeoPage(pageUri);
    break;
case 'periodical':
    hackPeriodicalPage(pageUri);
    break;
case 'home':
    if (document.location.pathname === '/') {
        hackHomePage();
    }
// case 'article'
// case 'date'
// case 'performace'
}
<|endoftext|>


<commit_before>angular.module('umbraco.security.interceptor')
    // This http interceptor listens for authentication successes and failures
    .factory('securityInterceptor', ['$injector', 'securityRetryQueue', 'notificationsService', 'requestInterceptorFilter', function ($injector, queue, notifications, requestInterceptorFilter) {
        return function(promise) {

            return promise.then(
                function(originalResponse) {
                    // Intercept successful requests

                    //Here we'll check if our custom header is in the response which indicates how many seconds the user's session has before it
                    //expires. Then we'll update the user in the user service accordingly.
                    var headers = originalResponse.headers();
                    if (headers["x-umb-user-seconds"]) {
                        // We must use $injector to get the $http service to prevent circular dependency
                        var userService = $injector.get('userService');
                        userService.setUserTimeout(headers["x-umb-user-seconds"]);
                    }

                    return promise;
                }, function(originalResponse) {
                    // Intercept failed requests

                    //Here we'll check if we should ignore the error, this will be based on an original header set
                    var headers = originalResponse.config ? originalResponse.config.headers : {};
                    if (headers["x-umb-ignore-error"] === "ignore" || originalResponse.config.umbIgnoreErrors === true) {
                        //exit/ignore
                        return promise;
                    }
                    var filtered = _.find(requestInterceptorFilter(), function(val) {
                        return originalResponse.config.url.indexOf(val) > 0;
                    });
                    if (filtered) {
                        return promise;
                    }

                    //A 401 means that the user is not logged in
                    if (originalResponse.status === 401) {

                      var userService = $injector.get('userService'); // see above

                      //Associate the user name with the retry to ensure we retry for the right user
                      promise = userService.getCurrentUser()
                        .then(function (user) {
                          var userName = user ? user.name : null;
                          //The request bounced because it was not authorized - add a new request to the retry queue
                          return queue.pushRetryFn('unauthorized-server', userName, function retryRequest() {
                            // We must use $injector to get the $http service to prevent circular dependency
                            return $injector.get('$http')(originalResponse.config);
                          });
                        });
                    }
                    else if (originalResponse.status === 404) {

                        //a 404 indicates that the request was not found - this could be due to a non existing url, or it could
                        //be due to accessing a url with a parameter that doesn't exist, either way we should notifiy the user about it

                        var errMsg = "The URL returned a 404 (not found):
" + originalResponse.config.url.split('?')[0] + "";
                        if (originalResponse.data && originalResponse.data.ExceptionMessage) {
                            errMsg += "
 with error:
" + originalResponse.data.ExceptionMessage + "";
                        }
                        if (originalResponse.config.data) {
                            errMsg += "
 with data:
" + angular.toJson(originalResponse.config.data) + "
Contact your administrator for information.";
                        }

                        notifications.error(
                            "Request error",
                            errMsg);

                    }
                    else if (originalResponse.status === 403) {
                        //if the status was a 403 it means the user didn't have permission to do what the request was trying to do.
                        //How do we deal with this now, need to tell the user somehow that they don't have permission to do the thing that was
                        //requested. We can either deal with this globally here, or we can deal with it globally for individual requests on the umbRequestHelper,
                        // or completely custom for services calling resources.

                        //http://issues.umbraco.org/issue/U4-2749

                        //It was decided to just put these messages into the normal status messages.

                        var msg = "Unauthorized access to URL:
" + originalResponse.config.url.split('?')[0] + "";
                        if (originalResponse.config.data) {
                            msg += "
 with data:
" + angular.toJson(originalResponse.config.data) + "
Contact your administrator for information.";
                        }

                        notifications.error(
                            "Authorization error",
                            msg);
                    }

                    return promise;
                });
        };
    }])

    .value('requestInterceptorFilter', function() {
        return ["www.gravatar.com"];
    })

    // We have to add the interceptor to the queue as a string because the interceptor depends upon service instances that are not available in the config block.
    .config(['$httpProvider', function ($httpProvider) {
        $httpProvider.responseInterceptors.push('securityInterceptor');
    }]);
<commit_msg>Added some extra validation<commit_after>angular.module('umbraco.security.interceptor')
    // This http interceptor listens for authentication successes and failures
    .factory('securityInterceptor', ['$injector', 'securityRetryQueue', 'notificationsService', 'requestInterceptorFilter', function ($injector, queue, notifications, requestInterceptorFilter) {
        return function(promise) {

            return promise.then(
                function(originalResponse) {
                    // Intercept successful requests

                    //Here we'll check if our custom header is in the response which indicates how many seconds the user's session has before it
                    //expires. Then we'll update the user in the user service accordingly.
                    var headers = originalResponse.headers();
                    if (headers["x-umb-user-seconds"]) {
                        // We must use $injector to get the $http service to prevent circular dependency
                        var userService = $injector.get('userService');
                        userService.setUserTimeout(headers["x-umb-user-seconds"]);
                    }

                    return promise;
                }, function(originalResponse) {
                    // Intercept failed requests

                    // Make sure we have the configuration of the request (don't we always?)
                    var config = originalResponse.config ? originalResponse.config : {};

                    // Make sure we have an object for the headers of the request
                    var headers = config.headers ? config.headers : {};

                    //Here we'll check if we should ignore the error (either based on the original header set or the request configuration)
                    if (headers["x-umb-ignore-error"] === "ignore" || config.umbIgnoreErrors === true) {
                        //exit/ignore
                        return promise;
                    }
                    var filtered = _.find(requestInterceptorFilter(), function(val) {
                        return config.url.indexOf(val) > 0;
                    });
                    if (filtered) {
                        return promise;
                    }

                    //A 401 means that the user is not logged in
                    if (originalResponse.status === 401) {

                      var userService = $injector.get('userService'); // see above

                      //Associate the user name with the retry to ensure we retry for the right user
                      promise = userService.getCurrentUser()
                        .then(function (user) {
                          var userName = user ? user.name : null;
                          //The request bounced because it was not authorized - add a new request to the retry queue
                          return queue.pushRetryFn('unauthorized-server', userName, function retryRequest() {
                            // We must use $injector to get the $http service to prevent circular dependency
                            return $injector.get('$http')(originalResponse.config);
                          });
                        });
                    }
                    else if (originalResponse.status === 404) {

                        //a 404 indicates that the request was not found - this could be due to a non existing url, or it could
                        //be due to accessing a url with a parameter that doesn't exist, either way we should notifiy the user about it

                        var errMsg = "The URL returned a 404 (not found):
" + originalResponse.config.url.split('?')[0] + "";
                        if (originalResponse.data && originalResponse.data.ExceptionMessage) {
                            errMsg += "
 with error:
" + originalResponse.data.ExceptionMessage + "";
                        }
                        if (originalResponse.config.data) {
                            errMsg += "
 with data:
" + angular.toJson(originalResponse.config.data) + "
Contact your administrator for information.";
                        }

                        notifications.error(
                            "Request error",
                            errMsg);

                    }
                    else if (originalResponse.status === 403) {
                        //if the status was a 403 it means the user didn't have permission to do what the request was trying to do.
                        //How do we deal with this now, need to tell the user somehow that they don't have permission to do the thing that was
                        //requested. We can either deal with this globally here, or we can deal with it globally for individual requests on the umbRequestHelper,
                        // or completely custom for services calling resources.

                        //http://issues.umbraco.org/issue/U4-2749

                        //It was decided to just put these messages into the normal status messages.

                        var msg = "Unauthorized access to URL:
" + originalResponse.config.url.split('?')[0] + "";
                        if (originalResponse.config.data) {
                            msg += "
 with data:
" + angular.toJson(originalResponse.config.data) + "
Contact your administrator for information.";
                        }

                        notifications.error(
                            "Authorization error",
                            msg);
                    }

                    return promise;
                });
        };
    }])

    .value('requestInterceptorFilter', function() {
        return ["www.gravatar.com"];
    })

    // We have to add the interceptor to the queue as a string because the interceptor depends upon service instances that are not available in the config block.
    .config(['$httpProvider', function ($httpProvider) {
        $httpProvider.responseInterceptors.push('securityInterceptor');
    }]);
<|endoftext|>


<commit_before>#!/usr/bin/env node
'use strict';
/*    Copyright 2016 Firewalla LLC
 *
 *    This program is free software: you can redistribute it and/or  modify
 *    it under the terms of the GNU Affero General Public License, version 3,
 *    as published by the Free Software Foundation.
 *
 *    This program is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    GNU Affero General Public License for more details.
 *
 *    You should have received a copy of the GNU Affero General Public License
 *    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

/*
 * Kickstart()
 *
 * Create default endpoints
 * Create default group
 *
 * Launch camera app
 *
 * bonjour endpoint information
 *
 * name: 'Encipher:'+eptname
 * type: 'http'
 * port: '80'
 * txt: {
 *         'gid':'group id',
 *         'er':'encrypted rendezvous key',
 *         'keyhint':'Please enter the CPU id of your device pasted on a sticker'.
 *      }
 *
 * setup redenzvous at rndezvous key
 * query every x min until group is none empty, or first invite
 */

process.title = "FireKick";
require('events').EventEmitter.prototype._maxListeners = 100;

const log = require("../net2/logger.js")(__filename);

const fs = require('fs');
const cp = require('child_process');
const exec = require('child-process-promise').exec;
const cloud = require('../encipher');
const program = require('commander');
const storage = require('node-persist');
const mathuuid = require('../lib/Math.uuid.js');
const rclient = require('../util/redis_manager.js').getRedisClient()
const SSH = require('../extension/ssh/ssh.js');
const ssh = new SSH('info');

const platformLoader = require('../platform/PlatformLoader.js');
const platform = platformLoader.getPlatform();

const util = require('util');

const f = require('../net2/Firewalla.js');

const fConfig = require('../net2/config.js');

const bone = require("../lib/Bone.js");

const SysManager = require('../net2/SysManager.js');
const sysManager = new SysManager();
const firewallaConfig = require('../net2/config.js').getConfig();

const InterfaceDiscoverSensor = require('../sensor/InterfaceDiscoverSensor');
const interfaceDiscoverSensor = new InterfaceDiscoverSensor();

const EptCloudExtension = require('../extension/ept/eptcloud.js');

const fwDiag = require('../extension/install/diag.js');

const FWInvitation = require('./invitation.js');

const Diag = require('../extension/diag/app.js');

let terminated = false;

log.forceInfo("+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++");
log.forceInfo("FireKick Starting ");
log.forceInfo("+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++");

function delay(t) {
  return new Promise(function(resolve) {
    setTimeout(resolve, t)
  });
}

(async() => {
  await rclient.delAsync("firekick:pairing:message");
  await sysManager.setConfig(firewallaConfig)
  await interfaceDiscoverSensor.run()
})();

const license = require('../util/license.js');

program.version('0.0.2')
  .option('--config [config]', 'configuration file, default to ./config/default.config')
  .option('--admin_invite_timeout [admin_invite_timeout]', 'admin invite timeout');

program.parse(process.argv);

if (program.config == null) {
  log.info("config file is required");
  process.exit(1);
}

let _license = license.getLicenseSync();

let configfile = fs.readFileSync(program.config, 'utf8');
if (configfile == null) {
  log.info("Unable to read config file");
}
let config = JSON.parse(configfile);
if (!config) {
  log.info("Error processing configuration information");
}
if (!config.controllers) {
  log.info("Controller missing from configuration file");
  process.exit(1);
}

let eptname = config.endpoint_name;
if (config.endpoint_name != null) {
  eptname = config.endpoint_name;
} else if (program.endpoint_name != null) {
  eptname = program.endpoint_name;
}

function getUserHome() {
  return process.env[(process.platform == 'win32') ? 'USERPROFILE' : 'HOME'];
}

let dbPath = getUserHome() + "/.encipher/db";
let configPath = getUserHome() + "/.encipher";

if (!fs.existsSync(configPath)) {
  fs.mkdirSync(configPath);
}
if (!fs.existsSync(dbPath)) {
  fs.mkdirSync(dbPath);
}

let symmetrickey = generateEncryptionKey(_license);

// start a diagnostic page for people to access during first binding process
const diag = new Diag()
diag.start()
diag.iptablesRedirection()

let eptcloud = new cloud(eptname, null);
eptcloud.debug(false);
let service = null;

storage.initSync({
  'dir': dbPath
});

function pad(value, length) {
  return (value.toString().length < length) ? pad("0" + value, length) : value;
}

function generateEncryptionKey(license) {
  // when there is no local license file, use default one
  let userKey = (license != null ? (license.SUUID || license.DATA.SUUID) : "cybersecuritymadesimple");
  //  log.info(`User Key is: ${userKey}`);
  let seed = mathuuid.uuidshort(32 - userKey.length);

  return {
    'key': userKey + seed,
    'seed': seed,
    'userkey': userKey,
    'noLicenseMode': license == null,
    'license': license != null && license.UUID
  };
}

function initializeGroup(callback) {
  let groupId = storage.getItemSync('groupId');
  if (groupId != null) {
    log.info("Found stored group x", groupId);
    callback(null, groupId);
    return;
  }

  log.info("Using identity:", eptcloud.eid);
  log.info("Creating new group ", config.service, config.endpoint_name);
  let meta = JSON.stringify({
    'type': config.serviceType,
    'member': config.memberType,
    'model': platform.getName()
  });
  eptcloud.eptcreateGroup(config.service, meta, config.endpoint_name, function (e, r) {
    log.info(r);
    if (e == null && r != null) {
      storage.setItemSync('groupId', r);
    }
    callback(e, r);
  });
}

function postAppLinked() {
  // When app is linked, to secure device, ssh password will be
  // automatically reset when boot up every time

  // only do this in production and always do after 15 seconds ...
  // the 15 seconds wait is for the process to wake up

  if(f.isProductionOrBeta() &&
    // resetPassword by default unless resetPassword flag is explictly set to false
    (typeof fConfig.resetPassword === 'undefined' ||
      fConfig.resetPassword === true)) {
    setTimeout(()=> {
      ssh.resetRandomPassword((err,password) => {
        if(err) {
          log.info("Failed to reset ssh password");
        } else {
          log.info("A new random SSH password is used!");
          sysManager.setSSHPassword(password);
        }
      });
    }, 15000);
  }
}

async function inviteFirstAdmin(gid) {
  log.forceInfo("Initializing first admin:", gid);

  const gidPrefix = gid.substring(0, 8);

  process.on('SIGTERM', exitHandler.bind(null, {
    terminated: true, cleanup: true, gid: gid, exit: true
  }));

  const group = await eptcloud.groupFindAsync(gid)

  if (!group || !group.symmetricKeys) {
    return false;
  }

  // number of key sym keys equals to number of members in this group
  // set this number to redis so that other js processes get this info
  let count = group.symmetricKeys.length;

  await rclient.hsetAsync("sys:ept", "group_member_cnt", count);

  const eptCloudExtension = new EptCloudExtension(eptcloud, gid);
  await eptCloudExtension.recordAllRegisteredClients(gid).catch((err) => {
    log.error("Failed to record registered clients, err:", err);
  });

  // new group without any apps bound;
  await platform.turnOnPowerLED();

  let fwInvitation = new FWInvitation(eptcloud, gid, symmetrickey);
  fwInvitation.diag = diag

  if (count > 1) {
    log.forceInfo(`Found existing group ${gid} with ${count} members`);
    fwInvitation.totalTimeout = 60 * 10; // 10 mins only for additional binding
    fwInvitation.recordFirstBinding = false // don't record for additional binding

    // broadcast message should already be updated, a new encryption message should be used instead of default one
    if(symmetrickey.userkey === "cybersecuritymadesimple") {
      log.warn("Encryption key should NOT be default after app linked");
    }
  }

  const expireDate = Math.floor(new Date() / 1000) + 600;
  diag.expireDate = expireDate;

  await fwDiag.submitInfo({
    event: "PAIRSTART",
    msg:"Pairing Ready",
    expire: expireDate,
    gidPrefix: gidPrefix
  }).catch((err) => {
    log.error("Failed to submit diag info", err);
  });

  const result = await fwInvitation.broadcast()

  if (result.status == 'success') {
    log.forceInfo("EXIT KICKSTART AFTER JOIN");
    log.info("some license stuff on device:", result.payload);

    postAppLinked()

    if (count > 1) {
      const eptCloudExtension = new EptCloudExtension(eptcloud, gid);
      await eptCloudExtension.job().catch((err) => {
        log.error("Failed to update group info, err:", err);
      });
    }

    await rclient.hsetAsync("sys:ept", "group_member_cnt", count + 1)

    await platform.turnOffPowerLED();

    await fwDiag.submitInfo({
      event: "PAIREND",
      msg: "Pairing Ended",
      gidPrefix: gidPrefix
    }).catch((err) => {
      log.error("Failed to submit diag info", err);
    });

    await launchService2(gid, null);
  } else {
    log.forceInfo("EXIT KICKSTART AFTER TIMEOUT");

    if (count > 1) postAppLinked()

    await fwDiag.submitInfo({
      event: "PAIREND_TIMEOUT",
      msg: "Pairing Ended",
      gidPrefix: gidPrefix
    }).catch((err) => {
      log.error("Failed to submit diag info", err);
    });
  }
}

function launchService2(gid,callback) {
  fs.writeFileSync('/home/pi/.firewalla/ui.conf',JSON.stringify({gid:gid}),'utf-8');

  // don't start bro until app is linked
  cp.execSync("sudo systemctl start brofish");
  cp.execSync("sudo systemctl enable brofish"); // even auto-start for future reboots

  // // start fire api
  // if (require('fs').existsSync("/tmp/FWPRODUCTION")) {
  //   cp.exec("sudo systemctl start fireapi");
  // } else {
  //   if (fs.existsSync("/.dockerenv")) {
  //     cp.exec("cd api; forever start -a --uid api bin/www");
  //   } else {
  //     cp.exec("sudo systemctl start fireapi");
  //   }
  // }
}

function login() {
  log.info("Logging in cloud");
  eptcloud.eptlogin(config.appId, config.appSecret, null, config.endpoint_name, function (err, result) {
    if (err == null) {
      log.info("Cloud Logged In")

      diag.connected = true

      initializeGroup(async function (err, gid) {
        if (gid) {
          // NOTE: This should be the only code to update sys:ept to avoid race condition
          log.info("Storing Firewalla Cloud Token info to redis");
          // log.info("EID:", eptcloud.eid);
          // log.info("GID:", gid);
          // log.info("TOKEN:", eptcloud.token);
          rclient.hmset("sys:ept", {
            eid: eptcloud.eid,
            token: eptcloud.token,
            gid: gid
          }, (err, data) => {
            if (err) {
            }
            log.info("Set sys:ept", err, data, eptcloud.eid, eptcloud.token, gid);
          });

          await inviteFirstAdmin(gid)

          await platform.turnOffPowerLED();
          await exec("sleep 2; sudo systemctl stop firekick")

        } else {
          log.error("Invalid gid");
          process.exit();
        }
      });
    } else {
      log.error("Unable to login", err);
      process.exit();
    }
  });
}

eptcloud.loadKeys()
  .then(login)

process.stdin.resume();

async function sendTerminatedInfoToDiagServer(gid) {
  if (terminated)
    return;
  const gidPrefix = gid.substring(0, 8);
  log.forceInfo("EXIT KICKSTART DUE TO PROCESS TERMINATION");
  terminated = true;
  await fwDiag.submitInfo({
    event: "FIREKICK_TERMINATED",
    msg: "Firekick Terminated",
    gidPrefix: gidPrefix
  }).catch((err) => {
    log.error("failed to submit diag info on termination", err);
  });
}

async function exitHandler(options, err) {
  if (err) log.info(err.stack);
  if (options.cleanup) {
    await platform.turnOffPowerLED();
    await diag.iptablesRedirection(false);
  }
  if (options.terminated) await sendTerminatedInfoToDiagServer(options.gid);
  if (options.exit) {
    process.exit();
  }
}

//do something when app is closing
process.on('beforeExit', exitHandler.bind(null, {
  cleanup: true, exit: true
}));

//catches ctrl+c event
process.on('SIGINT', exitHandler.bind(null, {
  cleanup: true, exit: true
}));

process.on('uncaughtException',(err)=>{
  log.info("################### CRASH #############");
  log.info("+-+-+-",err.message,err.stack);
  if (err && err.message && err.message.includes("Redis connection")) {
    return;
  }
  bone.logAsync("error", {
    type: 'FIREWALLA.KICKSTART.exception',
    msg: err.message,
    stack: err.stack,
    err: JSON.stringify(err)
  });
  setTimeout(()=>{
    cp.execSync("touch /home/pi/.firewalla/managed_reboot")
    process.exit(1);
  },1000*2);
});

process.on('unhandledRejection', (reason, p)=>{
  let msg = "Possibly Unhandled Rejection at: Promise " + p + " reason: "+ reason;
  log.warn('###### Unhandled Rejection',msg,reason.stack);
  bone.logAsync("error", {
    type: 'FIREWALLA.KICKSTART.unhandledRejection',
    msg: msg,
    stack: reason.stack,
    err: JSON.stringify(reason)
  });
});
<commit_msg>restart brofish accordingly in kickstart.js<commit_after>#!/usr/bin/env node
'use strict';
/*    Copyright 2016 Firewalla LLC
 *
 *    This program is free software: you can redistribute it and/or  modify
 *    it under the terms of the GNU Affero General Public License, version 3,
 *    as published by the Free Software Foundation.
 *
 *    This program is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    GNU Affero General Public License for more details.
 *
 *    You should have received a copy of the GNU Affero General Public License
 *    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

/*
 * Kickstart()
 *
 * Create default endpoints
 * Create default group
 *
 * Launch camera app
 *
 * bonjour endpoint information
 *
 * name: 'Encipher:'+eptname
 * type: 'http'
 * port: '80'
 * txt: {
 *         'gid':'group id',
 *         'er':'encrypted rendezvous key',
 *         'keyhint':'Please enter the CPU id of your device pasted on a sticker'.
 *      }
 *
 * setup redenzvous at rndezvous key
 * query every x min until group is none empty, or first invite
 */

process.title = "FireKick";
require('events').EventEmitter.prototype._maxListeners = 100;

const log = require("../net2/logger.js")(__filename);

const fs = require('fs');
const cp = require('child_process');
const exec = require('child-process-promise').exec;
const cloud = require('../encipher');
const program = require('commander');
const storage = require('node-persist');
const mathuuid = require('../lib/Math.uuid.js');
const rclient = require('../util/redis_manager.js').getRedisClient()
const SSH = require('../extension/ssh/ssh.js');
const ssh = new SSH('info');

const platformLoader = require('../platform/PlatformLoader.js');
const platform = platformLoader.getPlatform();

const util = require('util');
const writeFileAsync = util.promisify(fs.writeFile);

const f = require('../net2/Firewalla.js');

const fConfig = require('../net2/config.js');

const bone = require("../lib/Bone.js");

const SysManager = require('../net2/SysManager.js');
const sysManager = new SysManager();
const firewallaConfig = require('../net2/config.js').getConfig();

const InterfaceDiscoverSensor = require('../sensor/InterfaceDiscoverSensor');
const interfaceDiscoverSensor = new InterfaceDiscoverSensor();

const EptCloudExtension = require('../extension/ept/eptcloud.js');

const fwDiag = require('../extension/install/diag.js');

const FWInvitation = require('./invitation.js');

const Diag = require('../extension/diag/app.js');

let terminated = false;

log.forceInfo("+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++");
log.forceInfo("FireKick Starting ");
log.forceInfo("+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++");

function delay(t) {
  return new Promise(function(resolve) {
    setTimeout(resolve, t)
  });
}

(async() => {
  await rclient.delAsync("firekick:pairing:message");
  await sysManager.setConfig(firewallaConfig)
  await interfaceDiscoverSensor.run()
})();

const license = require('../util/license.js');

program.version('0.0.2')
  .option('--config [config]', 'configuration file, default to ./config/default.config')
  .option('--admin_invite_timeout [admin_invite_timeout]', 'admin invite timeout');

program.parse(process.argv);

if (program.config == null) {
  log.info("config file is required");
  process.exit(1);
}

let _license = license.getLicenseSync();

let configfile = fs.readFileSync(program.config, 'utf8');
if (configfile == null) {
  log.info("Unable to read config file");
}
let config = JSON.parse(configfile);
if (!config) {
  log.info("Error processing configuration information");
}
if (!config.controllers) {
  log.info("Controller missing from configuration file");
  process.exit(1);
}

let eptname = config.endpoint_name;
if (config.endpoint_name != null) {
  eptname = config.endpoint_name;
} else if (program.endpoint_name != null) {
  eptname = program.endpoint_name;
}

function getUserHome() {
  return process.env[(process.platform == 'win32') ? 'USERPROFILE' : 'HOME'];
}

let dbPath = getUserHome() + "/.encipher/db";
let configPath = getUserHome() + "/.encipher";

if (!fs.existsSync(configPath)) {
  fs.mkdirSync(configPath);
}
if (!fs.existsSync(dbPath)) {
  fs.mkdirSync(dbPath);
}

let symmetrickey = generateEncryptionKey(_license);

// start a diagnostic page for people to access during first binding process
const diag = new Diag()
diag.start()
diag.iptablesRedirection()

let eptcloud = new cloud(eptname, null);
eptcloud.debug(false);
let service = null;

storage.initSync({
  'dir': dbPath
});

function pad(value, length) {
  return (value.toString().length < length) ? pad("0" + value, length) : value;
}

function generateEncryptionKey(license) {
  // when there is no local license file, use default one
  let userKey = (license != null ? (license.SUUID || license.DATA.SUUID) : "cybersecuritymadesimple");
  //  log.info(`User Key is: ${userKey}`);
  let seed = mathuuid.uuidshort(32 - userKey.length);

  return {
    'key': userKey + seed,
    'seed': seed,
    'userkey': userKey,
    'noLicenseMode': license == null,
    'license': license != null && license.UUID
  };
}

function initializeGroup(callback) {
  let groupId = storage.getItemSync('groupId');
  if (groupId != null) {
    log.info("Found stored group x", groupId);
    callback(null, groupId);
    return;
  }

  log.info("Using identity:", eptcloud.eid);
  log.info("Creating new group ", config.service, config.endpoint_name);
  let meta = JSON.stringify({
    'type': config.serviceType,
    'member': config.memberType,
    'model': platform.getName()
  });
  eptcloud.eptcreateGroup(config.service, meta, config.endpoint_name, function (e, r) {
    log.info(r);
    if (e == null && r != null) {
      storage.setItemSync('groupId', r);
    }
    callback(e, r);
  });
}

function postAppLinked() {
  // When app is linked, to secure device, ssh password will be
  // automatically reset when boot up every time

  // only do this in production and always do after 15 seconds ...
  // the 15 seconds wait is for the process to wake up

  if(f.isProductionOrBeta() &&
    // resetPassword by default unless resetPassword flag is explictly set to false
    (typeof fConfig.resetPassword === 'undefined' ||
      fConfig.resetPassword === true)) {
    setTimeout(()=> {
      ssh.resetRandomPassword((err,password) => {
        if(err) {
          log.info("Failed to reset ssh password");
        } else {
          log.info("A new random SSH password is used!");
          sysManager.setSSHPassword(password);
        }
      });
    }, 15000);
  }
}

async function inviteFirstAdmin(gid) {
  log.forceInfo("Initializing first admin:", gid);

  const gidPrefix = gid.substring(0, 8);

  process.on('SIGTERM', exitHandler.bind(null, {
    terminated: true, cleanup: true, gid: gid, exit: true
  }));

  const group = await eptcloud.groupFindAsync(gid)

  if (!group || !group.symmetricKeys) {
    return false;
  }

  // number of key sym keys equals to number of members in this group
  // set this number to redis so that other js processes get this info
  let count = group.symmetricKeys.length;

  await rclient.hsetAsync("sys:ept", "group_member_cnt", count);

  const eptCloudExtension = new EptCloudExtension(eptcloud, gid);
  await eptCloudExtension.recordAllRegisteredClients(gid).catch((err) => {
    log.error("Failed to record registered clients, err:", err);
  });

  // new group without any apps bound;
  await platform.turnOnPowerLED();

  let fwInvitation = new FWInvitation(eptcloud, gid, symmetrickey);
  fwInvitation.diag = diag

  if (count > 1) {
    log.forceInfo(`Found existing group ${gid} with ${count} members`);
    fwInvitation.totalTimeout = 60 * 10; // 10 mins only for additional binding
    fwInvitation.recordFirstBinding = false // don't record for additional binding

    // broadcast message should already be updated, a new encryption message should be used instead of default one
    if(symmetrickey.userkey === "cybersecuritymadesimple") {
      log.warn("Encryption key should NOT be default after app linked");
    }
  }

  const expireDate = Math.floor(new Date() / 1000) + 600;
  diag.expireDate = expireDate;

  await fwDiag.submitInfo({
    event: "PAIRSTART",
    msg:"Pairing Ready",
    expire: expireDate,
    gidPrefix: gidPrefix
  }).catch((err) => {
    log.error("Failed to submit diag info", err);
  });

  const result = await fwInvitation.broadcast()

  if (result.status == 'success') {
    log.forceInfo("EXIT KICKSTART AFTER JOIN");
    log.info("some license stuff on device:", result.payload);

    postAppLinked()

    if (count > 1) {
      const eptCloudExtension = new EptCloudExtension(eptcloud, gid);
      await eptCloudExtension.job().catch((err) => {
        log.error("Failed to update group info, err:", err);
      });
    }

    await rclient.hsetAsync("sys:ept", "group_member_cnt", count + 1)

    await platform.turnOffPowerLED();

    await fwDiag.submitInfo({
      event: "PAIREND",
      msg: "Pairing Ended",
      gidPrefix: gidPrefix
    }).catch((err) => {
      log.error("Failed to submit diag info", err);
    });

    await launchService2(gid, null);
  } else {
    log.forceInfo("EXIT KICKSTART AFTER TIMEOUT");

    if (count > 1) postAppLinked()

    await fwDiag.submitInfo({
      event: "PAIREND_TIMEOUT",
      msg: "Pairing Ended",
      gidPrefix: gidPrefix
    }).catch((err) => {
      log.error("Failed to submit diag info", err);
    });
  }
}

async function launchService2(gid) {
  await writeFileAsync('/home/pi/.firewalla/ui.conf', JSON.stringify({gid:gid}), 'utf8');

  // don't start bro until app is linked
  await exec("sudo systemctl is-active brofish").catch((err) => {
    // need to restart brofish
    log.info("Restart brofish.service ...");
    return exec("sudo systemctl restart brofish").catch((err) => { // use restart instead. use 'start' may be trapped due to 'TimeoutStartSec' in brofish.service
      log.error("Failed to restart brofish", err);
    });
  }).then(() => {
    log.info("Enable brofish.service ...");
    return exec("sudo systemctl enable brofish").catch((err) => { // even auto-start for future reboots
      log.error("Failed to enable brofish", err);
    });
  })

  // // start fire api
  // if (require('fs').existsSync("/tmp/FWPRODUCTION")) {
  //   cp.exec("sudo systemctl start fireapi");
  // } else {
  //   if (fs.existsSync("/.dockerenv")) {
  //     cp.exec("cd api; forever start -a --uid api bin/www");
  //   } else {
  //     cp.exec("sudo systemctl start fireapi");
  //   }
  // }
}

function login() {
  log.info("Logging in cloud");
  eptcloud.eptlogin(config.appId, config.appSecret, null, config.endpoint_name, function (err, result) {
    if (err == null) {
      log.info("Cloud Logged In")

      diag.connected = true

      initializeGroup(async function (err, gid) {
        if (gid) {
          // NOTE: This should be the only code to update sys:ept to avoid race condition
          log.info("Storing Firewalla Cloud Token info to redis");
          // log.info("EID:", eptcloud.eid);
          // log.info("GID:", gid);
          // log.info("TOKEN:", eptcloud.token);
          rclient.hmset("sys:ept", {
            eid: eptcloud.eid,
            token: eptcloud.token,
            gid: gid
          }, (err, data) => {
            if (err) {
            }
            log.info("Set sys:ept", err, data, eptcloud.eid, eptcloud.token, gid);
          });

          await inviteFirstAdmin(gid)

          await platform.turnOffPowerLED();
          exec("sleep 2; sudo systemctl stop firekick").catch((err) => {
            // this command will kill the program itself, catch this error silently
          })

        } else {
          log.error("Invalid gid");
          process.exit();
        }
      });
    } else {
      log.error("Unable to login", err);
      process.exit();
    }
  });
}

eptcloud.loadKeys()
  .then(login)

process.stdin.resume();

async function sendTerminatedInfoToDiagServer(gid) {
  if (terminated)
    return;
  const gidPrefix = gid.substring(0, 8);
  log.forceInfo("EXIT KICKSTART DUE TO PROCESS TERMINATION");
  terminated = true;
  await fwDiag.submitInfo({
    event: "FIREKICK_TERMINATED",
    msg: "Firekick Terminated",
    gidPrefix: gidPrefix
  }).catch((err) => {
    log.error("failed to submit diag info on termination", err);
  });
}

async function exitHandler(options, err) {
  if (err) log.info(err.stack);
  if (options.cleanup) {
    await platform.turnOffPowerLED();
    await diag.iptablesRedirection(false);
  }
  if (options.terminated) await sendTerminatedInfoToDiagServer(options.gid);
  if (options.exit) {
    process.exit();
  }
}

//do something when app is closing
process.on('beforeExit', exitHandler.bind(null, {
  cleanup: true, exit: true
}));

//catches ctrl+c event
process.on('SIGINT', exitHandler.bind(null, {
  cleanup: true, exit: true
}));

process.on('uncaughtException',(err)=>{
  log.info("################### CRASH #############");
  log.info("+-+-+-",err.message,err.stack);
  if (err && err.message && err.message.includes("Redis connection")) {
    return;
  }
  bone.logAsync("error", {
    type: 'FIREWALLA.KICKSTART.exception',
    msg: err.message,
    stack: err.stack,
    err: JSON.stringify(err)
  });
  setTimeout(()=>{
    cp.execSync("touch /home/pi/.firewalla/managed_reboot")
    process.exit(1);
  },1000*2);
});

process.on('unhandledRejection', (reason, p)=>{
  let msg = "Possibly Unhandled Rejection at: Promise " + p + " reason: "+ reason;
  log.warn('###### Unhandled Rejection',msg,reason.stack);
  bone.logAsync("error", {
    type: 'FIREWALLA.KICKSTART.unhandledRejection',
    msg: msg,
    stack: reason.stack,
    err: JSON.stringify(reason)
  });
});
<|endoftext|>


<commit_before>import "webcomponents.js/webcomponents-lite.min.js";

export default class CustomElement extends HTMLElement {
  createdCallback() {
    if(this.template) {
      let templateClone = document.importNode(this.template.content, true);
      this.createShadowRoot().appendChild(templateClone);
    }
  }

  attachedCallback() {}

  detachedCallback() {}

  attributeChangedCallback(attr, oldVal, newVal) {}

  static register(name, template) {
    document.registerElement(name, {
      prototype: Object.create(this.prototype, {
        template: {
          value: template,
          enumerable: true
        }
      })
    });
  }
}
<commit_msg>use unminified version on webcomponents-lite<commit_after>import "webcomponents.js/webcomponents-lite.js";

export default class CustomElement extends HTMLElement {
  createdCallback() {
    if(this.template) {
      let templateClone = document.importNode(this.template.content, true);
      this.createShadowRoot().appendChild(templateClone);
    }
  }

  attachedCallback() {}

  detachedCallback() {}

  attributeChangedCallback(attr, oldVal, newVal) {}

  static register(name, template) {
    document.registerElement(name, {
      prototype: Object.create(this.prototype, {
        template: {
          value: template,
          enumerable: true
        }
      })
    });
  }
}
<|endoftext|>


<commit_before>define([
    'jquery',
    'bluebird',
    'kb_userProfile_widget_base',
    'kb/service/client/userProfile'
],
    function ($, Promise, SocialWidget, UserProfileService) {
        "use strict";
        var widget = Object.create(SocialWidget, {
            init: {
                value: function (cfg) {
                    cfg.name = 'UserSearch';
                    cfg.title = 'Find Other Users';
                    this.SocialWidget_init(cfg);

                    return this;
                }
            },
            go: {
                value: function () {
                    this.start();
                    return this;
                }
            },
            setup: {
                value: function () {
                    // User profile service
                    if (this.runtime.service('session').isLoggedIn()) {
                        if (this.runtime.hasConfig('services.user_profile.url')) {
                            this.userProfileClient = new UserProfileService(this.runtime.getConfig('user_profile_url'), {
                                token: this.runtime.service('session').getAuthToken()
                            });
                        } else {
                            throw 'The user profile client url is not defined';
                        }
                    }
                }
            },
            renderLayout: {
                value: function () {
                    this.container.html(this.renderTemplate('layout'));
                    this.places = {
                        title: this.container.find('[data-placeholder="title"]'),
                        alert: this.container.find('[data-placeholder="alert"]'),
                        content: this.container.find('[data-placeholder="content"]')
                    };

                    // Only enable the search form if the user is logged in.
                    if (this.runtime.service('session').isLoggedIn()) {
                        var widget = this;
                        this.container.find('[data-field="search_text"] input').on('keyup', function (e) {
                            if ((e.key === undefined && e.keyCode === 27) || e.key === 'Esc' || e.key === 'Escape') {
                                $(this).val('');
                                widget.setState('searchResults', []);
                                return;
                            }
                            widget.params.searchText = $(this).val();
                            if (widget.params.searchText && widget.params.searchText.length < 3) {
                                widget.refresh().done();
                            } else {
                                Promise.resolve(widget.userProfileClient.filter_users({
                                    filter: widget.params.searchText
                                }))
                                    .then(function (users) {
                                        users.sort(function (a, b) {
                                            var aName = a.realname.toLowerCase();
                                            var bName = b.realname.toLowerCase();
                                            if (aName < bName) {
                                                return -1;
                                            } else if (aName > bName) {
                                                return 1;
                                            } else {
                                                return 0;
                                            }
                                        });
                                        widget.setState('searchResults', users);
                                    })
                                    .catch(function (err) {
                                        widget.renderErrorView(err);
                                    });
                            }
                        });
                    }
                }
            }

        });
        return widget;
    });<commit_msg>replace incorrect user profile service config key with correct one<commit_after>define([
    'jquery',
    'bluebird',
    'kb_userProfile_widget_base',
    'kb/service/client/userProfile'
],
    function ($, Promise, SocialWidget, UserProfileService) {
        "use strict";
        var widget = Object.create(SocialWidget, {
            init: {
                value: function (cfg) {
                    cfg.name = 'UserSearch';
                    cfg.title = 'Find Other Users';
                    this.SocialWidget_init(cfg);

                    return this;
                }
            },
            go: {
                value: function () {
                    this.start();
                    return this;
                }
            },
            setup: {
                value: function () {
                    // User profile service
                    if (this.runtime.service('session').isLoggedIn()) {
                        if (this.runtime.hasConfig('services.user_profile.url')) {
                            this.userProfileClient = new UserProfileService(this.runtime.config('services.user_profile.url'), {
                                token: this.runtime.service('session').getAuthToken()
                            });
                        } else {
                            throw 'The user profile client url is not defined';
                        }
                    }
                }
            },
            renderLayout: {
                value: function () {
                    this.container.html(this.renderTemplate('layout'));
                    this.places = {
                        title: this.container.find('[data-placeholder="title"]'),
                        alert: this.container.find('[data-placeholder="alert"]'),
                        content: this.container.find('[data-placeholder="content"]')
                    };

                    // Only enable the search form if the user is logged in.
                    if (this.runtime.service('session').isLoggedIn()) {
                        var widget = this;
                        this.container.find('[data-field="search_text"] input').on('keyup', function (e) {
                            if ((e.key === undefined && e.keyCode === 27) || e.key === 'Esc' || e.key === 'Escape') {
                                $(this).val('');
                                widget.setState('searchResults', []);
                                return;
                            }
                            widget.params.searchText = $(this).val();
                            if (widget.params.searchText && widget.params.searchText.length < 3) {
                                widget.refresh().done();
                            } else {
                                Promise.resolve(widget.userProfileClient.filter_users({
                                    filter: widget.params.searchText
                                }))
                                    .then(function (users) {
                                        users.sort(function (a, b) {
                                            var aName = a.realname.toLowerCase();
                                            var bName = b.realname.toLowerCase();
                                            if (aName < bName) {
                                                return -1;
                                            } else if (aName > bName) {
                                                return 1;
                                            } else {
                                                return 0;
                                            }
                                        });
                                        widget.setState('searchResults', users);
                                    })
                                    .catch(function (err) {
                                        widget.renderErrorView(err);
                                    });
                            }
                        });
                    }
                }
            }

        });
        return widget;
    });<|endoftext|>


<commit_before>window.customUI = window.customUI || {
  VERSION: '20170724',

  lightOrShadow: function (elem, selector) {
    return elem.shadowRoot ?
        elem.shadowRoot.querySelector(selector) :
        elem.querySelector(selector);
  },

  getElementHierarchy: function (root, hierarchy) {
    if (root === null) return null;
    const elem = hierarchy.shift();
    if (elem) {
      return window.customUI.getElementHierarchy(
        window.customUI.lightOrShadow(root, elem), hierarchy);
    }
    return root;
  },

  getContext: function (elem) {
    if (elem._context === undefined) {
      elem._context = [];
      for (let element = (elem.tagName === 'HA-ENTITIES-CARD' ? elem.domHost : elem); element; element = element.domHost) {
        switch (element.tagName) {
          case 'HA-ENTITIES-CARD':
            if (element.groupEntity) {
              elem._context.push(element.groupEntity.entity_id);
            } else if (element.groupEntity === false && element.states && element.states.length) {
              elem._context.push('group.' + window.hassUtil.computeDomain(element.states[0]));
            }
            break;
          case 'MORE-INFO-GROUP':
            if (element.stateObj) {
              elem._context.push(element.stateObj.entity_id);
            }
            break;
          case 'PARTIAL-CARDS':
            elem._context.push(element.currentView ? element.currentView : 'default_view');
            break;
          // no default
        }
      }
      elem._context.reverse();
    }
    return elem._context;
  },

  maybeChangeObject: function (elem, stateObj, inDialog, allowHidden) {
    if (inDialog) return stateObj;
    const context = window.customUI.getContext(elem);
    if (!context) return stateObj;

    if (!stateObj.attributes.group) {
      return stateObj;
    }
    const attributes = {};
    context.forEach(function (c) {
      if (stateObj.attributes.group[c]) {
        Object.assign(attributes, stateObj.attributes.group[c]);
      }
    });

    if (!Object.keys(attributes).length) return stateObj;

    if (attributes.hidden && allowHidden) {
      return null;
    }

    return {
      entity_id: stateObj.entity_id,
      state: stateObj.state,
      attributes: Object.assign({}, stateObj.attributes, attributes),
      last_changed: stateObj.last_changed,
    };
  },

  fixGroupTitles: function () {
    const haCards = window.customUI.getElementHierarchy(document, [
      'home-assistant',
      'home-assistant-main',
      'partial-cards',
      'ha-cards']);
    if (haCards === null) return;
    const main = window.customUI.lightOrShadow(haCards, '.main');
    const cards = main.querySelectorAll('ha-entities-card');
    cards.forEach(function (card) {
      if (card.groupEntity) {
        const obj = window.customUI.maybeChangeObject(
          card,
          card.groupEntity,
          false /* inDialog */,
          false /* allowHidden */);
        if (obj !== card.groupEntity && obj.attributes.friendly_name) {
          const nameElem = window.customUI.lightOrShadow(card, '.name');
          nameElem.textContent = obj.attributes.friendly_name;
        }
      }
    });
  },

  showVersion: function () {
    if (window.location.pathname !== '/dev-info') return;
    const devInfo = window.customUI.getElementHierarchy(document, [
      'home-assistant',
      'home-assistant-main',
      'partial-panel-resolver',
      'ha-panel-dev-info']);
    if (devInfo === null) return;
    const about = window.customUI.lightOrShadow(devInfo, '.about');
    const secondP = about.querySelectorAll('p')[1];
    const version = document.createElement('p');
    version.textContent = 'Custom UI ' + window.customUI.VERSION;
    about.insertBefore(version, secondP);
  },

  runHooks: function () {
    window.customUI.fixGroupTitles();
    window.customUI.showVersion();
  },

  getName: function () {
    return window.localStorage.getItem('ha-device-name') || '';
  },

  setName: function (name) {
    window.localStorage.setItem('ha-device-name', name || '');
  },
};

window.customUI.runHooks();

if (!window.customUI.runHooksListenerAttached) {
  window.addEventListener('location-changed', window.setTimeout.bind(null, window.customUI.runHooks, 100));
  window.customUI.runHooksListenerAttached = true;
  /* eslint-disable no-console */
  console.log('Loaded CustomUI ' + window.customUI.VERSION);
  /* eslint-enable no-console */
}
<commit_msg>Add support for hiding attributes set by customizer<commit_after>window.customUI = window.customUI || {
  VERSION: '20170724',

  lightOrShadow: function (elem, selector) {
    return elem.shadowRoot ?
        elem.shadowRoot.querySelector(selector) :
        elem.querySelector(selector);
  },

  getElementHierarchy: function (root, hierarchy) {
    if (root === null) return null;
    const elem = hierarchy.shift();
    if (elem) {
      return window.customUI.getElementHierarchy(
        window.customUI.lightOrShadow(root, elem), hierarchy);
    }
    return root;
  },

  getContext: function (elem) {
    if (elem._context === undefined) {
      elem._context = [];
      for (let element = (elem.tagName === 'HA-ENTITIES-CARD' ? elem.domHost : elem); element; element = element.domHost) {
        switch (element.tagName) {
          case 'HA-ENTITIES-CARD':
            if (element.groupEntity) {
              elem._context.push(element.groupEntity.entity_id);
            } else if (element.groupEntity === false && element.states && element.states.length) {
              elem._context.push('group.' + window.hassUtil.computeDomain(element.states[0]));
            }
            break;
          case 'MORE-INFO-GROUP':
            if (element.stateObj) {
              elem._context.push(element.stateObj.entity_id);
            }
            break;
          case 'PARTIAL-CARDS':
            elem._context.push(element.currentView ? element.currentView : 'default_view');
            break;
          // no default
        }
      }
      elem._context.reverse();
    }
    return elem._context;
  },

  maybeChangeObject: function (elem, stateObj, inDialog, allowHidden) {
    if (inDialog) return stateObj;
    const context = window.customUI.getContext(elem);
    if (!context) return stateObj;

    if (!stateObj.attributes.group) {
      return stateObj;
    }
    const attributes = {};
    context.forEach(function (c) {
      if (stateObj.attributes.group[c]) {
        Object.assign(attributes, stateObj.attributes.group[c]);
      }
    });

    if (!Object.keys(attributes).length) return stateObj;

    if (attributes.hidden && allowHidden) {
      return null;
    }

    return {
      entity_id: stateObj.entity_id,
      state: stateObj.state,
      attributes: Object.assign({}, stateObj.attributes, attributes),
      last_changed: stateObj.last_changed,
    };
  },

  fixGroupTitles: function () {
    const haCards = window.customUI.getElementHierarchy(document, [
      'home-assistant',
      'home-assistant-main',
      'partial-cards',
      'ha-cards']);
    if (haCards === null) return;
    const main = window.customUI.lightOrShadow(haCards, '.main');
    const cards = main.querySelectorAll('ha-entities-card');
    cards.forEach(function (card) {
      if (card.groupEntity) {
        const obj = window.customUI.maybeChangeObject(
          card,
          card.groupEntity,
          false /* inDialog */,
          false /* allowHidden */);
        if (obj !== card.groupEntity && obj.attributes.friendly_name) {
          const nameElem = window.customUI.lightOrShadow(card, '.name');
          nameElem.textContent = obj.attributes.friendly_name;
        }
      }
    });
  },

  showVersion: function () {
    if (window.location.pathname !== '/dev-info') return;
    const devInfo = window.customUI.getElementHierarchy(document, [
      'home-assistant',
      'home-assistant-main',
      'partial-panel-resolver',
      'ha-panel-dev-info']);
    if (devInfo === null) return;
    const about = window.customUI.lightOrShadow(devInfo, '.about');
    const secondP = about.querySelectorAll('p')[1];
    const version = document.createElement('p');
    version.textContent = 'Custom UI ' + window.customUI.VERSION;
    about.insertBefore(version, secondP);
  },

  useCustomizer: function () {
    const main = window.customUI.getElementHierarchy(document, [
      'home-assistant',
      'home-assistant-main']);
    const customizer = main.hass.states['customizer.customizer'];
    if (!customizer) return;
    window.hassUtil.LOGIC_STATE_ATTRIBUTES = window.hassUtil.LOGIC_STATE_ATTRIBUTES || [];
    if (customizer.attributes.hide_attributes) {
      Array.prototype.push.apply(
          window.hassUtil.LOGIC_STATE_ATTRIBUTES, customizer.attributes.hide_attributes);
    }
    if (customizer.attributes.hide_customui_attributes) {
      window.hassUtil.LOGIC_STATE_ATTRIBUTES.push(
        'custom_ui_state_card',
        'group',
        'state_card_mode',
        'badges_list',
        'show_last_changed',
        'hide_control',
        'extra_data_template',
        'extra_badge',
        'stretch_slider',
        'slider_theme'
      );
    }
  },

  init: function () {
    window.customUI.runHooks();
    window.customUI.useCustomizer();
    if (!window.customUI.initDone) {
      window.addEventListener('location-changed', window.setTimeout.bind(null, window.customUI.runHooks, 100));
      window.customUI.initDone = true;
      /* eslint-disable no-console */
      console.log('Loaded CustomUI ' + window.customUI.VERSION);
      /* eslint-enable no-console */
    }
  },

  runHooks: function () {
    window.customUI.fixGroupTitles();
    window.customUI.showVersion();
  },

  getName: function () {
    return window.localStorage.getItem('ha-device-name') || '';
  },

  setName: function (name) {
    window.localStorage.setItem('ha-device-name', name || '');
  },
};

window.customUI.init();
<|endoftext|>


<commit_before>var isInScrollBar = function(canvas, context, x, y){
	var height = 400;
	var width = 30;
	var distanceFromTop = 100;

	return x > canvas.width - width && y > distanceFromTop && y < distanceFromTop + height;
}

var getScrollPositionFromScrollBar = function(canvas, context, y, maxScroll){
	var height = 400;
	var width = 20;
	var distanceFromTop = 100;

	return (y - distanceFromTop) * maxScroll / height;
}

var drawScrollBar = function(canvas, context, scrollPosition, maxScroll){
	var height = 400;
	var width = 30;
	var distanceFromTop = 100;

	context.beginPath();
	context.strokeStyle=staveColour;
	context.rect(canvas.width - width, distanceFromTop, width, height);
	context.stroke();

	context.beginPath();
	context.strokeStyle=noteColour;
	context.lineWidth =4;
	context.moveTo(canvas.width - width, scrollPosition * height / maxScroll + distanceFromTop);
	context.lineTo(canvas.width, scrollPosition * height / maxScroll + distanceFromTop);
	context.stroke();
	context.lineWidth = 1;
}

function getCurrentLine(y, lines, maxScroll){
	var height = 400;
	var distanceFromTop = 100;

	for(var i = 0; i < lines.length; i++){
		if(lines[i].y > (y - distanceFromTop) * maxScroll / height )
		return lines[i];
	}
	return false;
}

var drawScrollPreview = function(canvas, context, y, lines, maxScroll){
	var rightMargin = 30;
	var width = 100;
	var height = 100;
	var farLeft = canvas.width - (rightMargin + width) - 15;

	context.beginPath();
	context.moveTo(farLeft, y);
	context.lineTo(farLeft + width + 10, y);
	context.lineTo(farLeft + width, y + 5)
	context.lineTo(farLeft + width, y + height);
	context.lineTo(farLeft, y + height);
	context.lineTo(farLeft, y);
	context.strokeStyle = "black";
	context.lineWidth = 4;
	context.stroke();
	context.fillStyle="white";
	context.fill();
	context.lineWidth = 1;

	var tempCurrentLine = getCurrentLine(y, lines, maxScroll);
	if(tempCurrentLine != false){
		context.font = "bold 12px Arial";
		context.fillStyle="black";
		context.fillText(tempCurrentLine.barNumber + "", farLeft + 5, y + 15 );
	}

	 drawSingleBar(canvas, context, farLeft + 10, y + 30, tempCurrentLine.instruments[0].bars[0], width - 20, height - 60);
}
<commit_msg>Update scrollbar.js<commit_after>var isInScrollBar = function(canvas, context, x, y){
	var height = 400;
	var width = 30;
	var distanceFromTop = 100;

	return x > canvas.width - width && y > distanceFromTop && y < distanceFromTop + height;
}

var getScrollPositionFromScrollBar = function(canvas, context, y, maxScroll){
	var height = 400;
	var width = 20;
	var distanceFromTop = 100;

	return (y - distanceFromTop) * maxScroll / height;
}

var drawScrollBar = function(canvas, context, scrollPosition, maxScroll){
	var height = 400;
	var width = 30;
	var distanceFromTop = 100;

	context.beginPath();
	context.strokeStyle=staveColour;
	context.rect(canvas.width - width, distanceFromTop, width, height);
	context.stroke();

	context.beginPath();
	context.strokeStyle=noteColour;
	context.lineWidth =4;
	context.moveTo(canvas.width - width, scrollPosition * height / maxScroll + distanceFromTop);
	context.lineTo(canvas.width, scrollPosition * height / maxScroll + distanceFromTop);
	context.stroke();
	context.lineWidth = 1;
}

function getCurrentLine(y, lines, maxScroll){
	var height = 400;
	var distanceFromTop = 100;

	for(var i = 0; i < lines.length; i++){
		if(lines[i].y > (y - distanceFromTop) * maxScroll / height )
		return lines[i];
	}
	return false;
}

var drawScrollPreview = function(canvas, context, y, lines, maxScroll){
	var rightMargin = 30;
	var width = 200;
	var height = 100;
	var farLeft = canvas.width - (rightMargin + width) - 15;

	context.beginPath();
	context.moveTo(farLeft, y);
	context.lineTo(farLeft + width + 10, y);
	context.lineTo(farLeft + width, y + 5)
	context.lineTo(farLeft + width, y + height);
	context.lineTo(farLeft, y + height);
	context.lineTo(farLeft, y);
	context.strokeStyle = "black";
	context.lineWidth = 4;
	context.stroke();
	context.fillStyle="white";
	context.fill();
	context.lineWidth = 1;

	var tempCurrentLine = getCurrentLine(y, lines, maxScroll);
	if(tempCurrentLine != false){
		context.font = "bold 12px Arial";
		context.fillStyle="black";
		context.fillText(tempCurrentLine.barNumber + "", farLeft + 5, y + 15 );
	}

	 drawSingleBar(canvas, context, farLeft + 10, y + 30, tempCurrentLine.instruments[0].bars[0], width - 20, height - 60);
}
<|endoftext|>