<commit_before>// ==========================================================================
//                          DG.ImageUtilities
//
//  Author:   Jonathan Sandoe
//
//  Copyright (c) 2017 by The Concord Consortium, Inc. All rights reserved.
//
//  Licensed under the Apache License, Version 2.0 (the "License");
//  you may not use this file except in compliance with the License.
//  You may obtain a copy of the License at
//
//    http://www.apache.org/licenses/LICENSE-2.0
//
//  Unless required by applicable law or agreed to in writing, software
//  distributed under the License is distributed on an "AS IS" BASIS,
//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//  See the License for the specific language governing permissions and
//  limitations under the License.
// ==========================================================================

/* global Promise */
DG.ImageUtilities = (function () {
  return {

    /**
     * Converts an SVG scene into a png image.
     * @param rootEl {Element} a DOM element. Either HTML or SVG. This routine
     *   will extract SVG elements from this object and place them in the final
     *   image. HTML DOM element will not be visible.
     * @param width {number}
     * @param height {number}
     * @param asDataURL {boolean} true - image returned as data URI, otherwise as blob
     * @return {Promise} The promise of an image.
     */
    captureSVGElementsToImage: function (rootEl, width, height, asDataURL) {

      function getCSSText() {
        var text = [], ix, jx;
        for (ix = 0; ix < document.styleSheets.length; ix += 1) {
          try {
            var styleSheet = document.styleSheets[ix];
            if (styleSheet) {
              var rules = styleSheet.rules || styleSheet.cssRules || [];
              for (jx = 0; jx < rules.length; jx += 1) {
                var rule = rules[jx];
                text.push(rule.cssText);
              }
            }
          } catch (ex) {
            DG.logWarn('Exception retrieving stylesheet: ' + ex);
          }
        }
        return text.join('\n');
      }

      function makeDataURLFromSVGElement(svgEl) {
        var svgClone = $(svgEl).clone();
        var css = $('<style>').text(getCSSText());
        svgClone.prepend(css);
        var svgData = new XMLSerializer().serializeToString(svgClone[0]);
        // Raphael overspecifies the url for the gradient. It prepends the
        // windows.location.href. This causes problems with the data url,
        // because we are no longer in this namespace. So, we remove.
        svgData = svgData.replace(
            new RegExp('url\\(\'[^#]*#', 'g'), 'url(\'#');
        // The use of unescape and encodeURIComponent are part of a well-
        // known hack work around btoa's handling of unicode characters.
        // see, eg:
        // http://ecmanaut.blogspot.com/2006/07/encoding-decoding-utf8-in-javascript.html
        return "data:image/svg+xml;base64,"
            + window.btoa(window.unescape(window.encodeURIComponent(svgData)));
      }

      /**
       * Create a canvas element with a white background. Not yet appended to
       * the DOM.
       * @param {number} width
       * @param {number} height
       * @returns {Canvas}
       */
      function makeCanvasEl(width, height) {
        var canvas = $("<canvas>").prop({width: width, height: height})[0];
        var ctx = canvas.getContext("2d");
        ctx.fillStyle = 'white';
        ctx.fillRect(0, 0, width, height);
        return canvas;
      }

      /**
       * Add an image to the canvas at the specified location.
       * @param {Canvas} canvas DOM Element
       * @param {img} image DOM Element
       * @param {number} x
       * @param {number} y
       * @param {number} width
       * @param {number} height
       */
      function addImageToCanvas(canvas, image, x, y, width, height) {
        var ctx = canvas.getContext("2d");
        ctx.drawImage(image, x, y, width, height);
      }

      /**
       * Convert a canvas object to a blob.
       * @param canvas
       * @returns {*}
       */
      function makeCanvasBlob(canvas) {
        var canvasDataURL = canvas.toDataURL("image/png");
        var canvasData = atob(canvasDataURL.substring("data:image/png;base64,".length));
        var canvasAsArray = new Uint8Array(canvasData.length);  /* global Uint8Array */

        for (var i = 0, len = canvasData.length; i < len; ++i) {
          canvasAsArray[i] = canvasData.charCodeAt(i);
        }

        return new Blob([canvasAsArray.buffer], {type: "image/png"});
      }

      /**
       * @returns a promise of an image.
       * @param dataURL
       */
      function makeSVGImage(dataURL) {
        return new Promise(function (resolve, reject) {
              try {
                var img = $('<img/>')[0];
                img.onload = function () {
                  resolve(img);
                };
                img.src = dataURL;
              } catch (ex) {
                reject(ex);
              }
            }
        );
      }

      //
      // ######## convertImage begin ########
      //
      // find all svg elements that are not children of hidden divs
      var elements = $(rootEl).find('img,div:not(.sc-hidden)>svg');
      var canvas = makeCanvasEl(width, height);
      var jobList = [];
      var jobIx = 0;

      function perform(job) {
        var elType = job && job.el && job.el.nodeName.toLowerCase();
        if (!job) {
          // we have drawn all the elements resolve the promise with a data URL or blob
          try {
            return Promise.resolve(asDataURL ? canvas.toDataURL("image/png") : makeCanvasBlob(canvas));
          } catch (e) {
            return Promise.reject(e);
          }
        }
        if (elType === 'div') {
          // get the background color from the base div
          drawRectToCanvas(canvas,$(job.el).css('background-color'), job.l, job.t, job.w, job.h);
          return perform(jobList[jobIx++]);
        } else if (elType === 'svg') {
          // add the svg as an image to the canvas
          return makeSVGImage(makeDataURLFromSVGElement(job.el)).then(function (img) {
              addImageToCanvas(canvas, img, job.l, job.t, job.w, job.h);
              return perform(jobList[jobIx++]);
            },
            function (error) {
              return Promise.reject(error);
            }
          );
        } else if (elType === 'img') {
          // add an img to the canvas only if it has the crossorigin attribute.
          // otherwise we will taint the canvas
          if (!SC.none(job.el.getAttribute('crossorigin'))) {
            addImageToCanvas(canvas, job.el, job.l, job.t, job.w, job.h);
          }
          return perform(jobList[jobIx++]);
        } else {
          return perform(jobList[jobIx++]);
        }

      }
      // for each svg we calculate its geometry, then add it to the canvas
      // through a promise.
      // Note: This implementation starts a number of asynchronous operations,
      // one for each SVG in the hierarchy. If they overlap and are handled out
      // of order, it is possible that they may be written to the canvas in the
      // wrong order.
      try {
        if (elements) {
          elements.forEach(function (element) {
            var width = element.offsetWidth || (element.width.baseVal?element.width.baseVal.value:0);
            var height = element.offsetHeight || (element.height.baseVal?element.height.baseVal.value:0);
            var left = 0;
            var top = 0;
            var rect = element.getBoundingClientRect();
            var rootRect = rootEl.getBoundingClientRect();
            if (element.nodeName.toLowerCase() === "img") {
              jobList.push({
                el:element,
                w: rect.width,
                h: rect.height,
                l: rect.left - rootRect.left,
                t: rect.top - rootRect.top
              });
            } else {
              $(element).add($(element).parentsUntil(rootEl)).each(function () {
                left += this.offsetLeft || 0;
                top += this.offsetTop || 0;
              });
              jobList.push({el: element, w: width, h: height, l: left, t: top});
            }
          });

          return perform (jobList[jobIx++]);
        }
      } catch (ex) {
        DG.logError('saving image: ' + ex);
      }
      return null;

    }
  };
}());<commit_msg>[#158544618] Fix bug: graph image capture should capture background color<commit_after>// ==========================================================================
//                          DG.ImageUtilities
//
//  Author:   Jonathan Sandoe
//
//  Copyright (c) 2017 by The Concord Consortium, Inc. All rights reserved.
//
//  Licensed under the Apache License, Version 2.0 (the "License");
//  you may not use this file except in compliance with the License.
//  You may obtain a copy of the License at
//
//    http://www.apache.org/licenses/LICENSE-2.0
//
//  Unless required by applicable law or agreed to in writing, software
//  distributed under the License is distributed on an "AS IS" BASIS,
//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//  See the License for the specific language governing permissions and
//  limitations under the License.
// ==========================================================================

/* global Promise */
DG.ImageUtilities = (function () {
  return {

    /**
     * Converts an SVG scene into a png image.
     * @param rootEl {Element} a DOM element. Either HTML or SVG. This routine
     *   will extract SVG elements from this object and place them in the final
     *   image. HTML DOM element will not be visible.
     * @param width {number}
     * @param height {number}
     * @param asDataURL {boolean} true - image returned as data URI, otherwise as blob
     * @return {Promise} The promise of an image.
     */
    captureSVGElementsToImage: function (rootEl, width, height, asDataURL) {

      function getCSSText() {
        var text = [], ix, jx;
        for (ix = 0; ix < document.styleSheets.length; ix += 1) {
          try {
            var styleSheet = document.styleSheets[ix];
            if (styleSheet) {
              var rules = styleSheet.rules || styleSheet.cssRules || [];
              for (jx = 0; jx < rules.length; jx += 1) {
                var rule = rules[jx];
                text.push(rule.cssText);
              }
            }
          } catch (ex) {
            DG.logWarn('Exception retrieving stylesheet: ' + ex);
          }
        }
        return text.join('\n');
      }

      function makeDataURLFromSVGElement(svgEl) {
        var svgClone = $(svgEl).clone();
        var css = $('<style>').text(getCSSText());
        svgClone.prepend(css);
        var svgData = new XMLSerializer().serializeToString(svgClone[0]);
        // Raphael overspecifies the url for the gradient. It prepends the
        // windows.location.href. This causes problems with the data url,
        // because we are no longer in this namespace. So, we remove.
        svgData = svgData.replace(
            new RegExp('url\\(\'[^#]*#', 'g'), 'url(\'#');
        // The use of unescape and encodeURIComponent are part of a well-
        // known hack work around btoa's handling of unicode characters.
        // see, eg:
        // http://ecmanaut.blogspot.com/2006/07/encoding-decoding-utf8-in-javascript.html
        return "data:image/svg+xml;base64,"
            + window.btoa(window.unescape(window.encodeURIComponent(svgData)));
      }

      /**
       * Create a canvas element with a white background. Not yet appended to
       * the DOM.
       * @param {number} width
       * @param {number} height
       * @returns {Canvas}
       */
      function makeCanvasEl(width, height) {
        var canvas = $("<canvas>").prop({width: width, height: height})[0];
        var ctx = canvas.getContext("2d");
        ctx.fillStyle = 'white';
        ctx.fillRect(0, 0, width, height);
        return canvas;
      }

      /**
       * Add an image to the canvas at the specified location.
       * @param {Canvas} canvas DOM Element
       * @param {img} image DOM Element
       * @param {number} x
       * @param {number} y
       * @param {number} width
       * @param {number} height
       */
      function addImageToCanvas(canvas, image, x, y, width, height) {
        var ctx = canvas.getContext("2d");
        ctx.drawImage(image, x, y, width, height);
      }

      function drawRectToCanvas(canvas, color, x, y, width, height) {
        var ctx = canvas.getContext("2d");
        ctx.fillStyle = color;
        ctx.fillRect(x, y, width, height);
      }
      /**
       * Convert a canvas object to a blob.
       * @param canvas
       * @returns {*}
       */
      function makeCanvasBlob(canvas) {
        var canvasDataURL = canvas.toDataURL("image/png");
        var canvasData = atob(canvasDataURL.substring("data:image/png;base64,".length));
        var canvasAsArray = new Uint8Array(canvasData.length);  /* global Uint8Array */

        for (var i = 0, len = canvasData.length; i < len; ++i) {
          canvasAsArray[i] = canvasData.charCodeAt(i);
        }

        return new Blob([canvasAsArray.buffer], {type: "image/png"});
      }

      /**
       * @returns a promise of an image.
       * @param dataURL
       */
      function makeSVGImage(dataURL) {
        return new Promise(function (resolve, reject) {
              try {
                var img = $('<img/>')[0];
                img.onload = function () {
                  resolve(img);
                };
                img.src = dataURL;
              } catch (ex) {
                reject(ex);
              }
            }
        );
      }

      //
      // ######## convertImage begin ########
      //
      // find all svg elements that are not children of hidden divs
      var elements = $(rootEl).find('div.dg-plot-view,img,div:not(.sc-hidden)>svg');
      var canvas = makeCanvasEl(width, height);
      var jobList = [];
      var jobIx = 0;

      function perform(job) {
        var elType = job && job.el && job.el.nodeName.toLowerCase();
        if (!job) {
          // we have drawn all the elements resolve the promise with a data URL or blob
          try {
            return Promise.resolve(asDataURL ? canvas.toDataURL("image/png") : makeCanvasBlob(canvas));
          } catch (e) {
            return Promise.reject(e);
          }
        }
        if (elType === 'div') {
          // get the background color from the base div
          drawRectToCanvas(canvas,$(job.el).css('background-color'), job.l, job.t, job.w, job.h);
          return perform(jobList[jobIx++]);
        } else if (elType === 'svg') {
          // add the svg as an image to the canvas
          return makeSVGImage(makeDataURLFromSVGElement(job.el)).then(function (img) {
              addImageToCanvas(canvas, img, job.l, job.t, job.w, job.h);
              return perform(jobList[jobIx++]);
            },
            function (error) {
              return Promise.reject(error);
            }
          );
        } else if (elType === 'img') {
          // add an img to the canvas only if it has the crossorigin attribute.
          // otherwise we will taint the canvas
          if (!SC.none(job.el.getAttribute('crossorigin'))) {
            addImageToCanvas(canvas, job.el, job.l, job.t, job.w, job.h);
          }
          return perform(jobList[jobIx++]);
        } else {
          return perform(jobList[jobIx++]);
        }

      }
      // for each svg we calculate its geometry, then add it to the canvas
      // through a promise.
      // Note: This implementation starts a number of asynchronous operations,
      // one for each SVG in the hierarchy. If they overlap and are handled out
      // of order, it is possible that they may be written to the canvas in the
      // wrong order.
      try {
        if (elements) {
          elements.forEach(function (element) {
            var width = element.offsetWidth || (element.width.baseVal?element.width.baseVal.value:0);
            var height = element.offsetHeight || (element.height.baseVal?element.height.baseVal.value:0);
            var left = 0;
            var top = 0;
            var rect = element.getBoundingClientRect();
            var rootRect = rootEl.getBoundingClientRect();
            if (element.nodeName.toLowerCase() === "img") {
              jobList.push({
                el:element,
                w: rect.width,
                h: rect.height,
                l: rect.left - rootRect.left,
                t: rect.top - rootRect.top
              });
            } else {
              $(element).add($(element).parentsUntil(rootEl)).each(function () {
                left += this.offsetLeft || 0;
                top += this.offsetTop || 0;
              });
              jobList.push({el: element, w: width, h: height, l: left, t: top});
            }
          });

          return perform (jobList[jobIx++]);
        }
      } catch (ex) {
        DG.logError('saving image: ' + ex);
      }
      return null;

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


<commit_before>define(['marionette',
    'collections/reprocessings',
    'collections/reprocessingparameters',
    'collections/reprocessingimagesweeps',
    'views/table',
    'utils/table'],
    function(Marionette,
        Reprocessings, ReprocessingParamters, ReprocessingImageSweeps,
        TableView, table) {

        var StatusCell = Backgrid.Cell.extend({
            render: function() {
                var icons = {
                    2: '',
                    null: '',
                    1: '',
                    0: '',
                }

                if (this.model.get('STATUS') in icons) this.$el.append(icons[this.model.get('STATUS')])

                return this
            }
        })

        var ArgsCell = Backgrid.Cell.extend({
            initialize: function(options) {
                ArgsCell.__super__.initialize.call(this, options)

                this.params = new ReprocessingParamters()
                console.log('col', this.column, 'opts', options)
                this.listenTo(this.column.get('params'), 'sync reset', this.syncParams)
                this.syncParams()

                this.sweeps = new ReprocessingImageSweeps()
                this.listenTo(this.column.get('sweeps'), 'sync reset', this.syncSweeps)
                this.syncSweeps()
            },

            syncParams: function() {
                var sw = this.column.get('params').where({ PROCESSINGJOBID: this.model.get('PROCESSINGJOBID') })
                this.params.reset(sw)
            },

            syncSweeps: function() {
                var sw = this.column.get('sweeps').where({ PROCESSINGJOBID: this.model.get('PROCESSINGJOBID') })
                this.sweeps.reset(sw)
            },

            render: function() {
                var columns = [
                   { name: 'PARAMETERKEY', label: 'Key', cell: 'string', editable: false },
                   { name: 'PARAMETERVALUE', label: 'Value', cell: 'string', editable: false },
                ]

                var ptable = new TableView({
                    collection: this.params,
                    columns: columns,
                    loading: false,
                    pages: false,
                    backgrid: { emptyText: 'No parameters found' },
                })

                this.$el.html(ptable.render().$el)

                var columns = [
                   { label: 'Files', cell: table.TemplateCell, editable: false, template: '<a href="/dc/visit/<%-VISIT%>/id/<%-DATACOLLECTIONID%>"><%-IMAGEDIRECTORY%><%-IMAGEPREFIX%>_<%-DATACOLLECTIONNUMBER%>' },
                   { label: 'Image #', cell: table.TemplateCell, editable: false, template: '<%-STARTIMAGE%> - <%-ENDIMAGE%>' },
                ]

                var stable = new TableView({
                    collection: this.sweeps,
                    columns: columns,
                    loading: false,
                    pages: false,
                    backgrid: { emptyText: 'No image sweeps found' },
                })

                this.$el.append(stable.render().$el)

                return this
            },

        })

        var ResultsCell = Backgrid.Cell.extend({
            render: function() {
                if (this.model.get('RLOW')) {
                    var columns = [
                       { name: 'KEY', label: '', cell: 'string', editable: false },
                       { name: 'VALUE', label: 'Overall', cell: 'string', editable: false },
                    ]

                    var results = []
                    _.each({
                        'Low Resolution': 'RLOW',
                        'High Resolution': 'RHIGH',
                        'Total Observations': 'NTOBS',
                        'Multiplicity': 'MULTIPLICITY',
                        'Rmerge': 'RMERGE',
                        'I/sig(I)': 'ISIGI',
                    }, function(v, k) {
                        results.push({
                            KEY: k,
                            VALUE: this.model.get(v)
                        })
                    }, this)

                    this.results = new Backbone.Collection(results)
                    console.log('results', this.results)
                    var rtable = new TableView({
                        collection: this.results,
                        columns: columns,
                        loading: false,
                        pages: false,
                        backgrid: { emptyText: 'No results found' },
                    })

                    this.$el.html(rtable.render().$el)
                }
                return this
            },
        })

        return Marionette.LayoutView.extend({
            template: _.template('<% if (!EMBED) { %>
Reprocessing

Here you can find all reprocessing jobs and their statuses
<% } %>

'),
            className: 'content',

            regions: {
                rwrap: '.wrapper',
                rflt: '.filter',
            },

            templateHelpers: function() {
                return {
                    EMBED: this.getOption('embed')
                }
            },

            initialize: function(options) {
                if (!options.collection) {
                    this.collection = new Reprocessings()
                    this.collection.queryParams.visit = options.visit
                    this.collection.fetch()
                }

                this.parameters = new ReprocessingParamters()
                this.parameters.queryParams.visit = options.visit
                this.parameters.state.pageSize = 9999

                this.sweeps = new ReprocessingImageSweeps()
                this.sweeps.queryParams.visit = options.visit
                this.sweeps.state.pageSize = 9999

                this.listenTo(this.collection, 'sync', this.refreshArgs)
            },

            refreshArgs: function() {
                this.sweeps.fetch()
                this.parameters.fetch()
            },

            onRender: function() {
                var columns = [
                    { label: 'Status', cell: StatusCell, editable: false },
                    { label: 'Sample', cell: table.TemplateCell, editable: false, template: '<a href="/samples/sid/<%-BLSAMPLEID%>"><%-SAMPLE%> - <a href="/proteins/pid/<%-PROTEINID%>"><%-PROTEIN%>' },
                    { label: 'DC', cell: table.TemplateCell, editable: false, template: '<a href="/dc/visit/<%-VISIT%>/id/<%-DATACOLLECTIONID%>"><%-IMAGEPREFIX%>' },
                    { name: 'DISPLAYNAME', label: 'Process', cell: 'string', editable: false },
                    { name: 'COMMENTS', label: 'Comments', cell: 'string', editable: false },
                    { name: 'LASTUPDATETIMESTAMP', label: 'Last Updated', cell: 'string', editable: false },
                    { name: 'PROCESSINGMESSAGE', label: 'Last Message', cell: 'string', editable: false },
                    { label: 'Arguments', cell: ArgsCell, editable: false, sweeps: this.sweeps, params: this.parameters },
                ]

                if (this.getOption('results')) columns.push(
                    { label: 'Results', cell: ResultsCell, editable: false }
                )

                this.rwrap.show(new TableView({
                    columns: columns,
                    collection: this.collection,
                    backgrid: { emptyText: 'No reprocessings found' },
                }))
            },

            onDestroy: function() {
                this.collection.stop()
            },

        })

})
<commit_msg>add completeness to reproc overview<commit_after>define(['marionette',
    'collections/reprocessings',
    'collections/reprocessingparameters',
    'collections/reprocessingimagesweeps',
    'views/table',
    'utils/table'],
    function(Marionette,
        Reprocessings, ReprocessingParamters, ReprocessingImageSweeps,
        TableView, table) {

        var StatusCell = Backgrid.Cell.extend({
            render: function() {
                var icons = {
                    2: '',
                    null: '',
                    1: '',
                    0: '',
                }

                if (this.model.get('STATUS') in icons) this.$el.append(icons[this.model.get('STATUS')])

                return this
            }
        })

        var ArgsCell = Backgrid.Cell.extend({
            initialize: function(options) {
                ArgsCell.__super__.initialize.call(this, options)

                this.params = new ReprocessingParamters()
                console.log('col', this.column, 'opts', options)
                this.listenTo(this.column.get('params'), 'sync reset', this.syncParams)
                this.syncParams()

                this.sweeps = new ReprocessingImageSweeps()
                this.listenTo(this.column.get('sweeps'), 'sync reset', this.syncSweeps)
                this.syncSweeps()
            },

            syncParams: function() {
                var sw = this.column.get('params').where({ PROCESSINGJOBID: this.model.get('PROCESSINGJOBID') })
                this.params.reset(sw)
            },

            syncSweeps: function() {
                var sw = this.column.get('sweeps').where({ PROCESSINGJOBID: this.model.get('PROCESSINGJOBID') })
                this.sweeps.reset(sw)
            },

            render: function() {
                var columns = [
                   { name: 'PARAMETERKEY', label: 'Key', cell: 'string', editable: false },
                   { name: 'PARAMETERVALUE', label: 'Value', cell: 'string', editable: false },
                ]

                var ptable = new TableView({
                    collection: this.params,
                    columns: columns,
                    loading: false,
                    pages: false,
                    backgrid: { emptyText: 'No parameters found' },
                })

                this.$el.html(ptable.render().$el)

                var columns = [
                   { label: 'Files', cell: table.TemplateCell, editable: false, template: '<a href="/dc/visit/<%-VISIT%>/id/<%-DATACOLLECTIONID%>"><%-IMAGEDIRECTORY%><%-IMAGEPREFIX%>_<%-DATACOLLECTIONNUMBER%>' },
                   { label: 'Image #', cell: table.TemplateCell, editable: false, template: '<%-STARTIMAGE%> - <%-ENDIMAGE%>' },
                ]

                var stable = new TableView({
                    collection: this.sweeps,
                    columns: columns,
                    loading: false,
                    pages: false,
                    backgrid: { emptyText: 'No image sweeps found' },
                })

                this.$el.append(stable.render().$el)

                return this
            },

        })

        var ResultsCell = Backgrid.Cell.extend({
            render: function() {
                if (this.model.get('RLOW')) {
                    var columns = [
                       { name: 'KEY', label: '', cell: 'string', editable: false },
                       { name: 'VALUE', label: 'Overall', cell: 'string', editable: false },
                    ]

                    var results = []
                    _.each({
                        'Low Resolution': 'RLOW',
                        'High Resolution': 'RHIGH',
                        'Total Observations': 'NTOBS',
                        'Multiplicity': 'MULTIPLICITY',
                        'Rmerge': 'RMERGE',
                        'I/sig(I)': 'ISIGI',
                        'Completeness': 'COMPLETENESS',
                    }, function(v, k) {
                        results.push({
                            KEY: k,
                            VALUE: this.model.get(v)
                        })
                    }, this)

                    this.results = new Backbone.Collection(results)
                    console.log('results', this.results)
                    var rtable = new TableView({
                        collection: this.results,
                        columns: columns,
                        loading: false,
                        pages: false,
                        backgrid: { emptyText: 'No results found' },
                    })

                    this.$el.html(rtable.render().$el)
                }
                return this
            },
        })

        return Marionette.LayoutView.extend({
            template: _.template('<% if (!EMBED) { %>
Reprocessing

Here you can find all reprocessing jobs and their statuses
<% } %>

'),
            className: 'content',

            regions: {
                rwrap: '.wrapper',
                rflt: '.filter',
            },

            templateHelpers: function() {
                return {
                    EMBED: this.getOption('embed')
                }
            },

            initialize: function(options) {
                if (!options.collection) {
                    this.collection = new Reprocessings()
                    this.collection.queryParams.visit = options.visit
                    this.collection.fetch()
                }

                this.parameters = new ReprocessingParamters()
                this.parameters.queryParams.visit = options.visit
                this.parameters.state.pageSize = 9999

                this.sweeps = new ReprocessingImageSweeps()
                this.sweeps.queryParams.visit = options.visit
                this.sweeps.state.pageSize = 9999

                this.listenTo(this.collection, 'sync', this.refreshArgs)
            },

            refreshArgs: function() {
                this.sweeps.fetch()
                this.parameters.fetch()
            },

            onRender: function() {
                var columns = [
                    { label: 'Status', cell: StatusCell, editable: false },
                    { label: 'Sample', cell: table.TemplateCell, editable: false, template: '<a href="/samples/sid/<%-BLSAMPLEID%>"><%-SAMPLE%> - <a href="/proteins/pid/<%-PROTEINID%>"><%-PROTEIN%>' },
                    { label: 'DC', cell: table.TemplateCell, editable: false, template: '<a href="/dc/visit/<%-VISIT%>/id/<%-DATACOLLECTIONID%>"><%-IMAGEPREFIX%>' },
                    { name: 'DISPLAYNAME', label: 'Process', cell: 'string', editable: false },
                    { name: 'COMMENTS', label: 'Comments', cell: 'string', editable: false },
                    { name: 'LASTUPDATETIMESTAMP', label: 'Last Updated', cell: 'string', editable: false },
                    { name: 'PROCESSINGMESSAGE', label: 'Last Message', cell: 'string', editable: false },
                    { label: 'Arguments', cell: ArgsCell, editable: false, sweeps: this.sweeps, params: this.parameters },
                ]

                if (this.getOption('results')) columns.push(
                    { label: 'Results', cell: ResultsCell, editable: false }
                )

                this.rwrap.show(new TableView({
                    columns: columns,
                    collection: this.collection,
                    backgrid: { emptyText: 'No reprocessings found' },
                }))
            },

            onDestroy: function() {
                this.collection.stop()
            },

        })

})
<|endoftext|>


<commit_before>/**
 * Compatibility check functions.
 *
 * Site Kit by Google, Copyright 2021 Google LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import API from 'googlesitekit-api';
import { CORE_SITE } from '../../../googlesitekit/datastore/site/constants';
import {
	AMP_PROJECT_TEST_URL,
	ERROR_AMP_CDN_RESTRICTED,
	ERROR_API_UNAVAILABLE,
	ERROR_FETCH_FAIL,
	ERROR_GOOGLE_API_CONNECTION_FAIL,
	ERROR_INVALID_HOSTNAME,
	ERROR_TOKEN_MISMATCH,
	ERROR_WP_PRE_V5,
} from './constants';

const isIP = /^(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)(?:\\.(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)){3}$/;

/**
 * Converts IP Address String to number.
 *
 * @since n.e.x.t
 *
 * @param {string} ip The IP Address string.
 * @return {number} The IP Address converted to number.
 *
 */
const ipNumber = ( ip ) =>
	// eslint-disable-next-line no-bitwise
	( +ip[ 1 ] << 24 ) + ( +ip[ 2 ] << 16 ) + ( +ip[ 3 ] << 8 ) + +ip[ 4 ];

/**
 * Gets IP Address Mask Mask from Mask Size.
 *
 * @since n.e.x.t
 *
 * @param {number} size Mask Size.
 * @return {number} The IP Address Mask.
 *
 */
// eslint-disable-next-line no-bitwise
const ipMask = ( size ) => -1 << ( 32 - size );

/**
 * Checks if a given ip is within a Subnet Range.
 *
 * @since n.e.x.t
 *
 * @param {string} ip     The IP Address.
 * @param {string} subnet The Subnet Address.
 * @param {number} mask   The Mask Size.
 * @return {boolean} Whether the ip is within the subnet range.
 */
// eslint-disable-next-line no-bitwise
const isIpInRange = ( ip, subnet, mask ) =>
	// eslint-disable-next-line no-bitwise
	( ipNumber( ip ) & ipMask( mask ) ) === ipNumber( subnet );

const invalidIpRanges = [
	{ subnet: '10.0.0.0', mask: 8 },
	{ subnet: '127.0.0.0', mask: 8 },
	{ subnet: '172.16.0.0', mask: 12 },
	{ subnet: '192.168.0.0', mask: 16 },
];
// Check for a known non-public/reserved domain.
export const checkHostname = async () => {
	const { hostname, port } = global.location;

	if ( port ) {
		throw ERROR_INVALID_HOSTNAME;
	}

	if ( isIP.test( hostname ) ) {
		for ( const { mask, subnet } of invalidIpRanges ) {
			if ( isIpInRange( hostname, subnet, mask ) ) {
				throw ERROR_INVALID_HOSTNAME;
			}
		}
	} else if ( ! hostname.includes( '.' ) ) {
		throw ERROR_INVALID_HOSTNAME;
	}
};
// Check for a Site Kit specific meta tag on the page to test for aggressive caching.
export const registryCheckSetupTag = ( registry ) => async () => {
	const setupTag = await registry.dispatch( CORE_SITE ).checkForSetupTag();
	if ( setupTag.error ) {
		throw ERROR_TOKEN_MISMATCH;
	}
};
// Check that server can connect to Google's APIs via the core/site/data/health-checks endpoint.
export const checkHealthChecks = async () => {
	const response = await API.get(
		'core',
		'site',
		'health-checks',
		undefined,
		{
			useCache: false,
		}
	).catch( ( error ) => {
		if ( error.code === 'fetch_error' ) {
			throw ERROR_FETCH_FAIL;
		}
		throw ERROR_API_UNAVAILABLE;
	} );

	if ( ! response?.checks?.googleAPI?.pass ) {
		throw ERROR_GOOGLE_API_CONNECTION_FAIL;
	}
};
// Check that client can connect to AMP Project.
export const checkAMPConnectivity = async () => {
	const response = await fetch( AMP_PROJECT_TEST_URL ).catch( () => {
		throw ERROR_AMP_CDN_RESTRICTED;
	} );

	if ( ! response.ok ) {
		throw ERROR_AMP_CDN_RESTRICTED;
	}
};
// Check that the current version of WordPress is 5.0+.
export const checkWPVersion = async () => {
	const { wpVersion } = global._googlesitekitBaseData || {};
	// Throw only if we can get the current version, otherwise ignore it.
	if ( wpVersion && wpVersion.major < 5 ) {
		throw ERROR_WP_PRE_V5;
	}
};
<commit_msg>Add Back Previous Logic.<commit_after>/**
 * Compatibility check functions.
 *
 * Site Kit by Google, Copyright 2021 Google LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import API from 'googlesitekit-api';
import { CORE_SITE } from '../../../googlesitekit/datastore/site/constants';
import {
	AMP_PROJECT_TEST_URL,
	ERROR_AMP_CDN_RESTRICTED,
	ERROR_API_UNAVAILABLE,
	ERROR_FETCH_FAIL,
	ERROR_GOOGLE_API_CONNECTION_FAIL,
	ERROR_INVALID_HOSTNAME,
	ERROR_TOKEN_MISMATCH,
	ERROR_WP_PRE_V5,
} from './constants';

const isIP = /^(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)(?:\\.(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)){3}$/;

/**
 * Converts IP Address String to number.
 *
 * @since n.e.x.t
 *
 * @param {string} ip The IP Address string.
 * @return {number} The IP Address converted to number.
 *
 */
const ipNumber = ( ip ) =>
	// eslint-disable-next-line no-bitwise
	( +ip[ 1 ] << 24 ) + ( +ip[ 2 ] << 16 ) + ( +ip[ 3 ] << 8 ) + +ip[ 4 ];

/**
 * Gets IP Address Mask Mask from Mask Size.
 *
 * @since n.e.x.t
 *
 * @param {number} size Mask Size.
 * @return {number} The IP Address Mask.
 *
 */
// eslint-disable-next-line no-bitwise
const ipMask = ( size ) => -1 << ( 32 - size );

/**
 * Checks if a given ip is within a Subnet Range.
 *
 * @since n.e.x.t
 *
 * @param {string} ip     The IP Address.
 * @param {string} subnet The Subnet Address.
 * @param {number} mask   The Mask Size.
 * @return {boolean} Whether the ip is within the subnet range.
 */
// eslint-disable-next-line no-bitwise
const isIpInRange = ( ip, subnet, mask ) =>
	// eslint-disable-next-line no-bitwise
	( ipNumber( ip ) & ipMask( mask ) ) === ipNumber( subnet );

const invalidIpRanges = [
	{ subnet: '10.0.0.0', mask: 8 },
	{ subnet: '127.0.0.0', mask: 8 },
	{ subnet: '172.16.0.0', mask: 12 },
	{ subnet: '192.168.0.0', mask: 16 },
];
// Check for a known non-public/reserved domain.
export const checkHostname = async () => {
	const { hostname, port } = global.location;

	if ( port ) {
		throw ERROR_INVALID_HOSTNAME;
	}

	if ( isIP.test( hostname ) ) {
		for ( const { mask, subnet } of invalidIpRanges ) {
			if ( isIpInRange( hostname, subnet, mask ) ) {
				throw ERROR_INVALID_HOSTNAME;
			}
		}
	} else if (
		! hostname.includes( '.' ) ||
		hostname.match( /\.(example|invalid|localhost|test)$/ )
	) {
		throw ERROR_INVALID_HOSTNAME;
	}
};
// Check for a Site Kit specific meta tag on the page to test for aggressive caching.
export const registryCheckSetupTag = ( registry ) => async () => {
	const setupTag = await registry.dispatch( CORE_SITE ).checkForSetupTag();
	if ( setupTag.error ) {
		throw ERROR_TOKEN_MISMATCH;
	}
};
// Check that server can connect to Google's APIs via the core/site/data/health-checks endpoint.
export const checkHealthChecks = async () => {
	const response = await API.get(
		'core',
		'site',
		'health-checks',
		undefined,
		{
			useCache: false,
		}
	).catch( ( error ) => {
		if ( error.code === 'fetch_error' ) {
			throw ERROR_FETCH_FAIL;
		}
		throw ERROR_API_UNAVAILABLE;
	} );

	if ( ! response?.checks?.googleAPI?.pass ) {
		throw ERROR_GOOGLE_API_CONNECTION_FAIL;
	}
};
// Check that client can connect to AMP Project.
export const checkAMPConnectivity = async () => {
	const response = await fetch( AMP_PROJECT_TEST_URL ).catch( () => {
		throw ERROR_AMP_CDN_RESTRICTED;
	} );

	if ( ! response.ok ) {
		throw ERROR_AMP_CDN_RESTRICTED;
	}
};
// Check that the current version of WordPress is 5.0+.
export const checkWPVersion = async () => {
	const { wpVersion } = global._googlesitekitBaseData || {};
	// Throw only if we can get the current version, otherwise ignore it.
	if ( wpVersion && wpVersion.major < 5 ) {
		throw ERROR_WP_PRE_V5;
	}
};
<|endoftext|>


<commit_before>/**
 * @author Björn Schießle <schiessle@owncloud.com>
 *
 * @copyright Copyright (c) 2015, ownCloud, Inc.
 * @license AGPL-3.0
 *
 * This code 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, version 3,
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
 *
 */

$(document).ready(function () {

	// show input field to add a new trusted server
	$("#ocFederationAddServer").on('click', function() {
		$('#ocFederationAddServerButton').addClass('hidden');
		$("#serverUrl").removeClass('hidden');
		$("#serverUrl").focus();
	});

	// add new trusted server
	$("#serverUrl").keyup(function (e) {
		if (e.keyCode === 13) { // add server on "enter"
			var url = $('#serverUrl').val();
			OC.msg.startSaving('#ocFederationAddServer .msg');
			$.post(
				OC.generateUrl('/apps/federation/trusted-servers'),
				{
					url: url
				}
			).done(function (data) {
					$('#serverUrl').attr('value', '');
					$('ul#listOfTrustedServers').prepend(
						$('
')
								.attr('id', data.id)
								.html('' +
									data.url +
									'')
					);
					OC.msg.finishedSuccess('#ocFederationAddServer .msg', data.message);
				})
				.fail(function (jqXHR) {
					OC.msg.finishedError('#ocFederationAddServer .msg', JSON.parse(jqXHR.responseText).message);
				});
		} else if (e.keyCode === 27) { // hide input filed again in ESC
			$('#ocFederationAddServerButton').toggleClass('hidden');
			$("#serverUrl").toggleClass('hidden');
		}
	});

// remove trusted server from list
	$( "#listOfTrustedServers" ).on('click', 'li > .icon-delete', function() {
		var $this = $(this).parent();
		id = $this.attr('id');
		$.ajax({
			url: OC.generateUrl('/apps/federation/trusted-servers/' + id),
			type: 'DELETE',
			success: function(response) {
				$this.remove();
			}
		});

	});

	$("#ocFederationSettings #autoAddServers").change(function() {
		$.post(
				OC.generateUrl('/apps/federation/auto-add-servers'),
				{
					autoAddServers: $(this).is(":checked")
				}
		);
	});

});
<commit_msg>Changed global variable to local<commit_after>/**
 * @author Björn Schießle <schiessle@owncloud.com>
 *
 * @copyright Copyright (c) 2015, ownCloud, Inc.
 * @license AGPL-3.0
 *
 * This code 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, version 3,
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
 *
 */

$(document).ready(function () {

	// show input field to add a new trusted server
	$("#ocFederationAddServer").on('click', function() {
		$('#ocFederationAddServerButton').addClass('hidden');
		$("#serverUrl").removeClass('hidden');
		$("#serverUrl").focus();
	});

	// add new trusted server
	$("#serverUrl").keyup(function (e) {
		if (e.keyCode === 13) { // add server on "enter"
			var url = $('#serverUrl').val();
			OC.msg.startSaving('#ocFederationAddServer .msg');
			$.post(
				OC.generateUrl('/apps/federation/trusted-servers'),
				{
					url: url
				}
			).done(function (data) {
					$('#serverUrl').attr('value', '');
					$('ul#listOfTrustedServers').prepend(
						$('
')
								.attr('id', data.id)
								.html('' +
									data.url +
									'')
					);
					OC.msg.finishedSuccess('#ocFederationAddServer .msg', data.message);
				})
				.fail(function (jqXHR) {
					OC.msg.finishedError('#ocFederationAddServer .msg', JSON.parse(jqXHR.responseText).message);
				});
		} else if (e.keyCode === 27) { // hide input filed again in ESC
			$('#ocFederationAddServerButton').toggleClass('hidden');
			$("#serverUrl").toggleClass('hidden');
		}
	});

// remove trusted server from list
	$( "#listOfTrustedServers" ).on('click', 'li > .icon-delete', function() {
		var $this = $(this).parent();
		var id = $this.attr('id');
		$.ajax({
			url: OC.generateUrl('/apps/federation/trusted-servers/' + id),
			type: 'DELETE',
			success: function(response) {
				$this.remove();
			}
		});

	});

	$("#ocFederationSettings #autoAddServers").change(function() {
		$.post(
				OC.generateUrl('/apps/federation/auto-add-servers'),
				{
					autoAddServers: $(this).is(":checked")
				}
		);
	});

});
<|endoftext|>


<commit_before>import React, { Component, PropTypes } from 'react';
import { Row, Col, Panel, Table, Button } from 'react-bootstrap';
import InviteNew from '../containers/invite_new';
import format from 'date-fns/format';
import { default as fromNow } from 'date-fns/distance_in_words_to_now';

class InvitesList extends Component {

  static propTypes = {
    invites: PropTypes.array.isRequired,
    revokeInvite: PropTypes.func.isRequired
  }

  revoke(id) {
    const { revokeInvite } = this.props;
    revokeInvite(id);
  }

  render() {
    const { invites } = this.props;

    return (
      <Row>
        <Col sm={10} smOffset={1}>
          <Panel>
            <Row>
              <Col sm={12}>

Invitations ({'openInviteCount'})

                <InviteNew />

              </Col>
            </Row>
            {invites.length > 0 ?
              <Row>
                <Col sm={12}>

                        Email Address
                        Type
                        Date Sent

                      {invites.map((invite) => {
                        return (

                            {invite.email}
                            {invite.role}

                              {format(invite.createdAt, 'MMMM D, YYYY h:mma')}
                               ({fromNow(invite.createdAt)} ago)

                              <Button
                                bsStyle='danger'
                                className='revoke-invite'
                                onClick={this.revoke.bind(this, invite._id)}>
                                Revoke
                              </Button>

                        );
                      })}

                </Col>
              </Row>
              :

No open invitations.

            }
          </Panel>
        </Col>
      </Row>
    );
  }
}

export default InvitesList;
<commit_msg>fix invite count in UI<commit_after>import React, { Component, PropTypes } from 'react';
import { Row, Col, Panel, Table, Button } from 'react-bootstrap';
import InviteNew from '../containers/invite_new';
import format from 'date-fns/format';
import { default as fromNow } from 'date-fns/distance_in_words_to_now';

class InvitesList extends Component {

  static propTypes = {
    invites: PropTypes.array.isRequired,
    revokeInvite: PropTypes.func.isRequired
  }

  revoke(id) {
    const { revokeInvite } = this.props;
    revokeInvite(id);
  }

  render() {
    const { invites } = this.props;

    return (
      <Row>
        <Col sm={10} smOffset={1}>
          <Panel>
            <Row>
              <Col sm={12}>

Invitations ({invites.length})

                <InviteNew />

              </Col>
            </Row>
            {invites.length > 0 ?
              <Row>
                <Col sm={12}>

                        Email Address
                        Type
                        Date Sent

                      {invites.map((invite) => {
                        return (

                            {invite.email}
                            {invite.role}

                              {format(invite.createdAt, 'MMMM D, YYYY h:mma')}
                               ({fromNow(invite.createdAt)} ago)

                              <Button
                                bsStyle='danger'
                                className='revoke-invite'
                                onClick={this.revoke.bind(this, invite._id)}>
                                Revoke
                              </Button>

                        );
                      })}

                </Col>
              </Row>
              :

No open invitations.

            }
          </Panel>
        </Col>
      </Row>
    );
  }
}

export default InvitesList;
<|endoftext|>


<commit_before>/**
 * Created by vcernomschi on 6/17/16.
 */

'use strict';

import GitHubApi from 'github';

export default class GitHubMsgPublisher {

  /**
   * @returns {string}
   * @constructor
   */
  static get isPullRequest() {
    let pullRequest = process.env['TRAVIS_PULL_REQUEST'];

    return (pullRequest !== 'false' || !isNaN(pullRequest));
  }

  /**
   * @returns {string}
   * @constructor
   */
  static get gitPRNumber() {
    return process.env['TRAVIS_PULL_REQUEST'];
  }

  /**
   * @returns {string}
   * @constructor
   */
  static get gitPrUrl() {
    return `https://api.github.com/repos/${GitHubMsgPublisher.gitUser}/${GitHubMsgPublisher.gitRepoName}/pulls/${GitHubMsgPublisher.gitPRNumber}`;
  }

  /**
   * @returns {string}
   * @constructor
   */
  static get gitUser() {
    return process.env['TRAVIS_REPO_SLUG'].replace(/(.*)\/.*/i, '$1');
  }

  /**
   * @returns {string}
   * @constructor
   */
  static get gitRepoName() {
    return process.env['TRAVIS_REPO_SLUG'].replace(/.*\/(.*)/i, '$1');
  }

  constructor() {
    this.github = new GitHubApi({
      debug: false,
      protocol: 'https',
      host: 'api.github.com',
      timeout: 5000,
      headers: {
        'user-agent': 'Code-Coverage-GitHub-App'
      },
      followRedirects: false, // default: true; there's currently an issue with non-get redirects, so allow ability to disable follow-redirects
      includePreview: true // default: false; includes accept headers to allow use of stuff under preview period
    });

    this.github.authenticate({
      type: 'oauth',
      token: process.env['GITHUB_OAUTH_TOKEN'],
    });
  }

  /**
   * Add comments for PR or fails if coverage
   * @param {Number} s3SumPercent - s3 summary coverage percent
   * @param {Number} localSumPercent - local summary coverage percent
   * @param {Function} callback
   */
  addComment(s3SumPercent, localSumPercent, callback) {
    let commentMsg;

    //failed if coverage decreased more that 1 %
    let isFailed = ((localSumPercent + 1) < s3SumPercent);
    let failMsg = 'Failed due to decreasing coverage';

    //no need to add comments for !PR
    if (!GitHubMsgPublisher.isPullRequest) {
      callback(null, null);
      return;
    }

    if (isFailed) {
      commentMsg = `:x: coverage decreased from ${s3SumPercent}% to ${localSumPercent}%`;
    } else if (localSumPercent === s3SumPercent) {
      commentMsg = `:white_check_mark: coverage remained the same at ${localSumPercent}%`;
    } else if (-1 < localSumPercent - s3SumPercent < 0) {
      commentMsg = `:warning: coverage decreased less than 1% from ${s3SumPercent}% to ${localSumPercent}%`;
    } else {
      commentMsg = `:white_check_mark: coverage increased from ${s3SumPercent}% to ${localSumPercent}%`;
    }

    this.github.issues.getComments({
        user: GitHubMsgPublisher.gitUser,
        repo: GitHubMsgPublisher.gitRepoName,
        number: GitHubMsgPublisher.gitPRNumber,
      }, (err, issues) => {
        let isCommentAdded = false;

        if (err) {
          console.log(err);
          callback(err, null);
          return;
        }

        for (let issue of issues) {
          if (issue.hasOwnProperty('body') && issue.body === commentMsg) {
            isCommentAdded = true;
            console.log('Comment has already been added: ', commentMsg);

            if (isFailed) {
              console.log(failMsg);
              process.exit(1)
            }
          }
        }

        if (!isCommentAdded) {
          this.github.issues.createComment({
            user: GitHubMsgPublisher.gitUser,
            repo: GitHubMsgPublisher.gitRepoName,
            number: GitHubMsgPublisher.gitPRNumber,
            body: commentMsg,
          }, (err, res) => {
            if (err) {
              console.log(err);
              callback(err, null);
              return;
            }

            callback(null, res);

            if (isFailed) {
              console.log(failMsg);
              process.exit(1)
            }
          });
        }
      }
    );
  }
}
<commit_msg>Fix operator priorities issue<commit_after>/**
 * Created by vcernomschi on 6/17/16.
 */

'use strict';

import GitHubApi from 'github';

export default class GitHubMsgPublisher {

  /**
   * @returns {string}
   * @constructor
   */
  static get isPullRequest() {
    let pullRequest = process.env['TRAVIS_PULL_REQUEST'];

    return (pullRequest !== 'false' || !isNaN(pullRequest));
  }

  /**
   * @returns {string}
   * @constructor
   */
  static get gitPRNumber() {
    return process.env['TRAVIS_PULL_REQUEST'];
  }

  /**
   * @returns {string}
   * @constructor
   */
  static get gitPrUrl() {
    return `https://api.github.com/repos/${GitHubMsgPublisher.gitUser}/${GitHubMsgPublisher.gitRepoName}/pulls/${GitHubMsgPublisher.gitPRNumber}`;
  }

  /**
   * @returns {string}
   * @constructor
   */
  static get gitUser() {
    return process.env['TRAVIS_REPO_SLUG'].replace(/(.*)\/.*/i, '$1');
  }

  /**
   * @returns {string}
   * @constructor
   */
  static get gitRepoName() {
    return process.env['TRAVIS_REPO_SLUG'].replace(/.*\/(.*)/i, '$1');
  }

  constructor() {
    this.github = new GitHubApi({
      debug: false,
      protocol: 'https',
      host: 'api.github.com',
      timeout: 5000,
      headers: {
        'user-agent': 'Code-Coverage-GitHub-App'
      },
      followRedirects: false, // default: true; there's currently an issue with non-get redirects, so allow ability to disable follow-redirects
      includePreview: true // default: false; includes accept headers to allow use of stuff under preview period
    });

    this.github.authenticate({
      type: 'oauth',
      token: process.env['GITHUB_OAUTH_TOKEN'],
    });
  }

  /**
   * Add comments for PR or fails if coverage
   * @param {Number} s3SumPercent - s3 summary coverage percent
   * @param {Number} localSumPercent - local summary coverage percent
   * @param {Function} callback
   */
  addComment(s3SumPercent, localSumPercent, callback) {
    let commentMsg;

    //failed if coverage decreased more that 1 %
    let isFailed = ((localSumPercent + 1) < s3SumPercent);
    let failMsg = 'Failed due to decreasing coverage';

    //no need to add comments for !PR
    if (!GitHubMsgPublisher.isPullRequest) {
      callback(null, null);
      return;
    }

    if (isFailed) {
      commentMsg = `:x: coverage decreased from ${s3SumPercent}% to ${localSumPercent}%`;
    } else if (localSumPercent === s3SumPercent) {
      commentMsg = `:white_check_mark: coverage remained the same at ${localSumPercent}%`;
    } else if (-1 < (localSumPercent - s3SumPercent) && (localSumPercent - s3SumPercent) < 0) {
      commentMsg = `:warning: coverage decreased less than 1% from ${s3SumPercent}% to ${localSumPercent}%`;
    } else {
      commentMsg = `:white_check_mark: coverage increased from ${s3SumPercent}% to ${localSumPercent}%`;
    }

    this.github.issues.getComments({
        user: GitHubMsgPublisher.gitUser,
        repo: GitHubMsgPublisher.gitRepoName,
        number: GitHubMsgPublisher.gitPRNumber,
      }, (err, issues) => {
        let isCommentAdded = false;

        if (err) {
          console.log(err);
          callback(err, null);
          return;
        }

        for (let issue of issues) {
          if (issue.hasOwnProperty('body') && issue.body === commentMsg) {
            isCommentAdded = true;
            console.log('Comment has already been added: ', commentMsg);

            if (isFailed) {
              console.log(failMsg);
              process.exit(1)
            }
          }
        }

        if (!isCommentAdded) {
          this.github.issues.createComment({
            user: GitHubMsgPublisher.gitUser,
            repo: GitHubMsgPublisher.gitRepoName,
            number: GitHubMsgPublisher.gitPRNumber,
            body: commentMsg,
          }, (err, res) => {
            if (err) {
              console.log(err);
              callback(err, null);
              return;
            }

            callback(null, res);

            if (isFailed) {
              console.log(failMsg);
              process.exit(1)
            }
          });
        }
      }
    );
  }
}
<|endoftext|>


<commit_before>/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- /
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */

'use strict';

function WindowSprite(win) {
  var element = this.element = document.createElement('div');
  if (win.application.fullscreen) {
    element.className = 'windowSprite fullscreen';
  } else {
    element.className = 'windowSprite';
  }
}

WindowSprite.prototype = {
  setActive: function ws_setActive(active) {
    var classes = this.element.classList;
    if (classes.contains('active') === active)
      return;

    classes.toggle('active');
  },

  add: function ws_add() {
    document.body.appendChild(this.element);
  },

  remove: function ws_remove() {
    if (!this.element.parentNode)
      return;

    document.body.removeChild(this.element);
  },

  crossFade: function ws_crossFade() {
    var afterCrossFade = (this.remove).bind(this);
    // XXX: wait for 50ms for iframe to be painted.
    // setTimeout(0) is used to escape the current event firing.
    setTimeout((function () {
      this.element.addEventListener('transitionend', afterCrossFade);
      this.element.classList.add('crossFade');
    }).bind(this), 50);
  }
};

var _statusBarHeight = null;

function AppWindow(application, id) {
  var element = this.element = document.createElement('iframe');
  element.setAttribute('mozallowfullscreen', 'true');

  // TODO: a platform fix will come
  var exceptions = ['Dialer', 'Settings', 'Camera'];
  if(exceptions.indexOf(application.name) == -1) {
    element.setAttribute('mozbrowser', 'true');
  }
  element.id = 'window_' + id;
  element.className = 'appWindow';

  this.application = application;
  this.id = id;
  this.resize();
}

AppWindow.prototype = {
  element: null,

  _loaded: false,

  show: function window_show() {
    this.element.classList.add('active');
  },

  hide: function window_hide() {
    this.element.classList.remove('active');
  },

  focus: function window_focus(callback) {
    if (this.element.classList.contains('active'))
      return;

    this.resize();

    var sprite = new WindowSprite(this);
    sprite.add();
    this.show();

    var focus = (function(evt) {
      sprite.element.removeEventListener('transitionend', focus);

      sprite.crossFade();

      var url = this.application.url;
      var element = this.element;
      if (!this._loaded) {
        element.src = url;
        this._loaded = true;

        window.addEventListener('message', function waitForAppReady(evt) {
          if (evt.data !== 'appready')
            return;

          window.removeEventListener('message', waitForAppReady);
          element.contentWindow.postMessage({
            message: 'visibilitychange',
            url: element.src,
            hidden: false
          }, '*');
        });
      } else {
        element.contentWindow.postMessage({
          message: 'visibilitychange',
          url: url,
          hidden: false
        }, '*');
      }
      element.focus();

      if (callback)
        callback();
    }).bind(this);
    sprite.element.addEventListener('transitionend', focus);

    if (this.application.fullscreen) {
      document.getElementById('screen').classList.add('fullscreen');
    }

    document.body.offsetHeight;
    sprite.setActive(true);
  },

  blur: function window_blur(callback) {
    if (!this.element.classList.contains('active'))
      return;

    var sprite = new WindowSprite(this);
    sprite.setActive(true);
    sprite.add();

    var blur = (function(evt) {
      sprite.element.removeEventListener('transitionend', blur);
      this.hide();
      sprite.remove();

      var element = this.element;
      element.blur();
      element.contentWindow.postMessage({
        message: 'visibilitychange',
        url: this.application.url,
        hidden: true
      }, '*');

      window.top.focus();

      if (callback)
        callback();
    }).bind(this);
    sprite.element.addEventListener('transitionend', blur);

    if (this.application.fullscreen) {
      document.getElementById('screen').classList.remove('fullscreen');
    }

    // NOTE: for the moment, orientation only works when fullscreen because of a
    // too dirty hack...
    if (this.application.fullscreen && this.application.orientation) {
      var width = this.element.style.width;
      this.element.style.width = this.element.style.height;
      this.element.style.height = width;
    }

    document.body.offsetHeight;
    sprite.setActive(false);
  },

  resize: function window_resize() {
    var element = this.element;
    var documentElement = document.documentElement;
    // NOTE: for the moment, orientation only works when fullscreen because of a
    // too dirty hack...
    if (this.application.fullscreen && this.application.orientation) {
      var width = this.element.style.width;
      element.style.width = documentElement.clientHeight + 'px';
      element.style.height = documentElement.clientWidth + 'px';

      element.classList.add(this.application.orientation);
    } else {
      element.style.width = documentElement.clientWidth + 'px';

      var height = documentElement.clientHeight;
      if (!this.application.fullscreen) {
        height -= document.getElementById('statusbar').offsetHeight;
      }
      element.style.height = height + 'px';
    }
  }
};

var WindowManager = {
  init: function wm_init() {
    window.addEventListener('keyup', this);

    window.addEventListener('appopen', this);
    window.addEventListener('appwillclose', this);
    window.addEventListener('locked', this);
    window.addEventListener('unlocked', this);
    window.addEventListener('resize', this);

    this.container = document.getElementById('windows');
  },

  enabled: true,

  handleEvent: function wm_handleEvent(evt) {
    switch (evt.type) {
      case 'keyup':
        switch (evt.keyCode) {
          case evt.DOM_VK_HOME:
            ScreenManager.turnScreenOn();
            if (this.enabled)
              this.closeForegroundWindow();
            break;
          case evt.DOM_VK_ESCAPE:
            if (this.enabled && !evt.defaultPrevented) {
              if (TaskManager.isActive()) {
                TaskManager.hide();
              } else if (IMEManager.targetWindow) {
                IMEManager.hideIME();
              } else {
                this.closeForegroundWindow();
              }
              evt.preventDefault();
            }
            break;
        }
        break;
      case 'message':
        if (!this.enabled)
          return;
        if (evt.data == 'appclose')
          this.closeForegroundWindow();
        break;
      case 'home':
        if (!this.enabled)
          return;
        this.closeForegroundWindow();
        break;
      case 'appopen':
        this.container.classList.add('active');
        break;
      case 'appwillclose':
        this.container.classList.remove('active');
        break;
      case 'locked':
        if (this._foregroundWindow.application.fullscreen) {
          document.getElementById('screen').classList.remove('fullscreen');
        }
        this.enabled = false;
        break;
      case 'unlocked':
        if (this._foregroundWindow.application.fullscreen) {
          document.getElementById('screen').classList.add('fullscreen');
        }
        this.enabled = true;
        break;
      case 'resize':
        if (!this._foregroundWindow)
          return;
        this._foregroundWindow.resize();
        break;
    }
  },

  windows: [],
  getWindowByApp: function wm_getWindowByApp(app) {
    var windows = this.windows;
    for (var i = 0, length = windows.length; i < length; i++) {
      if (windows[i].application === app)
        return windows[i];
    }

    return null;
  },

  add: function wm_add(win) {
    this.windows.push(win);
    this.container.appendChild(win.element);
  },

  remove: function wm_remove(win) {
    var windows = this.windows;
    for (var i = 0, length = windows.length; i < length; i++) {
      if (windows[i] != win)
        continue;

      this.container.removeChild(win.element);
      windows.splice(i, 1);
      return;
    }
  },

  _foregroundWindow: null,
  getForegroundWindow: function wm_getForegroundWindow() {
    return this._foregroundWindow;
  },

  setForegroundWindow: function wm_setForegroundWindow(newWindow) {
    var oldWindow = this._foregroundWindow;
    if (oldWindow === newWindow || this._isInTransition)
      return;
    this._foregroundWindow = newWindow;
    this._isInTransition = true;

    newWindow.focus((function focusCallback() {
      this._isInTransition = false;
      this._fireEvent(newWindow.element, 'appopen', newWindow.name);
    }).bind(this));
  },

  closeForegroundWindow: function wm_closeForegroundWindow() {
    var foregroundWindow = this._foregroundWindow;
    if (!foregroundWindow || this._isInTransition)
      return;

    this._fireEvent(foregroundWindow.element, 'appwillclose', name);

    var oldWindow = this._foregroundWindow;
    this._foregroundWindow = null;
    this._isInTransition = true;

    oldWindow.blur((function blurCallback() {
      this._isInTransition = false;
      this._fireEvent(foregroundWindow.element, 'appclose');
      if (oldWindow.application.hackKillMe) {
        // waiting for the closing transition to end before removing the iframe from dom
        oldWindow.element.addEventListener('transitionend', function waitToKill() {
          oldWindow.element.removeEventListener('transitionend', waitToKill);
          TaskManager.remove(oldWindow.application, oldWindow.id);
        });
      }
    }).bind(this));
  },

  _lastWindowId: 0,
  launch: function wm_launch(url) {
    var application = Gaia.AppManager.getInstalledAppForURL(url);
    if (!application)
      return;

    var name = application.name;

    // getInstalledAppForURL will return an object with the URL stripped
    // so let's set it back to default
    application.url = url;

    var applicationWindow = this.getWindowByApp(application);
    if (applicationWindow) {
      Gaia.AppManager.foregroundWindow = applicationWindow.element;
      TaskManager.sendToFront(applicationWindow.id);
    } else {
      applicationWindow = new AppWindow(application, ++this._lastWindowId);
      this.add(applicationWindow);

      // To be compatible with the upstream webapi.js file,
      // foregroundWindow should be set on the AppManager...
      Gaia.AppManager.foregroundWindow = applicationWindow.element;

      this._fireEvent(applicationWindow.element, 'appwillopen', name);
      TaskManager.add(application, applicationWindow.id);
    }

    this.setForegroundWindow(applicationWindow);
    return applicationWindow;
  },

  kill: function wm_kill(url) {
    var application = Gaia.AppManager.getInstalledAppForURL(url);
    var applicationWindow = this.getWindowByApp(application);

    if (!applicationWindow)
      return;

    var name = application.name;
    this._fireEvent(applicationWindow.element, 'appkill', name);
    this.remove(applicationWindow);
  },

  _fireEvent: function wm_fireEvent(target, type, details) {
    var evt = document.createEvent('CustomEvent');
    evt.initCustomEvent(type, true, false, details || null);
    target.dispatchEvent(evt);
  }
};

window.addEventListener('load', function wm_loadHandler(evt) {
  window.removeEventListener('load', wm_loadHandler);
  WindowManager.init();
});

<commit_msg>comment the mystery lines that look like no-ops<commit_after>/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- /
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */

'use strict';

function WindowSprite(win) {
  var element = this.element = document.createElement('div');
  if (win.application.fullscreen) {
    element.className = 'windowSprite fullscreen';
  } else {
    element.className = 'windowSprite';
  }
}

WindowSprite.prototype = {
  setActive: function ws_setActive(active) {
    var classes = this.element.classList;
    if (classes.contains('active') === active)
      return;

    classes.toggle('active');
  },

  add: function ws_add() {
    document.body.appendChild(this.element);
  },

  remove: function ws_remove() {
    if (!this.element.parentNode)
      return;

    document.body.removeChild(this.element);
  },

  crossFade: function ws_crossFade() {
    var afterCrossFade = (this.remove).bind(this);
    // XXX: wait for 50ms for iframe to be painted.
    // setTimeout(0) is used to escape the current event firing.
    setTimeout((function () {
      this.element.addEventListener('transitionend', afterCrossFade);
      this.element.classList.add('crossFade');
    }).bind(this), 50);
  }
};

var _statusBarHeight = null;

function AppWindow(application, id) {
  var element = this.element = document.createElement('iframe');
  element.setAttribute('mozallowfullscreen', 'true');

  // TODO: a platform fix will come
  var exceptions = ['Dialer', 'Settings', 'Camera'];
  if(exceptions.indexOf(application.name) == -1) {
    element.setAttribute('mozbrowser', 'true');
  }
  element.id = 'window_' + id;
  element.className = 'appWindow';

  this.application = application;
  this.id = id;
  this.resize();
}

AppWindow.prototype = {
  element: null,

  _loaded: false,

  show: function window_show() {
    this.element.classList.add('active');
  },

  hide: function window_hide() {
    this.element.classList.remove('active');
  },

  focus: function window_focus(callback) {
    if (this.element.classList.contains('active'))
      return;

    this.resize();

    var sprite = new WindowSprite(this);
    sprite.add();
    this.show();

    var focus = (function(evt) {
      sprite.element.removeEventListener('transitionend', focus);

      sprite.crossFade();

      var url = this.application.url;
      var element = this.element;
      if (!this._loaded) {
        element.src = url;
        this._loaded = true;

        window.addEventListener('message', function waitForAppReady(evt) {
          if (evt.data !== 'appready')
            return;

          window.removeEventListener('message', waitForAppReady);
          element.contentWindow.postMessage({
            message: 'visibilitychange',
            url: element.src,
            hidden: false
          }, '*');
        });
      } else {
        element.contentWindow.postMessage({
          message: 'visibilitychange',
          url: url,
          hidden: false
        }, '*');
      }
      element.focus();

      if (callback)
        callback();
    }).bind(this);
    sprite.element.addEventListener('transitionend', focus);

    if (this.application.fullscreen) {
      document.getElementById('screen').classList.add('fullscreen');
    }

    document.body.offsetHeight; // Trigger layout; not a no-op

    sprite.setActive(true);
  },

  blur: function window_blur(callback) {
    if (!this.element.classList.contains('active'))
      return;

    var sprite = new WindowSprite(this);
    sprite.setActive(true);
    sprite.add();

    var blur = (function(evt) {
      sprite.element.removeEventListener('transitionend', blur);
      this.hide();
      sprite.remove();

      var element = this.element;
      element.blur();
      element.contentWindow.postMessage({
        message: 'visibilitychange',
        url: this.application.url,
        hidden: true
      }, '*');

      window.top.focus();

      if (callback)
        callback();
    }).bind(this);
    sprite.element.addEventListener('transitionend', blur);

    if (this.application.fullscreen) {
      document.getElementById('screen').classList.remove('fullscreen');
    }

    // NOTE: for the moment, orientation only works when fullscreen because of a
    // too dirty hack...
    if (this.application.fullscreen && this.application.orientation) {
      var width = this.element.style.width;
      this.element.style.width = this.element.style.height;
      this.element.style.height = width;
    }

    document.body.offsetHeight;  // Trigger layout; not a no-op.

    sprite.setActive(false);
  },

  resize: function window_resize() {
    var element = this.element;
    var documentElement = document.documentElement;
    // NOTE: for the moment, orientation only works when fullscreen because of a
    // too dirty hack...
    if (this.application.fullscreen && this.application.orientation) {
      var width = this.element.style.width;
      element.style.width = documentElement.clientHeight + 'px';
      element.style.height = documentElement.clientWidth + 'px';

      element.classList.add(this.application.orientation);
    } else {
      element.style.width = documentElement.clientWidth + 'px';

      var height = documentElement.clientHeight;
      if (!this.application.fullscreen) {
        height -= document.getElementById('statusbar').offsetHeight;
      }
      element.style.height = height + 'px';
    }
  }
};

var WindowManager = {
  init: function wm_init() {
    window.addEventListener('keyup', this);

    window.addEventListener('appopen', this);
    window.addEventListener('appwillclose', this);
    window.addEventListener('locked', this);
    window.addEventListener('unlocked', this);
    window.addEventListener('resize', this);

    this.container = document.getElementById('windows');
  },

  enabled: true,

  handleEvent: function wm_handleEvent(evt) {
    switch (evt.type) {
      case 'keyup':
        switch (evt.keyCode) {
          case evt.DOM_VK_HOME:
            ScreenManager.turnScreenOn();
            if (this.enabled)
              this.closeForegroundWindow();
            break;
          case evt.DOM_VK_ESCAPE:
            if (this.enabled && !evt.defaultPrevented) {
              if (TaskManager.isActive()) {
                TaskManager.hide();
              } else if (IMEManager.targetWindow) {
                IMEManager.hideIME();
              } else {
                this.closeForegroundWindow();
              }
              evt.preventDefault();
            }
            break;
        }
        break;
      case 'message':
        if (!this.enabled)
          return;
        if (evt.data == 'appclose')
          this.closeForegroundWindow();
        break;
      case 'home':
        if (!this.enabled)
          return;
        this.closeForegroundWindow();
        break;
      case 'appopen':
        this.container.classList.add('active');
        break;
      case 'appwillclose':
        this.container.classList.remove('active');
        break;
      case 'locked':
        if (this._foregroundWindow.application.fullscreen) {
          document.getElementById('screen').classList.remove('fullscreen');
        }
        this.enabled = false;
        break;
      case 'unlocked':
        if (this._foregroundWindow.application.fullscreen) {
          document.getElementById('screen').classList.add('fullscreen');
        }
        this.enabled = true;
        break;
      case 'resize':
        if (!this._foregroundWindow)
          return;
        this._foregroundWindow.resize();
        break;
    }
  },

  windows: [],
  getWindowByApp: function wm_getWindowByApp(app) {
    var windows = this.windows;
    for (var i = 0, length = windows.length; i < length; i++) {
      if (windows[i].application === app)
        return windows[i];
    }

    return null;
  },

  add: function wm_add(win) {
    this.windows.push(win);
    this.container.appendChild(win.element);
  },

  remove: function wm_remove(win) {
    var windows = this.windows;
    for (var i = 0, length = windows.length; i < length; i++) {
      if (windows[i] != win)
        continue;

      this.container.removeChild(win.element);
      windows.splice(i, 1);
      return;
    }
  },

  _foregroundWindow: null,
  getForegroundWindow: function wm_getForegroundWindow() {
    return this._foregroundWindow;
  },

  setForegroundWindow: function wm_setForegroundWindow(newWindow) {
    var oldWindow = this._foregroundWindow;
    if (oldWindow === newWindow || this._isInTransition)
      return;
    this._foregroundWindow = newWindow;
    this._isInTransition = true;

    newWindow.focus((function focusCallback() {
      this._isInTransition = false;
      this._fireEvent(newWindow.element, 'appopen', newWindow.name);
    }).bind(this));
  },

  closeForegroundWindow: function wm_closeForegroundWindow() {
    var foregroundWindow = this._foregroundWindow;
    if (!foregroundWindow || this._isInTransition)
      return;

    this._fireEvent(foregroundWindow.element, 'appwillclose', name);

    var oldWindow = this._foregroundWindow;
    this._foregroundWindow = null;
    this._isInTransition = true;

    oldWindow.blur((function blurCallback() {
      this._isInTransition = false;
      this._fireEvent(foregroundWindow.element, 'appclose');
      if (oldWindow.application.hackKillMe) {
        // waiting for the closing transition to end before removing the iframe from dom
        oldWindow.element.addEventListener('transitionend', function waitToKill() {
          oldWindow.element.removeEventListener('transitionend', waitToKill);
          TaskManager.remove(oldWindow.application, oldWindow.id);
        });
      }
    }).bind(this));
  },

  _lastWindowId: 0,
  launch: function wm_launch(url) {
    var application = Gaia.AppManager.getInstalledAppForURL(url);
    if (!application)
      return;

    var name = application.name;

    // getInstalledAppForURL will return an object with the URL stripped
    // so let's set it back to default
    application.url = url;

    var applicationWindow = this.getWindowByApp(application);
    if (applicationWindow) {
      Gaia.AppManager.foregroundWindow = applicationWindow.element;
      TaskManager.sendToFront(applicationWindow.id);
    } else {
      applicationWindow = new AppWindow(application, ++this._lastWindowId);
      this.add(applicationWindow);

      // To be compatible with the upstream webapi.js file,
      // foregroundWindow should be set on the AppManager...
      Gaia.AppManager.foregroundWindow = applicationWindow.element;

      this._fireEvent(applicationWindow.element, 'appwillopen', name);
      TaskManager.add(application, applicationWindow.id);
    }

    this.setForegroundWindow(applicationWindow);
    return applicationWindow;
  },

  kill: function wm_kill(url) {
    var application = Gaia.AppManager.getInstalledAppForURL(url);
    var applicationWindow = this.getWindowByApp(application);

    if (!applicationWindow)
      return;

    var name = application.name;
    this._fireEvent(applicationWindow.element, 'appkill', name);
    this.remove(applicationWindow);
  },

  _fireEvent: function wm_fireEvent(target, type, details) {
    var evt = document.createEvent('CustomEvent');
    evt.initCustomEvent(type, true, false, details || null);
    target.dispatchEvent(evt);
  }
};

window.addEventListener('load', function wm_loadHandler(evt) {
  window.removeEventListener('load', wm_loadHandler);
  WindowManager.init();
});

<|endoftext|>