<commit_before>// ==========================================================================
//                        DG.DocumentArchiver
//
//  Author:   Kirk Swenson
//
//  Copyright (c) 2014 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 */
/**
 * @class DocumentArchiver
 *
 * Coordinates streaming of documents and document elements. Deals with documents
 * as streamable entities in contrast to the Document Controller that
 * provides operational coordination of document elements. This class addresses
 * how to get documents in and out of CODAP predictably and the Document
 * Controller deals with Documents while they are being presented and
 * interacted with within CODAP.
 *
 * This class, at this point, has no state. It
 * functions, more or less, as a mixin to the App Controller, providing
 * a logically related collection of utility methods.
 *
 * @extends SC.Object
 */
/* globals Papa: true*/

DG.DocumentArchiver = SC.Object.extend(
/** @scope DG.DocumentArchiver.prototype */ {

    /**
     * Tests the JSON text for validity as a possible document.
     *
     * The following assertions are tested:
     *
     * (1) The document is valid JSON. that is: it parses correctly
     * (2) The document looks like a valid CODAP document. It has all mandatory
     * top level elements and no unexpected top level elements.
     * (3) all internal links can be resolved.
     *
     * @param    {String}    iDocument -- The JSON-formatted document text
     * @returns  {[String]}   An array of error messages, zero length, if none.
     */
    isValidJsonDocument: function (iDocument) {
      function visit(key, value, fn) {
        var rtn = fn(key, value);
        if (rtn !== false) {
          if (Array.isArray(value)) {
            value.forEach(function (item) {
              visit(key, item, fn);
            });
          } else if (typeof value === 'object') {
            DG.ObjectMap.forEach(value, function (key, item) {
              visit(key, item, fn);
            });
          }
        }
      }

      function validateInternalRefs(iDocument) {
        var parts = subDocs.copy(false);
        var symbols = [];
        var references = [];
        parts.unshift(iDocument);
        parts.forEach(function (documentPart) {
          visit('doc', documentPart, function (key, value) {
            if (key === 'guid') {
              // store guids in symbol table
              symbols.push(Number(value));
              return true;
            } else if (key === '_links_') {
              // store links as references
              visit(key, value, function (k, v) {
                if (k === 'id') {
                  references.push(Number(v));
                }
                return true;
              });
              return false;
            } else if (key === 'parent') {
              // store parents as references
              references.push(Number(value));
              return true;
            } else if (key === 'contextStorage') {
            // context storage is private to data interactive
              return false;
            }

          });
        });
        references.forEach(function (ref) {
          // verify link is resolved
          if (symbols.indexOf(Number(ref)) < 0) {
            errors.push('DG.AppController.validateDocument.unresolvedID'.loc(ref));
          }
        });
      }
      var expectedProperties = [
        'appBuildNum',
        'appName',
        'appVersion',
        'changeCount',
        'components',
        'contexts',
        'globalValues',
        'guid',
        'metadata',
        'name',
        '_permissions',
        '_openedFromSharedDocument' // this is an annotation we may create in
                                    // CodapCommonStorage
      ];
      var requiredProperties = [
        'name'
      ];
      var errors = [];
      var doc;
      var subDocs = [];

      if (typeof iDocument === 'string') {
        try {
          doc = JSON.parse(iDocument);
        } catch (ex) {
          errors.push('DG.AppController.validateDocument.parseError'.loc(ex));
        }
      } else {
        doc = iDocument;
      }

      if (doc) {
        requiredProperties.forEach(function (prop) {
            if (!doc.hasOwnProperty(prop)) {
              errors.push('DG.AppController.validateDocument.missingRequiredProperty'.loc(prop));
            }
          }
        );
        DG.ObjectMap.keys(doc).forEach(function (prop) {
            if (expectedProperties.indexOf(prop) < 0) {
              // log unexpected properties but don't fail to open
              DG.log('DG.AppController.validateDocument.unexpectedProperty'.loc(prop));
            }
          }
        );
        validateInternalRefs(doc);
      }
      DG.log('Document validation: ' + (errors.length? JSON.stringify(errors): 'No Errors'));
      return errors;
    },

    /**
     * Converts a string representing the contents of a CSV or tab-delimited
     * file into a simple Streamable CODAP document. This document can be loaded
     * into CODAP. The CODAP document will have one data context with a single
     * Collection that contains the CSV data. It is assumed that either the first
     * line will contain the name of the collection and the second will contain the
     * names of each of the attributes or the collection name will be omitted and
     * the first line will contain the names of the attributes.
     *
     * @param {string} iText The contents of a CSV or tab-delimited file formatted
     *   according to the [rfc4180](http://www.ietf.org/rfc/rfc4180.txt) standard.
   *   @param {string} iContextName name of the context we will create
   *   @param {string} iCollectionName name of the collection we will create
     * @param {string} iFileURL The name of the file or URL from which the
     *   CSV text was extracted.
     * @returns {Object||undefined} A streamable CODAP document.
     */
    convertCSVDataToCODAPDocument: function (iText, iContextName, iCollectionName, iFileURL) {
      /**
       * Returns table stats object:
       *   numRows {number}
       *   maxCols {number}
       *   headerRows {[number]} Array of row indices of header rows
       *   rowLengthCounts {[number]} sparse array of counts of row lengths
       */
      function getTableStats(table) {
        var stats = {
          numRows: 0,
          maxCols: 0,
          rowLengthCounts: [],
          preponderantLength: 0
        };

        stats.numRows = table.length;
        table.forEach(function (row) {
          var len = row.length;
          if (stats.rowLengthCounts[len]) {
            stats.rowLengthCounts[len]++;
          } else {
            stats.rowLengthCounts[len] = 1;
          }
          stats.maxCols = Math.max(stats.maxCols, len);
        });
        var maxCount = 0;
        stats.rowLengthCounts.forEach(function (count, ix) {
          if (maxCount < count) {
            maxCount = count;
            stats.preponderantLength = ix;
          }
        });
        console.log('stats: ' + JSON.stringify(stats));
        return stats;
      }

      function guaranteeUnique(name, nameArray) {
        var ix = 0;
        var newName = name;
        while (nameArray.indexOf(newName) >= 0) {
          ix++;
          newName = name + '_' + ix;
        }
        return newName;
      }

      var tValuesArray,
        tAttrNamesRow,// Column Header Names: should be second row
        tAttrNames = [], tTableStats, ix,
        tDoc = {
          name: 'DG.Document.defaultDocumentName'.loc(),
          guid: 1,
          components: [
            {
              "type": "DG.TableView",
              "componentStorage": {
                "_links_": {
                  "context": {
                    "type": "DG.DataContextRecord",
                    "id": 4
                  }
                }
              }
            },
            {
              "type": "DG.TextView",
              "componentStorage": {
                "text": "url=" + iFileURL,
                "name": "Data Source"
              }
            }
          ],
          contexts: [
            {
              "type": "DG.DataContext",
              "collections": [
                {
                  "attrs": [],
                  "cases": []
                }
              ],
              "contextStorage": {
                "gameUrl": iFileURL
              }
            }
          ],
          appName: DG.APPNAME,
          appVersion: DG.VERSION,
          appBuildNum: DG.BUILD_NUM,
          globalValues: []
        },
        tAttrsArray = tDoc.contexts[0].collections[0].attrs,
        tCasesArray = tDoc.contexts[0].collections[0].cases;

      // a euro number is...
      //                     + optional sign value
      //                     |     + either grouped whole number using dots or spaces for grouping
      //                     |     |                            + or ungrouped whole number
      //                     |     |                            |       + followed optionally by a comma decimal separator
      //                     |     |                            |       |   +git and either decimal digits or
      //                     v     v                            v       v   v        v grouped decimal digits
      var isEuroNumberRE = /^[-+]?(([0-9]{1,3}([. ][0-9]{3})*)|([0-9]*))(,(([0-9]*)|([0-9]{3}[. ])[0-9]{1,3}))?$/;

      var result = Papa.parse(iText, {
        dynamicTyping: false,
        header: false,
        skipEmptyLines: true
      });

      if (result.errors && result.errors.length > 0) {
        DG.logWarn("CSV Parse errors (%@): %@".loc(iFileURL, JSON.stringify(result.errors)));
        return;
      }

      // if we have a csv with semicolon separators we assume are using the EU
      // number representation (comma as the decimal separator), so we convert
      // anything that looks like an EU number to standard representation.
      DG.log("CSV Parse Metadata: %@".loc(JSON.stringify(result.meta)));
      var euroDelimiter = (result.meta && result.meta.delimiter === ';');
      tValuesArray = result.data.map(function (row) {
        return row.map(function (str) {
          if (str === null || str === undefined) {
            return str;
          }
          str = str.trim();
          if (str.length === 0) {
            return str;
          }
          // converts a euro number string to a US number string
          var isEuroNumber = euroDelimiter && isEuroNumberRE.test(str);
          if (isEuroNumber) {
            return Number(str.replace(/[. ]/g, '').replace(/,/, '.'));
          } else {
            if (!isNaN(str)) {
              return Number(str);
            } else {
              return str;
            }
          }
        });
      });

      tTableStats = getTableStats(tValuesArray);

      if (!tValuesArray || tValuesArray.length < 1) {
        DG.logWarn('CSV or Tab-delimited parsing failed: ' + iFileURL);
        return;
      }

      tAttrNamesRow = tValuesArray.shift();

      var tDataContext = tDoc.contexts[0],
          tCollection = tDataContext.collections[0];
      tDataContext.name = tDataContext.title = iContextName;
      tCollection.name = tCollection.title = iCollectionName;

      var canonicalName;
      for (ix = 0; ix < tTableStats.maxCols; ix += 1) {
        canonicalName = DG.Attribute.canonicalizeName(tAttrNamesRow[ix]);
        tAttrNames.push(guaranteeUnique(canonicalName, tAttrNames));
      }

      tAttrNames.forEach(function (iName) {
        tAttrsArray.push( {
          name: iName,
          editable: true
        });
      });

      tValuesArray.forEach( function( iValues) {
        var tCase = {
          values: {}
        };
        tAttrNames.forEach( function( iName, iIndex) {
          var tValue = iValues[ iIndex];
          if( DG.isDateString( tValue)) {
            tValue = DG.createDate( tValue);
          }
          tCase.values[ iName] = DG.DataUtilities.canonicalizeInputValue(tValue);
        });
        tCasesArray.push( tCase);
      });
      return tDoc;
    },

    /**
     * We create a very simple document as a wrapper on the provided Data
     * Interactive URL and open it.
     *
     * @param iURL - URL of data interactive
     * @returns {DG.Document}
     */
    createNewDocumentWithDataInteractiveURL: function (iURL) {
      var tDoc = {
        name: 'DG.Document.defaultDocumentName'.loc(),
        guid: 1,
        components: [
          {
            "type": "DG.GameView",
            "componentStorage": {
              "currentGameName": "",
              "currentGameUrl": iURL
            }
          }
        ],
        appName: DG.APPNAME,
        appVersion: DG.VERSION,
        appBuildNum: DG.BUILD_NUM,
        globalValues: []
      };

      return DG.Document.createDocument(tDoc);
    }

  }
);
<commit_msg>[#162736733] Fix bug introduced: can't open empty document<commit_after>// ==========================================================================
//                        DG.DocumentArchiver
//
//  Author:   Kirk Swenson
//
//  Copyright (c) 2014 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 */
/**
 * @class DocumentArchiver
 *
 * Coordinates streaming of documents and document elements. Deals with documents
 * as streamable entities in contrast to the Document Controller that
 * provides operational coordination of document elements. This class addresses
 * how to get documents in and out of CODAP predictably and the Document
 * Controller deals with Documents while they are being presented and
 * interacted with within CODAP.
 *
 * This class, at this point, has no state. It
 * functions, more or less, as a mixin to the App Controller, providing
 * a logically related collection of utility methods.
 *
 * @extends SC.Object
 */
/* globals Papa: true*/

DG.DocumentArchiver = SC.Object.extend(
/** @scope DG.DocumentArchiver.prototype */ {

    /**
     * Tests the JSON text for validity as a possible document.
     *
     * The following assertions are tested:
     *
     * (1) The document is valid JSON. that is: it parses correctly
     * (2) The document looks like a valid CODAP document. It has all mandatory
     * top level elements and no unexpected top level elements.
     * (3) all internal links can be resolved.
     *
     * @param    {String}    iDocument -- The JSON-formatted document text
     * @returns  {[String]}   An array of error messages, zero length, if none.
     */
    isValidJsonDocument: function (iDocument) {
      function visit(key, value, fn) {
        var rtn = fn(key, value);
        if (rtn !== false) {
          if (Array.isArray(value)) {
            value.forEach(function (item) {
              visit(key, item, fn);
            });
          } else if (typeof value === 'object') {
            DG.ObjectMap.forEach(value, function (key, item) {
              visit(key, item, fn);
            });
          }
        }
      }

      function validateInternalRefs(iDocument) {
        var parts = subDocs.copy(false);
        var symbols = [];
        var references = [];
        parts.unshift(iDocument);
        parts.forEach(function (documentPart) {
          visit('doc', documentPart, function (key, value) {
            if (key === 'guid') {
              // store guids in symbol table
              symbols.push(Number(value));
              return true;
            } else if (key === '_links_') {
              // store links as references
              visit(key, value, function (k, v) {
                if (k === 'id') {
                  references.push(Number(v));
                }
                return true;
              });
              return false;
            } else if (key === 'parent') {
              // store parents as references
              references.push(Number(value));
              return true;
            } else if (key === 'contextStorage') {
            // context storage is private to data interactive
              return false;
            }

          });
        });
        references.forEach(function (ref) {
          // verify link is resolved
          if (symbols.indexOf(Number(ref)) < 0) {
            errors.push('DG.AppController.validateDocument.unresolvedID'.loc(ref));
          }
        });
      }
      var expectedProperties = [
        'appBuildNum',
        'appName',
        'appVersion',
        'changeCount',
        'components',
        'contexts',
        'globalValues',
        'guid',
        'metadata',
        'name',
        '_permissions',
        '_openedFromSharedDocument' // this is an annotation we may create in
                                    // CodapCommonStorage
      ];
      var requiredProperties = [
        'name'
      ];
      var errors = [];
      var doc;
      var subDocs = [];

      if (typeof iDocument === 'string') {
        try {
          doc = JSON.parse(iDocument);
        } catch (ex) {
          errors.push('DG.AppController.validateDocument.parseError'.loc(ex));
        }
      } else {
        doc = iDocument;
      }

      if (doc) {
        requiredProperties.forEach(function (prop) {
            if (!doc.hasOwnProperty(prop)) {
              errors.push('DG.AppController.validateDocument.missingRequiredProperty'.loc(prop));
            }
          }
        );
        DG.ObjectMap.keys(doc).forEach(function (prop) {
            if (expectedProperties.indexOf(prop) < 0) {
              // log unexpected properties but don't fail to open
              DG.log('DG.AppController.validateDocument.unexpectedProperty'.loc(prop));
            }
          }
        );
        validateInternalRefs(doc);
      }
      DG.log('Document validation: ' + (errors.length? JSON.stringify(errors): 'No Errors'));
      return errors;
    },

    /**
     * Converts a string representing the contents of a CSV or tab-delimited
     * file into a simple Streamable CODAP document. This document can be loaded
     * into CODAP. The CODAP document will have one data context with a single
     * Collection that contains the CSV data. It is assumed that either the first
     * line will contain the name of the collection and the second will contain the
     * names of each of the attributes or the collection name will be omitted and
     * the first line will contain the names of the attributes.
     *
     * @param {string} iText The contents of a CSV or tab-delimited file formatted
     *   according to the [rfc4180](http://www.ietf.org/rfc/rfc4180.txt) standard.
   *   @param {string} iContextName name of the context we will create
   *   @param {string} iCollectionName name of the collection we will create
     * @param {string} iFileURL The name of the file or URL from which the
     *   CSV text was extracted.
     * @returns {Object||undefined} A streamable CODAP document.
     */
    convertCSVDataToCODAPDocument: function (iText, iContextName, iCollectionName, iFileURL) {
      /**
       * Returns table stats object:
       *   numRows {number}
       *   maxCols {number}
       *   headerRows {[number]} Array of row indices of header rows
       *   rowLengthCounts {[number]} sparse array of counts of row lengths
       */
      function getTableStats(table) {
        var stats = {
          numRows: 0,
          maxCols: 0,
          rowLengthCounts: [],
          preponderantLength: 0
        };

        stats.numRows = table.length;
        table.forEach(function (row) {
          var len = row.length;
          if (stats.rowLengthCounts[len]) {
            stats.rowLengthCounts[len]++;
          } else {
            stats.rowLengthCounts[len] = 1;
          }
          stats.maxCols = Math.max(stats.maxCols, len);
        });
        var maxCount = 0;
        stats.rowLengthCounts.forEach(function (count, ix) {
          if (maxCount < count) {
            maxCount = count;
            stats.preponderantLength = ix;
          }
        });
        console.log('stats: ' + JSON.stringify(stats));
        return stats;
      }

      function guaranteeUnique(name, nameArray) {
        var ix = 0;
        var newName = name;
        while (nameArray.indexOf(newName) >= 0) {
          ix++;
          newName = name + '_' + ix;
        }
        return newName;
      }

      var tValuesArray,
        tAttrNamesRow,// Column Header Names: should be second row
        tAttrNames = [], tTableStats, ix,
        tDoc = {
          name: 'DG.Document.defaultDocumentName'.loc(),
          guid: 1,
          components: [
            {
              "type": "DG.TableView",
              "componentStorage": {
                "_links_": {
                  "context": {
                    "type": "DG.DataContextRecord",
                    "id": 4
                  }
                }
              }
            },
            {
              "type": "DG.TextView",
              "componentStorage": {
                "text": "url=" + iFileURL,
                "name": "Data Source"
              }
            }
          ],
          contexts: [
            {
              "type": "DG.DataContext",
              "collections": [
                {
                  "attrs": [],
                  "cases": []
                }
              ],
              "contextStorage": {
                "gameUrl": iFileURL
              }
            }
          ],
          appName: DG.APPNAME,
          appVersion: DG.VERSION,
          appBuildNum: DG.BUILD_NUM,
          globalValues: []
        },
        tAttrsArray = tDoc.contexts[0].collections[0].attrs,
        tCasesArray = tDoc.contexts[0].collections[0].cases;

      // a euro number is...
      //                     + optional sign value
      //                     |     + either grouped whole number using dots or spaces for grouping
      //                     |     |                            + or ungrouped whole number
      //                     |     |                            |       + followed optionally by a comma decimal separator
      //                     |     |                            |       |   +git and either decimal digits or
      //                     v     v                            v       v   v        v grouped decimal digits
      var isEuroNumberRE = /^[-+]?(([0-9]{1,3}([. ][0-9]{3})*)|([0-9]*))(,(([0-9]*)|([0-9]{3}[. ])[0-9]{1,3}))?$/;

      var result = Papa.parse(iText, {
        dynamicTyping: false,
        header: false,
        skipEmptyLines: true
      });

      if (result.errors) {
        // Ignore the undetectable delimiter warning, but otherwise log and abort
        if (result.errors.length > 1 ||
            (result.errors.length === 1 &&
                result.errors[0].code !== 'UndetectableDelimiter')) {
          DG.logWarn("CSV Parse errors (%@): %@".loc(iFileURL,
              JSON.stringify(result.errors)));
          return;
        }
      }

      // if we have a csv with semicolon separators we assume are using the EU
      // number representation (comma as the decimal separator), so we convert
      // anything that looks like an EU number to standard representation.
      DG.log("CSV Parse Metadata: %@".loc(JSON.stringify(result.meta)));
      var euroDelimiter = (result.meta && result.meta.delimiter === ';');
      tValuesArray = result.data.map(function (row) {
        return row.map(function (str) {
          if (str === null || str === undefined) {
            return str;
          }
          str = str.trim();
          if (str.length === 0) {
            return str;
          }
          // converts a euro number string to a US number string
          var isEuroNumber = euroDelimiter && isEuroNumberRE.test(str);
          if (isEuroNumber) {
            return Number(str.replace(/[. ]/g, '').replace(/,/, '.'));
          } else {
            if (!isNaN(str)) {
              return Number(str);
            } else {
              return str;
            }
          }
        });
      });

      tTableStats = getTableStats(tValuesArray);

      if (!tValuesArray || tValuesArray.length < 1) {
        DG.logWarn('CSV or Tab-delimited parsing failed: ' + iFileURL);
        return;
      }

      tAttrNamesRow = tValuesArray.shift();

      var tDataContext = tDoc.contexts[0],
          tCollection = tDataContext.collections[0];
      tDataContext.name = tDataContext.title = iContextName;
      tCollection.name = tCollection.title = iCollectionName;

      var canonicalName;
      for (ix = 0; ix < tTableStats.maxCols; ix += 1) {
        canonicalName = DG.Attribute.canonicalizeName(tAttrNamesRow[ix]);
        tAttrNames.push(guaranteeUnique(canonicalName, tAttrNames));
      }

      tAttrNames.forEach(function (iName) {
        tAttrsArray.push( {
          name: iName,
          editable: true
        });
      });

      tValuesArray.forEach( function( iValues) {
        var tCase = {
          values: {}
        };
        tAttrNames.forEach( function( iName, iIndex) {
          var tValue = iValues[ iIndex];
          if( DG.isDateString( tValue)) {
            tValue = DG.createDate( tValue);
          }
          tCase.values[ iName] = DG.DataUtilities.canonicalizeInputValue(tValue);
        });
        tCasesArray.push( tCase);
      });
      return tDoc;
    },

    /**
     * We create a very simple document as a wrapper on the provided Data
     * Interactive URL and open it.
     *
     * @param iURL - URL of data interactive
     * @returns {DG.Document}
     */
    createNewDocumentWithDataInteractiveURL: function (iURL) {
      var tDoc = {
        name: 'DG.Document.defaultDocumentName'.loc(),
        guid: 1,
        components: [
          {
            "type": "DG.GameView",
            "componentStorage": {
              "currentGameName": "",
              "currentGameUrl": iURL
            }
          }
        ],
        appName: DG.APPNAME,
        appVersion: DG.VERSION,
        appBuildNum: DG.BUILD_NUM,
        globalValues: []
      };

      return DG.Document.createDocument(tDoc);
    }

  }
);
<|endoftext|>


<commit_before>define(function(require, exports, module) {
    var UI = require("ui");
    var ContentHelpers = require("content-helpers");
//    var http = require('http');
//    var fs = require('fs');

    return UI.registerAction("convert-video-link", UI.AbstractUIAction.extend({

        defaultConfiguration: function() {
        	console.log('default');

            var config = this.base();

            config.title = "Conversion Finished";
            config.iconClass = "fa fa-plus";
            return config;
        },

        prepareAction: function(actionContext, config, callback) {
        	console.log('prepare');

            actionContext.currentPath = actionContext.observable("path").get();

            callback();
        },

        executeAction: function(actionContext, config, callback) {

            var self = this;
            console.log('execute');
            var project = actionContext.observable("project").get();
            var branch = actionContext.observable("branch").get();
            var document = actionContext.observable("document").get();
            var attachment = document.attach("comment", "text/plain", "Do attachments work?");
            console.log(attachment);

            UI.showPopupModal({
                "title": config.title,
                "body": "Built!"
            }, function(modalDiv) {
                // TODO: add any post-render logic here to manipulate the modal div
            });
        }

    }));
});

define(function(require, exports, module) {

    var UI = require("ui");
    var ContentHelpers = require("content-helpers");

    return UI.registerAction("convert-video-link", UI.AbstractUIAction.extend({

        defaultConfiguration: function() {

            var config = this.base();

            config.title = "Convert a video";
            config.iconClass = "fa fa-plus";
            return config;
        },

        prepareAction: function(actionContext, config, callback) {

            actionContext.currentPath = actionContext.observable("path").get();

            callback();
        },

        executeAction: function(actionContext, config, callback) {

            var self = this;

//            var download = function(url, dest, cb) {
//	              var file = fs.createWriteStream(dest);
//	              var request = http.get(url, function(response) {
//		                response.pipe(file);
//		                file.on('finish', function() {
//		                	file.close(cb);  // close() is async, call cb after close completes.
//		                });
//	              }).on('error', function(err) { // Handle errors
//		                fs.unlink(dest); // Delete the file async. (But we don't check the result)
//		                if (cb) cb(err.message);
//	              });
//            };

//            UI.showModal({
//                "title": config.title,
//                "content": "This is the content for the model",
//                "buttons": [{
//                    "id": "close",
//                    "title": "Close",
//                    "handler": function(e) {
//                    }
//                }],
//                "cancel": true
//            }, function(modalDiv) {
//                // TODO: add any post-render logic here to manipulate the modal div
//            });
        }

    }));
});
<commit_msg>Adds comment creation<commit_after>define(function(require, exports, module) {
    var UI = require("ui");
    var ContentHelpers = require("content-helpers");
//    var http = require('http');
//    var fs = require('fs');

    return UI.registerAction("convert-video-link", UI.AbstractUIAction.extend({

        defaultConfiguration: function() {
        	console.log('default');

            var config = this.base();

            config.title = "Conversion Finished";
            config.iconClass = "fa fa-plus";
            return config;
        },

        prepareAction: function(actionContext, config, callback) {
        	console.log('prepare');

            actionContext.currentPath = actionContext.observable("path").get();

            callback();
        },

        executeAction: function(actionContext, config, callback) {

            var self = this;
            console.log('execute');
            var project = actionContext.observable("project").get();
            var branch = actionContext.observable("branch").get();
            var document = actionContext.observable("document").get();
            var attachment = document.attach("comment", "text/plain", "Do attachments work?");
            console.log(attachment);

            UI.showPopupModal({
                "title": config.title,
                "body": "Built!"
            }, function(modalDiv) {
                // TODO: add any post-render logic here to manipulate the modal div
            });
        }

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


<commit_before>/**
    Licensed to the Apache Software Foundation (ASF) under one
    or more contributor license agreements.  See the NOTICE file
    distributed with this work for additional information
    regarding copyright ownership.  The ASF licenses this file
    to you 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.
*/
var path          = require('path'),
    fs            = require('fs'),
    shell         = require('shelljs'),
    platforms     = require('../platforms'),
    npm           = require('npm'),
    events        = require('./events'),
    request       = require('request'),
    config        = require('./config'),
    hooker        = require('./hooker'),
    https         = require('follow-redirects').https,
    zlib          = require('zlib'),
    tar           = require('tar'),
    URL           = require('url'),
    Q             = require('q'),
    util          = require('./util');

module.exports = {
    // Returns a promise for the path to the lazy-loaded directory.
    cordova:function lazy_load(platform) {
        if (!(platform in platforms)) {
            return Q.reject(new Error('Cordova library "' + platform + '" not recognized.'));
        }

        var url = platforms[platform].url + ';a=snapshot;h=' + platforms[platform].version + ';sf=tgz';
        return module.exports.custom(url, 'cordova', platform, platforms[platform].version);
    },
    // Returns a promise for the path to the lazy-loaded directory.
    custom:function(url, id, platform, version) {
        var download_dir = (platform == 'wp7' || platform == 'wp8' ? path.join(util.libDirectory, 'wp', id, version) :
                                                                     path.join(util.libDirectory, platform, id, version));

        var lib_dir = platforms[platform] && platforms[platform].subdirectory && platform !== "blackberry10" ? path.join(download_dir, platforms[platform].subdirectory) : download_dir;

        if (fs.existsSync(download_dir)) {
            events.emit('verbose', id + ' library for "' + platform + '" already exists. No need to download. Continuing.');
            return Q(lib_dir);
        }
        try {
            // readlinkSync throws if download_dir points to a non-existent file.
            var dangling = fs.readlinkSync(download_dir);
            return Q.reject(download_dir + " points to non-existent or unreadable location: " + dangling);
        } catch (e) {}
        events.emit('log', 'Installing ' + id + ' library for ' + platform + '...');
        return hooker.fire('before_library_download', {
            platform:platform,
            url:url,
            id:id,
            version:version
        }).then(function() {
            var uri = URL.parse(url);
            var d = Q.defer();
            if (uri.protocol && uri.protocol[1] != ':') { // second part of conditional is for awesome windows support. fuuu windows
                npm.load(function() {
                    // Check if NPM proxy settings are set. If so, include them in the request() call.
                    var proxy;
                    if (uri.protocol == 'https:') {
                        proxy = npm.config.get('https-proxy');
                    } else if (uri.protocol == 'http:') {
                        proxy = npm.config.get('proxy');
                    }

                    shell.mkdir('-p', download_dir);
                    var size = 0;
                    var request_options = {uri:url};
                    if (proxy) {
                        request_options.proxy = proxy;
                    }
                    events.emit('verbose', 'Requesting ' + JSON.stringify(request_options) + '...');
                    events.emit('log', 'Downloading ' + id + ' library for ' + platform + '...');
                    var req = request.get(request_options, function(err, res, body) {
                        if (err) {
                            shell.rm('-rf', download_dir);
                            d.reject(err);
                        } else if (res.statusCode != 200) {
                            shell.rm('-rf', download_dir);
                            d.reject(new Error('HTTP error ' + res.statusCode + ' retrieving version ' + version + ' of ' + id + ' for ' + platform));
                        } else {
                            size = body.length;
                        }
                    });

                    req.pipe(zlib.createUnzip())
                    .pipe(tar.Extract({path:download_dir}))
                    .on('error', function(err) {
                        shell.rm('-rf', download_dir);
                        d.reject(err);
                    })
                    .on('end', function() {
                        events.emit('verbose', 'Downloaded, unzipped and extracted ' + size + ' byte response.');
                        events.emit('log', 'Download complete');
                        var entries = fs.readdirSync(download_dir);
                        var entry = path.join(download_dir, entries[0]);
                        shell.mv('-f', path.join(entry, (platform=='blackberry10'?'blackberry10':''), '*'), download_dir);
                        shell.rm('-rf', entry);
                        events.emit('log', 'Installing ' + id + ' library for ' + platform + '...');
                        d.resolve(hooker.fire('after_library_download', {
                            platform:platform,
                            url:url,
                            id:id,
                            version:version,
                            path: lib_dir,
                            size:size,
                            symlink:false
                        }));
                    });
                });
            } else {
                // Local path.
                // Do nothing here; users of this code should be using the returned path.
                download_dir = uri.protocol && uri.protocol[1] == ':' ? uri.href : uri.path;
                lib_dir = platforms[platform] && platforms[platform].subdirectory ? path.join(download_dir, platforms[platform].subdirectory) : download_dir;

                events.emit('log', 'Installing ' + id + ' library for ' + platform + '...');
                d.resolve(hooker.fire('after_library_download', {
                    platform:platform,
                    url:url,
                    id:id,
                    version:version,
                    path: lib_dir,
                    symlink:false
                }));
            }
            return d.promise.then(function () { return lib_dir; });
        });
    },
    // Returns a promise for the path to the lazy-loaded directory.
    based_on_config:function(project_root, platform) {
        var custom_path = config.has_custom_path(project_root, platform);
        if (custom_path) {
            var dot_file = config.read(project_root);
            return module.exports.custom(dot_file.lib[platform].uri, dot_file.lib[platform].id, platform, dot_file.lib[platform].version);
        } else {
            return module.exports.cordova(platform);
        }
    }
};
<commit_msg>CB-4976 dont use cache for local directory<commit_after>/**
    Licensed to the Apache Software Foundation (ASF) under one
    or more contributor license agreements.  See the NOTICE file
    distributed with this work for additional information
    regarding copyright ownership.  The ASF licenses this file
    to you 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.
*/
var path          = require('path'),
    fs            = require('fs'),
    shell         = require('shelljs'),
    platforms     = require('../platforms'),
    npm           = require('npm'),
    events        = require('./events'),
    request       = require('request'),
    config        = require('./config'),
    hooker        = require('./hooker'),
    https         = require('follow-redirects').https,
    zlib          = require('zlib'),
    tar           = require('tar'),
    URL           = require('url'),
    Q             = require('q'),
    util          = require('./util');

module.exports = {
    // Returns a promise for the path to the lazy-loaded directory.
    cordova:function lazy_load(platform) {
        if (!(platform in platforms)) {
            return Q.reject(new Error('Cordova library "' + platform + '" not recognized.'));
        }

        var url = platforms[platform].url + ';a=snapshot;h=' + platforms[platform].version + ';sf=tgz';
        return module.exports.custom(url, 'cordova', platform, platforms[platform].version);
    },
    // Returns a promise for the path to the lazy-loaded directory.
    custom:function(url, id, platform, version) {
        var download_dir = (platform == 'wp7' || platform == 'wp8' ? path.join(util.libDirectory, 'wp', id, version) :
                                                                     path.join(util.libDirectory, platform, id, version));

        var lib_dir = platforms[platform] && platforms[platform].subdirectory && platform !== "blackberry10" ? path.join(download_dir, platforms[platform].subdirectory) : download_dir;

        // For remote resources and archives this will download and/or unpack and cache them in the .cordova dir.
        // In the event that the custom url is a local dir the cache is not used and we must not return here. The later logic will set things appropriately.
        var uri = URL.parse(url);
        if (uri.protocol && uri.protocol[1] != ':') { // second part of conditional is for awesome windows support. fuuu windows
          if (fs.existsSync(download_dir)) {
            events.emit('verbose', id + ' library for "' + platform + '" already exists. No need to download. Continuing.');
            return Q(lib_dir);
          }
        }
        try {
            // readlinkSync throws if download_dir points to a non-existent file.
            var dangling = fs.readlinkSync(download_dir);
            return Q.reject(download_dir + " points to non-existent or unreadable location: " + dangling);
        } catch (e) {}
        events.emit('log', 'Installing ' + id + ' library for ' + platform + '...');
        return hooker.fire('before_library_download', {
            platform:platform,
            url:url,
            id:id,
            version:version
        }).then(function() {
            var uri = URL.parse(url);
            var d = Q.defer();
            if (uri.protocol && uri.protocol[1] != ':') { // second part of conditional is for awesome windows support. fuuu windows
                npm.load(function() {
                    // Check if NPM proxy settings are set. If so, include them in the request() call.
                    var proxy;
                    if (uri.protocol == 'https:') {
                        proxy = npm.config.get('https-proxy');
                    } else if (uri.protocol == 'http:') {
                        proxy = npm.config.get('proxy');
                    }

                    shell.mkdir('-p', download_dir);
                    var size = 0;
                    var request_options = {uri:url};
                    if (proxy) {
                        request_options.proxy = proxy;
                    }
                    events.emit('verbose', 'Requesting ' + JSON.stringify(request_options) + '...');
                    events.emit('log', 'Downloading ' + id + ' library for ' + platform + '...');
                    var req = request.get(request_options, function(err, res, body) {
                        if (err) {
                            shell.rm('-rf', download_dir);
                            d.reject(err);
                        } else if (res.statusCode != 200) {
                            shell.rm('-rf', download_dir);
                            d.reject(new Error('HTTP error ' + res.statusCode + ' retrieving version ' + version + ' of ' + id + ' for ' + platform));
                        } else {
                            size = body.length;
                        }
                    });

                    req.pipe(zlib.createUnzip())
                    .pipe(tar.Extract({path:download_dir}))
                    .on('error', function(err) {
                        shell.rm('-rf', download_dir);
                        d.reject(err);
                    })
                    .on('end', function() {
                        events.emit('verbose', 'Downloaded, unzipped and extracted ' + size + ' byte response.');
                        events.emit('log', 'Download complete');
                        var entries = fs.readdirSync(download_dir);
                        var entry = path.join(download_dir, entries[0]);
                        shell.mv('-f', path.join(entry, (platform=='blackberry10'?'blackberry10':''), '*'), download_dir);
                        shell.rm('-rf', entry);
                        events.emit('log', 'Installing ' + id + ' library for ' + platform + '...');
                        d.resolve(hooker.fire('after_library_download', {
                            platform:platform,
                            url:url,
                            id:id,
                            version:version,
                            path: lib_dir,
                            size:size,
                            symlink:false
                        }));
                    });
                });
            } else {
                // Local path.
                // Do nothing here; users of this code should be using the returned path.
                download_dir = uri.protocol && uri.protocol[1] == ':' ? uri.href : uri.path;
                lib_dir = platforms[platform] && platforms[platform].subdirectory ? path.join(download_dir, platforms[platform].subdirectory) : download_dir;

                events.emit('log', 'Installing ' + id + ' library for ' + platform + '...');
                d.resolve(hooker.fire('after_library_download', {
                    platform:platform,
                    url:url,
                    id:id,
                    version:version,
                    path: lib_dir,
                    symlink:false
                }));
            }
            return d.promise.then(function () { return lib_dir; });
        });
    },
    // Returns a promise for the path to the lazy-loaded directory.
    based_on_config:function(project_root, platform) {
        var custom_path = config.has_custom_path(project_root, platform);
        if (custom_path) {
            var dot_file = config.read(project_root);
            return module.exports.custom(dot_file.lib[platform].uri, dot_file.lib[platform].id, platform, dot_file.lib[platform].version);
        } else {
            return module.exports.cordova(platform);
        }
    }
};
<|endoftext|>


<commit_before>// JavaScript source code
(function () {
    angular.module('bsTable', [])
   .directive('bsTableControl', function () {
       return {
           restrict: 'EA',
           scope: {
               options: '='
           },
           link: function (scope, element, attr) {
               $(element).bootstrapTable(scope.options);
               setInterval(function () { $(element).bootstrapTable('resetView'); }, 500)
           }
       };
   })
})();
<commit_msg>update bsTable.js<commit_after>// JavaScript source code
(function () {
    angular.module('bsTable', [])
   .directive('bsTableControl', function () {
       return {
           restrict: 'EA',
           scope: {
               options: '='
           },
           link: function (scope, element, attr) {
               var tableCreated = false;
               scope.$watch('options', function (newValue, oldValue) {
                   if (tableCreated && newValue === oldValue) return;
                   $(element).bootstrapTable('destroy');
                   if (newValue) {
                       $(element).bootstrapTable(scope.options);
                   }
                   tableCreated = typeof (newValue) !== 'undefined';
               });
               $(window).resize(function () {
                   if (tableCreated)
                       $(element).bootstrapTable('resetView');
               })
           }
       };
   })
})();
<|endoftext|>


<commit_before>/*    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/>.
 */
'use strict';

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

const Intel = require('./Intel.js');
const rp = require('request-promise');

const iptool = require('ip');

class Cymon2Intel extends Intel {

  async enrichAlarm(alarm) {
    const ip = alarm["p.dest.ip"];

    if(ip) {
      const result = await this.loadFromCymon(ip);
      if(result["total"] !== undefined) {
        alarm["p.security.numOfReportSources2"] = result["total"];
      }
    }

    return alarm;
  }

  async loadFromCymon(ip) {

    if(!ip) {
      return;
    }

    if(!iptool.isV4Format(ip)) { // only support v4 yet..
      return;
    }

    const uri = `https://api.cymon.io/v2/ioc/search/ip/${ip}`

    const body = await rp({
      uri: uri,
      method: 'GET',
      family: 4,
      json: true,
      timeout: 10000 //ms
    }).catch((err) => {
      log.error("Failed to request info from cymon, err:", err);
      return null;
    });

    if(body) {
      try {
        const json = JSON.parse(body);

        return json;

      } catch(err) {
        log.error("Failed to parse json output:", body, "err:", err);
      }

    }

    return null;
  }

}

module.exports = Cymon2Intel;
<commit_msg>bug fix<commit_after>/*    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/>.
 */
'use strict';

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

const Intel = require('./Intel.js');
const rp = require('request-promise');

const iptool = require('ip');

class Cymon2Intel extends Intel {

  async enrichAlarm(alarm) {
    const ip = alarm["p.dest.ip"];

    if(ip) {
      const result = await this.loadFromCymon(ip);
      if(result["total"] !== undefined) {
        alarm["p.security.numOfReportSources2"] = result["total"];
      }
    }

    return alarm;
  }

  async loadFromCymon(ip) {

    if(!ip) {
      return;
    }

    if(!iptool.isV4Format(ip)) { // only support v4 yet..
      return;
    }

    const uri = `https://api.cymon.io/v2/ioc/search/ip/${ip}`

    const body = await rp({
      uri: uri,
      method: 'GET',
      family: 4,
      json: true,
      timeout: 10000 //ms
    }).catch((err) => {
      log.error("Failed to request info from cymon, err:", err);
      return null;
    });

    return body;
  }

}

module.exports = Cymon2Intel;
<|endoftext|>


<commit_before>import React from 'react';
import { storiesOf } from '@storybook/react';
import {
  text, boolean, select, button
} from '@storybook/addon-knobs';
import { State, Store } from '@sambego/storybook-state';
import { action } from '@storybook/addon-actions';
import OptionsHelper from '../../utils/helpers/options-helper';
import { notes, info } from './documentation';
import Confirm from './confirm.js';

const store = new Store({
  open: false
});
const handleCancel = () => {
  action('cancel')();
  store.set({ open: false });
};
const handleOpen = () => {
  action('open')();
  store.set({ open: true });
};

storiesOf('Confirm', module)
  .addParameters({
    info: {
      propTablesExclude: [State]
    }
  })
  .add('default', () => {
    button('open', handleOpen);
    const children = text('children', 'This is an example of a confirm.');
    const title = text('title', 'Are you sure?');
    const enableBackgroundUI = boolean('enableBackgroundUI', false);
    const disableEscKey = boolean('disableEscKey', false);
    const ariaRole = text('ariaRole', Confirm.defaultProps.ariaRole);
    const height = text('height', '');
    const subtitle = text('subtitle', '');
    const size = select('size', OptionsHelper.sizesFull, Confirm.defaultProps.size);
    const showCloseIcon = boolean('showCloseIcon', Confirm.defaultProps.showCloseIcon);
    const autoFocus = boolean('autoFocus', Confirm.defaultProps.autoFocus);
    const stickyFormFooter = boolean('stickyFormFooter', false);
    const confirmLabel = text('confirmLabel', '');
    const cancelLabel = text('cancelLabel', '');

    return (
      <State store={ store }>
        <Confirm
          title={ title }
          open={ store.get('open') }
          enableBackgroundUI={ enableBackgroundUI }
          disableEscKey={ disableEscKey }
          ariaRole={ ariaRole }
          height={ height }
          subtitle={ subtitle }
          size={ size }
          showCloseIcon={ showCloseIcon }
          autoFocus={ autoFocus }
          stickyFormFooter={ stickyFormFooter }
          confirmLabel={ confirmLabel }
          cancelLabel={ cancelLabel }
          onConfirm={ action('confirm') }
          onCancel={ handleCancel }
        >
          { children }
        </Confirm>
      </State>
    );
  }, {
    info: { text: info },
    notes: { markdown: notes }
  });
<commit_msg>adding data-element attribute to confirm dialog<commit_after>import React from 'react';
import { storiesOf } from '@storybook/react';
import {
  text, boolean, select, button
} from '@storybook/addon-knobs';
import { State, Store } from '@sambego/storybook-state';
import { action } from '@storybook/addon-actions';
import OptionsHelper from '../../utils/helpers/options-helper';
import { notes, info } from './documentation';
import Confirm from './confirm.js';

const store = new Store({
  open: false
});
const handleCancel = () => {
  action('cancel')();
  store.set({ open: false });
};
const handleOpen = () => {
  action('open')();
  store.set({ open: true });
};

storiesOf('Confirm', module)
  .addParameters({
    info: {
      propTablesExclude: [State]
    }
  })
  .add('default', () => {
    button('open', handleOpen);
    const children = This is an example of a confirm.;
    const title = text('title', 'Are you sure?');
    const enableBackgroundUI = boolean('enableBackgroundUI', false);
    const disableEscKey = boolean('disableEscKey', false);
    const ariaRole = text('ariaRole', Confirm.defaultProps.ariaRole);
    const height = text('height', '');
    const subtitle = text('subtitle', '');
    const size = select('size', OptionsHelper.sizesFull, Confirm.defaultProps.size);
    const showCloseIcon = boolean('showCloseIcon', Confirm.defaultProps.showCloseIcon);
    const autoFocus = boolean('autoFocus', Confirm.defaultProps.autoFocus);
    const stickyFormFooter = boolean('stickyFormFooter', false);
    const confirmLabel = text('confirmLabel', '');
    const cancelLabel = text('cancelLabel', '');

    return (
      <State store={ store }>
        <Confirm
          title={ title }
          open={ store.get('open') }
          enableBackgroundUI={ enableBackgroundUI }
          disableEscKey={ disableEscKey }
          ariaRole={ ariaRole }
          height={ height }
          subtitle={ subtitle }
          size={ size }
          showCloseIcon={ showCloseIcon }
          autoFocus={ autoFocus }
          stickyFormFooter={ stickyFormFooter }
          confirmLabel={ confirmLabel }
          cancelLabel={ cancelLabel }
          onConfirm={ action('confirm') }
          onCancel={ handleCancel }
        >
          { children }
        </Confirm>
      </State>
    );
  }, {
    info: { text: info },
    notes: { markdown: notes }
  });
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
// @flow

import React, { PureComponent } from 'react';
import classNames from 'classnames';
import { timeCode } from '../../utils/time-code';
import { getSampleCallNodes } from '../../profile-logic/profile-data';
import { BLUE_70, BLUE_40 } from 'photon-colors';

import type { Thread } from '../../types/profile';
import type { Milliseconds } from '../../types/units';
import type {
  CallNodeInfo,
  IndexIntoCallNodeTable,
} from '../../types/profile-derived';

type Props = {|
  +thread: Thread,
  +interval: Milliseconds,
  +rangeStart: Milliseconds,
  +rangeEnd: Milliseconds,
  +callNodeInfo: CallNodeInfo,
  +selectedCallNodeIndex: IndexIntoCallNodeTable | null,
  +className: string,
  +onStackClick: (time: Milliseconds) => void,
|};

class ThreadStackGraph extends PureComponent<Props> {
  _canvas: null | HTMLCanvasElement;
  _requestedAnimationFrame: boolean;
  _resizeListener: () => void;
  _takeCanvasRef = (canvas: HTMLCanvasElement | null) =>
    (this._canvas = canvas);

  constructor(props: Props) {
    super(props);
    this._resizeListener = () => this.forceUpdate();
    this._requestedAnimationFrame = false;
    this._canvas = null;
  }

  _scheduleDraw() {
    if (!this._requestedAnimationFrame) {
      this._requestedAnimationFrame = true;
      window.requestAnimationFrame(() => {
        this._requestedAnimationFrame = false;
        const canvas = this._canvas;
        if (canvas) {
          timeCode('ThreadStackGraph render', () => {
            this.drawCanvas(canvas);
          });
        }
      });
    }
  }

  componentDidMount() {
    window.addEventListener('resize', this._resizeListener);
    this.forceUpdate(); // for initial size
  }

  componentWillUnmount() {
    window.removeEventListener('resize', this._resizeListener);
  }

  drawCanvas(canvas: HTMLCanvasElement) {
    const {
      thread,
      interval,
      rangeStart,
      rangeEnd,
      callNodeInfo,
      selectedCallNodeIndex,
    } = this.props;

    const devicePixelRatio = canvas.ownerDocument
      ? canvas.ownerDocument.defaultView.devicePixelRatio
      : 1;
    const rect = canvas.getBoundingClientRect();
    canvas.width = Math.round(rect.width * devicePixelRatio);
    canvas.height = Math.round(rect.height * devicePixelRatio);
    const ctx = canvas.getContext('2d');
    let maxDepth = 0;
    const { callNodeTable, stackIndexToCallNodeIndex } = callNodeInfo;
    const sampleCallNodes = getSampleCallNodes(
      thread.samples,
      stackIndexToCallNodeIndex
    );
    for (let i = 0; i < callNodeTable.depth.length; i++) {
      if (callNodeTable.depth[i] > maxDepth) {
        maxDepth = callNodeTable.depth[i];
      }
    }
    const range = [rangeStart, rangeEnd];
    const rangeLength = range[1] - range[0];
    const xPixelsPerMs = canvas.width / rangeLength;
    const yPixelsPerDepth = canvas.height / maxDepth;
    const trueIntervalPixelWidth = interval * xPixelsPerMs;
    const multiplier = trueIntervalPixelWidth < 2.0 ? 1.2 : 1.0;
    const drawnIntervalWidth = Math.max(
      0.8,
      trueIntervalPixelWidth * multiplier
    );
    let selectedCallNodeDepth = 0;
    if (selectedCallNodeIndex !== -1 && selectedCallNodeIndex !== null) {
      selectedCallNodeDepth = callNodeTable.depth[selectedCallNodeIndex];
    }
    function hasSelectedCallNodePrefix(callNodePrefix) {
      let callNodeIndex = callNodePrefix;
      if (callNodeIndex === null) {
        return false;
      }
      for (
        let depth = callNodeTable.depth[callNodeIndex];
        depth > selectedCallNodeDepth;
        depth--
      ) {
        callNodeIndex = callNodeTable.prefix[callNodeIndex];
      }
      return callNodeIndex === selectedCallNodeIndex;
    }

    // Draw all of the samples
    for (let i = 0; i < sampleCallNodes.length; i++) {
      const sampleTime = thread.samples.time[i];
      if (
        sampleTime + drawnIntervalWidth / xPixelsPerMs < range[0] ||
        sampleTime > range[1]
      ) {
        continue;
      }
      const callNodeIndex = sampleCallNodes[i];
      if (callNodeIndex === null) {
        continue;
      }
      const isHighlighted = hasSelectedCallNodePrefix(callNodeIndex);
      const sampleHeight = callNodeTable.depth[callNodeIndex] * yPixelsPerDepth;
      const startY = canvas.height - sampleHeight;
      // const responsiveness = thread.samples.responsiveness[i];
      // const jankSeverity = Math.min(1, responsiveness / 100);
      ctx.fillStyle = isHighlighted ? BLUE_70 : BLUE_40;
      ctx.fillRect(
        (sampleTime - range[0]) * xPixelsPerMs,
        startY,
        drawnIntervalWidth,
        sampleHeight
      );
    }
  }

  _onMouseUp = (e: SyntheticMouseEvent<>) => {
    const canvas = this._canvas;
    if (canvas) {
      const { rangeStart, rangeEnd } = this.props;
      const r = canvas.getBoundingClientRect();

      const x = e.pageX - r.left;
      const time = rangeStart + x / r.width * (rangeEnd - rangeStart);
      this.props.onStackClick(time);
    }
  };

  render() {
    this._scheduleDraw();
    return (

        <canvas
          className={classNames(
            `${this.props.className}Canvas`,
            'threadStackGraphCanvas'
          )}
          ref={this._takeCanvasRef}
          onMouseUp={this._onMouseUp}
        />

    );
  }
}

export default ThreadStackGraph;
<commit_msg>Draw the ThreadStackGraph more efficiently.<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
// @flow

import React, { PureComponent } from 'react';
import classNames from 'classnames';
import bisection from 'bisection';
import { timeCode } from '../../utils/time-code';
import { getSampleCallNodes } from '../../profile-logic/profile-data';
import { BLUE_70, BLUE_40 } from 'photon-colors';

import type { Thread } from '../../types/profile';
import type { Milliseconds } from '../../types/units';
import type {
  CallNodeInfo,
  IndexIntoCallNodeTable,
} from '../../types/profile-derived';

type Props = {|
  +thread: Thread,
  +interval: Milliseconds,
  +rangeStart: Milliseconds,
  +rangeEnd: Milliseconds,
  +callNodeInfo: CallNodeInfo,
  +selectedCallNodeIndex: IndexIntoCallNodeTable | null,
  +className: string,
  +onStackClick: (time: Milliseconds) => void,
|};

class ThreadStackGraph extends PureComponent<Props> {
  _canvas: null | HTMLCanvasElement;
  _requestedAnimationFrame: boolean;
  _resizeListener: () => void;
  _takeCanvasRef = (canvas: HTMLCanvasElement | null) =>
    (this._canvas = canvas);

  constructor(props: Props) {
    super(props);
    this._resizeListener = () => this.forceUpdate();
    this._requestedAnimationFrame = false;
    this._canvas = null;
  }

  _scheduleDraw() {
    if (!this._requestedAnimationFrame) {
      this._requestedAnimationFrame = true;
      window.requestAnimationFrame(() => {
        this._requestedAnimationFrame = false;
        const canvas = this._canvas;
        if (canvas) {
          timeCode('ThreadStackGraph render', () => {
            this.drawCanvas(canvas);
          });
        }
      });
    }
  }

  componentDidMount() {
    window.addEventListener('resize', this._resizeListener);
    this.forceUpdate(); // for initial size
  }

  componentWillUnmount() {
    window.removeEventListener('resize', this._resizeListener);
  }

  drawCanvas(canvas: HTMLCanvasElement) {
    const {
      thread,
      interval,
      rangeStart,
      rangeEnd,
      callNodeInfo,
      selectedCallNodeIndex,
    } = this.props;

    const devicePixelRatio = canvas.ownerDocument
      ? canvas.ownerDocument.defaultView.devicePixelRatio
      : 1;
    const rect = canvas.getBoundingClientRect();
    canvas.width = Math.round(rect.width * devicePixelRatio);
    canvas.height = Math.round(rect.height * devicePixelRatio);
    const ctx = canvas.getContext('2d');
    let maxDepth = 0;
    const { callNodeTable, stackIndexToCallNodeIndex } = callNodeInfo;
    const sampleCallNodes = getSampleCallNodes(
      thread.samples,
      stackIndexToCallNodeIndex
    );
    for (let i = 0; i < callNodeTable.depth.length; i++) {
      if (callNodeTable.depth[i] > maxDepth) {
        maxDepth = callNodeTable.depth[i];
      }
    }
    const range = [rangeStart, rangeEnd];
    const rangeLength = range[1] - range[0];
    const xPixelsPerMs = canvas.width / rangeLength;
    const yPixelsPerDepth = canvas.height / maxDepth;
    const trueIntervalPixelWidth = interval * xPixelsPerMs;
    const multiplier = trueIntervalPixelWidth < 2.0 ? 1.2 : 1.0;
    const drawnIntervalWidth = Math.max(
      0.8,
      trueIntervalPixelWidth * multiplier
    );
    let selectedCallNodeDepth = 0;
    if (selectedCallNodeIndex !== -1 && selectedCallNodeIndex !== null) {
      selectedCallNodeDepth = callNodeTable.depth[selectedCallNodeIndex];
    }
    function hasSelectedCallNodePrefix(callNodePrefix) {
      let callNodeIndex = callNodePrefix;
      if (callNodeIndex === null) {
        return false;
      }
      for (
        let depth = callNodeTable.depth[callNodeIndex];
        depth > selectedCallNodeDepth;
        depth--
      ) {
        callNodeIndex = callNodeTable.prefix[callNodeIndex];
      }
      return callNodeIndex === selectedCallNodeIndex;
    }

    const firstDrawnSampleTime = range[0] - drawnIntervalWidth / xPixelsPerMs;
    const lastDrawnSampleTime = range[1];

    const firstDrawnSampleIndex = bisection.right(
      thread.samples.time,
      firstDrawnSampleTime
    );
    const afterLastDrawnSampleIndex = bisection.right(
      thread.samples.time,
      lastDrawnSampleTime,
      firstDrawnSampleIndex
    );

    // Do one pass over the samples array to gather the samples we want to draw.
    const regularSamples = {
      height: [],
      xPos: [],
    };
    const highlightedSamples = {
      height: [],
      xPos: [],
    };
    // Enforce a minimum distance so that we don't draw more than 4 samples per
    // pixel.
    const minGapMs = 0.25 / xPixelsPerMs;
    let nextMinTime = -Infinity;
    for (let i = firstDrawnSampleIndex; i < afterLastDrawnSampleIndex; i++) {
      const sampleTime = thread.samples.time[i];
      if (sampleTime < nextMinTime) {
        continue;
      }
      const callNodeIndex = sampleCallNodes[i];
      if (callNodeIndex === null) {
        continue;
      }
      const height = callNodeTable.depth[callNodeIndex] * yPixelsPerDepth;
      const xPos = (sampleTime - range[0]) * xPixelsPerMs;
      if (hasSelectedCallNodePrefix(callNodeIndex)) {
        highlightedSamples.height.push(height);
        highlightedSamples.xPos.push(xPos);
      } else {
        regularSamples.height.push(height);
        regularSamples.xPos.push(xPos);
      }
      nextMinTime = sampleTime + minGapMs;
    }

    // Draw the regular samples first, and then the highlighted samples.
    // This means that we only set ctx.fillStyle twice, which saves on time
    // that's spent parsing color strings.
    ctx.fillStyle = BLUE_40;
    for (let i = 0; i < regularSamples.height.length; i++) {
      const height = regularSamples.height[i];
      const startY = canvas.height - height;
      const xPos = regularSamples.xPos[i];
      ctx.fillRect(xPos, startY, drawnIntervalWidth, height);
    }
    ctx.fillStyle = BLUE_70;
    for (let i = 0; i < highlightedSamples.height.length; i++) {
      const height = highlightedSamples.height[i];
      const startY = canvas.height - height;
      const xPos = highlightedSamples.xPos[i];
      ctx.fillRect(xPos, startY, drawnIntervalWidth, height);
    }
  }

  _onMouseUp = (e: SyntheticMouseEvent<>) => {
    const canvas = this._canvas;
    if (canvas) {
      const { rangeStart, rangeEnd } = this.props;
      const r = canvas.getBoundingClientRect();

      const x = e.pageX - r.left;
      const time = rangeStart + x / r.width * (rangeEnd - rangeStart);
      this.props.onStackClick(time);
    }
  };

  render() {
    this._scheduleDraw();
    return (

        <canvas
          className={classNames(
            `${this.props.className}Canvas`,
            'threadStackGraphCanvas'
          )}
          ref={this._takeCanvasRef}
          onMouseUp={this._onMouseUp}
        />

    );
  }
}

export default ThreadStackGraph;
<|endoftext|>


<commit_before>/* get interger range bound */
var getRangeBound = function (min, max, splitNumber) {
  var temp1 = min;
  var temp2 = max;
  min = Math.min(temp1, temp2);
  max = Math.max(temp1, temp2);
  var getSplitRange = function (min, max, splitNumber) {
    //max >= min >= 0
    //splitNumber should better be 2 - 10;
    splitNumber = Math.round(Math.max(2, splitNumber));
    if (max === min) {
      if (max === 0) {
        max = 1;
      } else {
        max = max * 1.1;
      }
    }
    var multiply = Math.pow(10, Math.floor(Math.log(max - min) / Math.log(10)));
    var range = (max - min) / multiply * 10; //[10, 100)
    var split = [1, 2, 4, 5, 10, 20, 40, 50, 100];
    var splitBound = [1, 2, 3, 5, 10, 20, 30, 50, 100];
    var sRange = range / (splitNumber - 1); // sRange is larger than need.
    var i, l;
    for (i = 0, l = split.length - 1; i < l; i++) {
      if (sRange <= splitBound[i]) {
        sRange = split[i];
        break;
      }
    }
    sRange = sRange * multiply / 10;
    return sRange;
  };
  var getRange = function (min, max, sRange, splitNumber) {
    splitNumber = Math.max(2, splitNumber);
    var rmin = Math.floor(min / sRange) * sRange;
    var rmax = rmin + sRange * splitNumber;

    // make range center;
    var downSplit = Math.floor(((rmax - max) / sRange) / 2);
    rmin -= downSplit * sRange;
    if (rmin < 0) {
      rmin = 0;
    }
    rmax = rmin + sRange * splitNumber;
    return [rmin, rmax];
  }
  if (min >= 0) {
    var sRange = getSplitRange(min, max, splitNumber);
    var range = getRange(min, max, sRange, splitNumber);
  } else if (max <=0) {
    var sRange = getSplitRange(-max, -min, splitNumber);
    var range = getRange(-max, -min, sRange, splitNumber);
    range = [-range[1], -range[0]];
  } else {
    //max > 0 and min < 0
    var span = max - min;
    var sRange = getSplitRange(0, span, splitNumber);
    var range = getRange(0, span, sRange, splitNumber);
    var rmin = Math.floor(min / sRange) * sRange;
    range = [rmin, rmin + (range[1] - range[0])];
  }
  return range;
};

/* get interger range ticks */
var getRangeTicks = function (min, max, splits) {
  var b = getRangeBound(min, max, splits);
  var splitValue = (b[1] - b[0]) / splits;
  var i;
  var r = [];
  for (i = 0; i <= splits; i++) {
    r.push(b[0] + i * splitValue);
  }
  return r;
};

/* try different split, to get smallest interger range */
var getDynamicSplit = function (min, max, defaultSplits) {
  var s = defaultSplits || 4;
  var r;
  var i, l;
  for (i = -1, l = 1; i <= l; i++) {
    var t = {
      range: getRangeBound(min, max, s + i),
      splitNumber: s + i,
      rangeTicks: undefined
    };
    if (typeof r === 'undefined') {
      r = t;
    } else {
      if (t.range[1] - t.range[0] < r.range[1] - r.range[0]) {
        r = t;
      }
    }
  }
  r.rangeTicks = getRangeTicks(min, max, r.splitNumber);
  return r;
};
<commit_msg>Update integer js<commit_after>/* get integer range bound */
var getRangeBound = function (min, max, splitNumber) {
  var temp1 = min;
  var temp2 = max;
  min = Math.min(temp1, temp2);
  max = Math.max(temp1, temp2);
  var getSplitRange = function (min, max, splitNumber) {
    //max >= min >= 0
    //splitNumber should better be 2 - 10;
    splitNumber = Math.round(Math.max(2, splitNumber));
    if (max === min) {
      if (max === 0) {
        max = 1;
      } else {
        max = max * 1.1;
      }
    }
    var multiply = Math.pow(10, Math.floor(Math.log(max - min) / Math.log(10)));
    var range = (max - min) / multiply * 10; //[10, 100)
    var split = [1, 2, 4, 5, 10, 20, 40, 50, 100];
    var splitBound = [1, 2, 3, 5, 10, 20, 30, 50, 100];
    var sRange = range / (splitNumber - 1); // sRange is larger than need.
    var i, l;
    for (i = 0, l = split.length - 1; i < l; i++) {
      if (sRange <= splitBound[i]) {
        sRange = split[i];
        break;
      }
    }
    sRange = sRange * multiply / 10;
    return sRange;
  };
  var getRange = function (min, max, sRange, splitNumber) {
    splitNumber = Math.max(2, splitNumber);
    var rmin = Math.floor(min / sRange) * sRange;
    var rmax = rmin + sRange * splitNumber;

    // make range center;
    var downSplit = Math.floor(((rmax - max) / sRange) / 2);
    rmin -= downSplit * sRange;
    if (rmin < 0) {
      rmin = 0;
    }
    rmax = rmin + sRange * splitNumber;
    return [rmin, rmax];
  }
  if (min >= 0) {
    var sRange = getSplitRange(min, max, splitNumber);
    var range = getRange(min, max, sRange, splitNumber);
  } else if (max <=0) {
    var sRange = getSplitRange(-max, -min, splitNumber);
    var range = getRange(-max, -min, sRange, splitNumber);
    range = [-range[1], -range[0]];
  } else {
    //max > 0 and min < 0
    var span = max - min;
    var sRange = getSplitRange(0, span, splitNumber);
    var range = getRange(0, span, sRange, splitNumber);
    var rmin = Math.floor(min / sRange) * sRange;
    range = [rmin, rmin + (range[1] - range[0])];
  }
  return range;
};

/* get integer range ticks */
var getRangeTicks = function (min, max, splits) {
  var b = getRangeBound(min, max, splits);
  var splitValue = (b[1] - b[0]) / splits;
  var i;
  var r = [];
  for (i = 0; i <= splits; i++) {
    r.push(b[0] + i * splitValue);
  }
  return r;
};

/* try different split, to get smallest interger range */
var getDynamicSplit = function (min, max, defaultSplits) {
  var s = defaultSplits || 4;
  var r;
  var i, l;
  for (i = -1, l = 1; i <= l; i++) {
    var t = {
      range: getRangeBound(min, max, s + i),
      splitNumber: s + i,
      rangeTicks: undefined
    };
    if (typeof r === 'undefined') {
      r = t;
    } else {
      if (t.range[1] - t.range[0] < r.range[1] - r.range[0]) {
        r = t;
      }
    }
  }
  r.rangeTicks = getRangeTicks(min, max, r.splitNumber);
  return r;
};
<|endoftext|>


<commit_before>/**
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements. See the NOTICE file distributed with this
 * work for additional information regarding copyright ownership. The ASF
 * licenses this file to you 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.
 */

var App = require('app');
var dataManipulation = require('utils/data_manipulation');

App.alertDefinitionSummaryMapper = App.QuickDataMapper.create({

  config: {},

  map: function(data) {
    console.time('App.alertDefinitionSummaryMapper execution time');

    if (!data.alerts_summary_grouped) return;
    var alertDefinitions = App.AlertDefinition.find();
    var alertDefinitionsMap = alertDefinitions.toArray().toMapByProperty('id');
    var summaryMap = {};
    data.alerts_summary_grouped.forEach(function(alertDefinitionSummary) {
      var alertDefinition = alertDefinitionsMap[alertDefinitionSummary.definition_id];
      if (alertDefinition) {
        var summary = {},
          timestamp = 0;
        Em.keys(alertDefinitionSummary.summary).forEach(function(status) {
          summary[status] = {
            count: alertDefinitionSummary.summary[status].count,
            maintenanceCount: alertDefinitionSummary.summary[status].maintenance_count
          };
          if (alertDefinitionSummary.summary[status].latest_text) {
            summary[status].latestText = alertDefinitionSummary.summary[status].latest_text;
          }
          if (alertDefinitionSummary.summary[status].original_timestamp > timestamp) {
            timestamp = alertDefinitionSummary.summary[status].original_timestamp;
          }
        });
        summaryMap[alertDefinitionSummary.definition_id] = {
          summary: summary,
          lastTriggered: App.dateTimeWithTimeZone(parseInt(timestamp)),
          lastTriggeredRaw: timestamp
        };
      }
    });

    alertDefinitions.forEach(function (d) {
      var id = d.get('id');
      if ((alertDefinitionsMap[id].get('stateManager.currentState.name') !== 'saved')) {
        alertDefinitionsMap[id].get('stateManager').transitionTo('saved');
      }
      alertDefinitionsMap[id].setProperties(summaryMap[id]);
      if (!alertDefinitionsMap[id].get('enabled')) {
        // clear summary for disabled alert definitions
        alertDefinitionsMap[id].set('summary', {});
      }
    });
    // set alertsCount and hasCriticalAlerts for each service
    var groupedByServiceName = dataManipulation.groupPropertyValues(alertDefinitions, 'service.serviceName');
    var groupedByComponentName = dataManipulation.groupPropertyValues(alertDefinitions, 'componentName');
    var services = App.Service.find();
    var servicesMap = services.toArray().toMapByProperty('id');
    Object.keys(groupedByServiceName).forEach(function(serviceName) {
      var service = servicesMap[serviceName];
      if (service) {
        var hasCriticalAlerts = false;

        var alertsCount = groupedByServiceName[serviceName].map(function (alertDefinition) {

          var criticalCount = alertDefinition.getWithDefault('summary.CRITICAL.count', 0);
          var warningCount = alertDefinition.getWithDefault('summary.WARNING.count', 0);

          if (criticalCount) {
            hasCriticalAlerts = true;
          }
          return criticalCount + warningCount;

        }).reduce(Em.sum, 0);

        service.setProperties({
          alertsCount: alertsCount,
          hasCriticalAlerts: hasCriticalAlerts
        });

        var masters = service.get('hostComponents').filterProperty('isMaster');
        masters.forEach(function (master) {
          var hasCriticalAlerts = false;

          var alertsCount = groupedByComponentName[master.get('componentName')].map(function (alertDefinition) {

            var criticalCount = alertDefinition.getWithDefault('summary.CRITICAL.count', 0);
            var warningCount = alertDefinition.getWithDefault('summary.WARNING.count', 0);

            if (criticalCount) {
              hasCriticalAlerts = true;
            }
            return criticalCount + warningCount;

          }).reduce(Em.sum, 0);

          master.setProperties({
            alertsCount: alertsCount,
            hasCriticalAlerts: hasCriticalAlerts
          });
        });
      }
    });
    if (!$.mocho) {
      //for some reasons this causing error in unit test
      App.store.commit();
    }
    console.timeEnd('App.alertDefinitionSummaryMapper execution time');

  }
});
<commit_msg>AMBARI-15181. JS error on UI load (onechiporenko)<commit_after>/**
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements. See the NOTICE file distributed with this
 * work for additional information regarding copyright ownership. The ASF
 * licenses this file to you 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.
 */

var App = require('app');
var dataManipulation = require('utils/data_manipulation');

App.alertDefinitionSummaryMapper = App.QuickDataMapper.create({

  config: {},

  map: function(data) {
    console.time('App.alertDefinitionSummaryMapper execution time');

    if (!data.alerts_summary_grouped) return;
    var alertDefinitions = App.AlertDefinition.find();
    var alertDefinitionsMap = alertDefinitions.toArray().toMapByProperty('id');
    var summaryMap = {};
    data.alerts_summary_grouped.forEach(function(alertDefinitionSummary) {
      var alertDefinition = alertDefinitionsMap[alertDefinitionSummary.definition_id];
      if (alertDefinition) {
        var summary = {},
          timestamp = 0;
        Em.keys(alertDefinitionSummary.summary).forEach(function(status) {
          summary[status] = {
            count: alertDefinitionSummary.summary[status].count,
            maintenanceCount: alertDefinitionSummary.summary[status].maintenance_count
          };
          if (alertDefinitionSummary.summary[status].latest_text) {
            summary[status].latestText = alertDefinitionSummary.summary[status].latest_text;
          }
          if (alertDefinitionSummary.summary[status].original_timestamp > timestamp) {
            timestamp = alertDefinitionSummary.summary[status].original_timestamp;
          }
        });
        summaryMap[alertDefinitionSummary.definition_id] = {
          summary: summary,
          lastTriggered: App.dateTimeWithTimeZone(parseInt(timestamp, 10)),
          lastTriggeredRaw: timestamp
        };
      }
    });

    alertDefinitions.forEach(function (d) {
      var id = d.get('id');
      if (alertDefinitionsMap[id].get('stateManager.currentState.name') !== 'saved') {
        alertDefinitionsMap[id].get('stateManager').transitionTo('saved');
      }
      alertDefinitionsMap[id].setProperties(summaryMap[id]);
      if (!alertDefinitionsMap[id].get('enabled')) {
        // clear summary for disabled alert definitions
        alertDefinitionsMap[id].set('summary', {});
      }
    });
    // set alertsCount and hasCriticalAlerts for each service
    var groupedByServiceName = dataManipulation.groupPropertyValues(alertDefinitions, 'service.serviceName');
    var groupedByComponentName = dataManipulation.groupPropertyValues(alertDefinitions, 'componentName');
    var services = App.Service.find();
    var servicesMap = services.toArray().toMapByProperty('id');
    Object.keys(groupedByServiceName).forEach(function(serviceName) {
      var service = servicesMap[serviceName];
      if (service) {
        var hasCriticalAlerts = false;

        var alertsCount = groupedByServiceName[serviceName].map(function (alertDefinition) {

          var criticalCount = alertDefinition.getWithDefault('summary.CRITICAL.count', 0);
          var warningCount = alertDefinition.getWithDefault('summary.WARNING.count', 0);

          if (criticalCount) {
            hasCriticalAlerts = true;
          }
          return criticalCount + warningCount;

        }).reduce(Em.sum, 0);

        service.setProperties({
          alertsCount: alertsCount,
          hasCriticalAlerts: hasCriticalAlerts
        });

        service.get('hostComponents').filterProperty('isMaster').forEach(function (master) {

          hasCriticalAlerts = false;
          alertsCount = (groupedByComponentName[master.get('componentName')] || []).map(function (alertDefinition) {

            var criticalCount = alertDefinition.getWithDefault('summary.CRITICAL.count', 0);
            var warningCount = alertDefinition.getWithDefault('summary.WARNING.count', 0);

            if (criticalCount) {
              hasCriticalAlerts = true;
            }
            return criticalCount + warningCount;

          }).reduce(Em.sum, 0);

          master.setProperties({
            alertsCount: alertsCount,
            hasCriticalAlerts: hasCriticalAlerts
          });
        });
      }
    });
    if (!$.mocho) {
      //for some reasons this causing error in unit test
      App.store.commit();
    }
    console.timeEnd('App.alertDefinitionSummaryMapper execution time');

  }
});
<|endoftext|>


<commit_before>/* global d3 */

function MapOrSectorController($location, storageService, locationsService) {
    var vm = this;
    var location_id = $location.search().location_id;

    if (['null', 'undefined', ''].indexOf(location_id) === -1) {
        locationsService.getLocation(location_id).then(function (location) {
            vm.showChart = location.location_type === 'district';
        });
    }

    function wrapXAxisLabels() {
        //This wrap te text on the xAxis label if text length is longer than 100
        //Found on stackoverflow: https://stackoverflow.com/questions/16701522/how-to-linebreak-an-svg-text-within-javascript/28553412#28553412
        //Replace svg text element to:
        //<text><tspan></tspan><tspan></tspan>...<text>
        d3.selectAll(".nv-x.nv-axis .tick text").each(function() {
            var text = d3.select(this),
                words = text.text().split(/\s+/).reverse(),
                word, line = [],
                lineNumber = 0,
                lineHeight = 1.1, // ems
                y = 2.5 * parseInt(words.length),
                dy = parseFloat(text.attr("dy")),
                tspan = text.text(null).append("tspan").attr("x", -5).attr("y", -y).attr("dy", dy + "em");

            word = words.pop();
            while (word) {
                line.push(word);
                tspan.text(line.join(" "));
                if (tspan.node().getComputedTextLength() > 100) {
                    line.pop();
                    tspan.text(line.join(" "));
                    line = [word];
                    tspan = text.append("tspan").attr("x", -5).attr("y", -y).attr("dy", ++lineNumber * lineHeight + dy + "em").text(word);
                }
                word = words.pop();
            }
        });
    }

    vm.chartOptions = {

        chart: {
            type: 'multiBarHorizontalChart',
            margin: {
                bottom: 40,
                left: 100,
            },
            x: function (d) {
                return d[0];
            },
            y: function (d) {
                return d[1];
            },
            showControls: false,
            showLegend: false,
            showValues: true,
            valueFormat: function (d) {
                if (vm.data.mapData.format === "number") {
                    return d;
                }
                return d3.format(".2%")(d);
            },
            xAxis: {
                showMaxMin: false,
            },
            yAxis: {
                tickFormat: function (d) {
                    if (vm.data.mapData.format === "number") {
                        return d3.format("d")(d);
                    }
                    var max = d3.max(vm.data.mapData.chart_data[0].values, function(value) {
                        return value[1];
                    });
                    return max < 0.1 ? d3.format(".2%")(d) : d3.format("%")(d);
                },
                axisLabelDistance: 20,
            },
            tooltip: {
                contentGenerator: function(d) {
                    if (!vm.data.mapData.tooltips_data || !vm.data.mapData.tooltips_data[d.value]) {
                        return 'NA';
                    }

                    return vm.templatePopup({
                        loc: {
                            properties: {
                                name: d.value,
                            },
                        },
                        row: vm.data.mapData.tooltips_data[d.value],
                    });
                },
            },
            callback: function(chart) {
                var height = 1500;
                var calcHeight = vm.data.mapData ? vm.data.mapData.chart_data[0].values.length * 60 : 0;
                vm.chartOptions.chart.height = calcHeight !== 0 ? calcHeight : height;

                chart.multibar.dispatch.on('elementClick', function (e) {
                    locationsService.getLocationByNameAndParent(e.data[0], location_id).then(function (locations) {
                        var location = locations[0];
                        $location.search('location_name', location.name);
                        $location.search('location_id', location.location_id);

                        storageService.setKey('search', $location.search());
                        if (location.location_type_name === 'awc') {
                            $location.path('awc_reports');
                        }
                    });
                });

                wrapXAxisLabels();

                return chart;
            },
        },
        caption: {
            enable: true,
            html: function () {
                return ' ' + (vm.data.mapData !== void(0) ? vm.data.mapData.info : "");
            },
            css: {
                'text-align': 'center',
                'margin': '0 auto',
                'width': '900px',
            },
        },
        title: {
            enable: true,
            text: vm.label,
            css: {
                'text-align': 'right',
                'color': 'black',
            },
        },
    };
}

MapOrSectorController.$inject = [ '$location', 'storageService', 'locationsService'];

var url = hqImport('hqwebapp/js/initial_page_data').reverse;

window.angular.module('icdsApp').directive('mapOrSectorView', function () {
    return {
        restrict: 'E',
        scope: {
            mode: '@',
            data: '=',
            templatePopup: '&',
            location: '=',
            label: '=',
        },
        templateUrl: url('icds-ng-template', 'map-or-sector-view.directive'),
        bindToController: true,
        controller: MapOrSectorController,
        controllerAs: '$ctrl',
    };
});
<commit_msg>[ICDS Dashboard] fix for ICDS-922<commit_after>/* global d3 */

function MapOrSectorController($location, storageService, locationsService) {
    var vm = this;
    var location_id = $location.search().location_id;

    if (['null', 'undefined', ''].indexOf(location_id) === -1) {
        locationsService.getLocation(location_id).then(function (location) {
            vm.showChart = location.location_type === 'district';
        });
    }

    function wrapXAxisLabels() {
        //This wrap te text on the xAxis label if text length is longer than 100
        //Found on stackoverflow: https://stackoverflow.com/questions/16701522/how-to-linebreak-an-svg-text-within-javascript/28553412#28553412
        //Replace svg text element to:
        //<text><tspan></tspan><tspan></tspan>...<text>
        d3.selectAll(".nv-x.nv-axis .tick text").each(function() {
            var text = d3.select(this),
                words = text.text().split(/\s+/).reverse(),
                word, line = [],
                lineNumber = 0,
                lineHeight = 1.1, // ems
                y = 2.5 * parseInt(words.length),
                dy = parseFloat(text.attr("dy")),
                tspan = text.text(null).append("tspan").attr("x", -5).attr("y", -y).attr("dy", dy + "em");

            word = words.pop();
            while (word) {
                line.push(word);
                tspan.text(line.join(" "));
                if (tspan.node().getComputedTextLength() > 100) {
                    line.pop();
                    tspan.text(line.join(" "));
                    line = [word];
                    tspan = text.append("tspan").attr("x", -5).attr("y", -y).attr("dy", ++lineNumber * lineHeight + dy + "em").text(word);
                }
                word = words.pop();
            }
        });
    }

    vm.chartOptions = {

        chart: {
            type: 'multiBarHorizontalChart',
            margin: {
                bottom: 40,
                left: 150,
            },
            x: function (d) {
                return d[0];
            },
            y: function (d) {
                return d[1];
            },
            showControls: false,
            showLegend: false,
            showValues: true,
            valueFormat: function (d) {
                if (vm.data.mapData.format === "number") {
                    return d;
                }
                return d3.format(".2%")(d);
            },
            xAxis: {
                showMaxMin: false,
            },
            yAxis: {
                tickFormat: function (d) {
                    if (vm.data.mapData.format === "number") {
                        return d3.format("d")(d);
                    }
                    var max = d3.max(vm.data.mapData.chart_data[0].values, function(value) {
                        return value[1];
                    });
                    return max < 0.1 ? d3.format(".2%")(d) : d3.format("%")(d);
                },
                axisLabelDistance: 20,
            },
            tooltip: {
                contentGenerator: function(d) {
                    if (!vm.data.mapData.tooltips_data || !vm.data.mapData.tooltips_data[d.value]) {
                        return 'NA';
                    }

                    return vm.templatePopup({
                        loc: {
                            properties: {
                                name: d.value,
                            },
                        },
                        row: vm.data.mapData.tooltips_data[d.value],
                    });
                },
            },
            callback: function(chart) {
                var height = 1500;
                var calcHeight = vm.data.mapData ? vm.data.mapData.chart_data[0].values.length * 60 : 0;
                vm.chartOptions.chart.height = calcHeight !== 0 ? calcHeight : height;

                chart.multibar.dispatch.on('elementClick', function (e) {
                    locationsService.getLocationByNameAndParent(e.data[0], location_id).then(function (locations) {
                        var location = locations[0];
                        $location.search('location_name', location.name);
                        $location.search('location_id', location.location_id);

                        storageService.setKey('search', $location.search());
                        if (location.location_type_name === 'awc') {
                            $location.path('awc_reports');
                        }
                    });
                });

                nv.utils.windowResize(function(){
                    wrapXAxisLabels();
                });
                wrapXAxisLabels();

                return chart;
            },
        },
        caption: {
            enable: true,
            html: function () {
                return ' ' + (vm.data.mapData !== void(0) ? vm.data.mapData.info : "");
            },
            css: {
                'text-align': 'center',
                'margin': '0 auto',
                'width': '900px',
            },
        },
        title: {
            enable: true,
            text: vm.label,
            css: {
                'text-align': 'right',
                'color': 'black',
            },
        },
    };
}

MapOrSectorController.$inject = [ '$location', 'storageService', 'locationsService'];

var url = hqImport('hqwebapp/js/initial_page_data').reverse;

window.angular.module('icdsApp').directive('mapOrSectorView', function () {
    return {
        restrict: 'E',
        scope: {
            mode: '@',
            data: '=',
            templatePopup: '&',
            location: '=',
            label: '=',
        },
        templateUrl: url('icds-ng-template', 'map-or-sector-view.directive'),
        bindToController: true,
        controller: MapOrSectorController,
        controllerAs: '$ctrl',
    };
});
<|endoftext|>