<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.
// ==========================================================================

/** @class

  Coordinating controller for the document.

  @extends SC.Object
*/

/*global CSV: 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) {
        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);
          });
        } else {
          fn(key, value);
        }
      }

      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') {
              symbols.push(Number(value));
            } else if (key === 'id') {
              references.push(Number(value));
            }
          });
        });
        references.forEach(function (ref) {
          if (symbols.indexOf(Number(ref)) < 0) {
            errors.push('DG.AppController.validateDocument.unresolvedID'.loc(ref));
          }
        });
      }
      var expectedProperties = [
        'appBuildNum',
        'appName',
        'appVersion',
        'components',
        'contexts',
        'globalValues',
        'guid',
        'name',
        '_permissions'
      ];
      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 (DG.ExternalDocumentCache) {
        subDocs = DG.ExternalDocumentCache.fetchAll().map(function (iFragment) {
          var parsedFragment = {};
          if (typeof iDocument === 'string') {
            try {
              parsedFragment = JSON.parse(iFragment);
            } catch (ex) {
              errors.push('DG.AppController.validateDocument.parseError'.loc(ex));
            }
          } else {
            parsedFragment = iFragment;
          }
          return parsedFragment;
        });
      }

      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) {
              errors.push('DG.AppController.validateDocument.unexpectedProperty'.loc(prop));
            }
          }
        );
        validateInternalRefs(doc);
      }
      DG.log('Document validation: ' + (errors.length? JSON.stringify(errors): 'No Errors'));
      return errors;
    },

  /**
    Open the specified document text as a new document, returning the newly-created document.
   */
  openDocument: function( iStore, iDocText) {
    var deferred = $.Deferred(),
    externalDocIds = DG.StringUtilities.scan(iDocText, /"externalDocumentId": ?"?(\d+)"?/g, function(m) { return m[1]; }),
        promises = DG.authorizationController.loadExternalDocuments(externalDocIds);

    Promise.all(promises).then(function() {
      try {
        var docArchive = SC.json.decode( iDocText),
          validationErrors;

        validationErrors = this.isValidJsonDocument(docArchive);
        if (validationErrors.length > 0) {
          deferred.reject(new Error('DG.AppController.validateDocument.invalidDocument'.loc(
            JSON.stringify(validationErrors))));
        }
        else {
          DG.store = DG.ModelStore.create();
          deferred.resolve(DG.Document.createDocument(docArchive));
        }
      } catch (ex) {
        deferred.reject(ex);
      }
      DG.ExternalDocumentCache.clear();
      DG.busyCursor.hide();
    }.bind(this));
    return deferred;
  },

  importCSV: function (iText, iFileName) {

    // trims empty columns from right side of
    function trimTrailingColumns(arr) {
      var newArr = [];
      arr.forEach(function (row) {
        var value;
        if (Array.isArray(row)) {
          do {
            value = row.pop();
          } while(value === '');
          if (!SC.empty(value)) row.push(value);
        }
        if (row.length) {
          newArr.push(row);
        }
      });
      return newArr;
    }
    function parseText() {
      var tValuesArray,
        tCollectionRow,
        tChildName = 'children',// Child Collection Name: should be first
                                // line of CSV
        tAttrNamesRow,// Column Header Names: should be second row
        tDoc = {
          name: 'DG.Document.defaultDocumentName'.loc(),
          components: [],
          contexts: [
            {
              "type": "DG.DataContext",
              "collections": [
                {
                  "attrs": [],
                  "cases": []
                }
              ],
              "contextStorage": {
                "gameUrl": iFileName
              }
            }
          ],
          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;

      CSV.RELAXED = true;
      CSV.IGNORE_RECORD_LENGTH = true;
      tValuesArray = CSV.parse(iText);
      if (tValuesArray && tValuesArray.length >= 2) {
        tValuesArray = trimTrailingColumns(tValuesArray);
        tCollectionRow = tValuesArray.shift();
        tAttrNamesRow = tValuesArray[0];

        if (Array.isArray(tCollectionRow)) {
          // check if it looks like name row is missing
          if ((tAttrNamesRow.length === tCollectionRow.length) && (tAttrNamesRow.length > 1)) {
            tAttrNamesRow = tCollectionRow;
          } else {
            tChildName = tCollectionRow[0];
            tValuesArray.shift();
          }
        }
        else {
          tChildName = tCollectionRow;
          tValuesArray.shift();
        }
        tDoc.contexts[0].collections[0].name = tChildName;

        tAttrNamesRow.forEach(function (iName) {
          tAttrsArray.push( {
            name: iName
          });
        });

        tValuesArray.forEach( function( iValues) {
          var tCase = {
            values: {}
          };
          tAttrNamesRow.forEach( function( iName, iIndex) {
            tCase.values[ iName] = iValues[ iIndex];
          });
          tCasesArray.push( tCase);
        });

        return tDoc;
      }
    } // parseText

    var docArchive = parseText( );
    return docArchive;
  },
  /**
   *
   * @param iText - csv or tab-delimited
   * @returns {DG.Document}
   */
  importTextIntoDocument: function( iText, iName) {
    var docArchive = this.importCSV(iText, iName);

    return DG.Document.createDocument(docArchive);
  },

  /**
   *
   * @param iURL - URL of data interactive
   * @returns {DG.Document}
   */
  importURLIntoDocument: 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);
  },

  /**
    Save the specified document in its JSON-text form.
    @param    {DG.Document}   iDocument   The document whose contents are to be archived

    signature of `callback`:
    @param  {Object} docArchive      An object suitable for JSON encoding
   */
  saveDocument: function( iDocument, callback, fullData) {

    // Prepare the context-specific storage for saving.
    // Start by saving the state of the current game in the appropriate context.
    // Callback below executes after the state has been saved
    DG.currDocumentController().saveCurrentGameState(function() {
      var docController = DG.currDocumentController();
      DG.DataContext.forEachContextInMap( iDocument.get('id'),
        function( iContextID, iContext) {
          iContext.willSaveContext();
        });
      if( docController) {
        // Prepare the component-specific storage for saving
        DG.ObjectMap.forEach( docController.componentControllersMap,
          function( iComponentID, iController) {
            iController.willSaveComponent();
          });
      }

      callback(iDocument.toArchive(fullData));
    });
  },

  saveDataContexts: function( iDocument, callback, saveAll) {
    var model,
        deferred = $.Deferred();
    DG.currDocumentController().saveCurrentGameState(function() {
      DG.DataContext.forEachContextInMap( iDocument.get('id'),
        function( iContextID, iContext) {
          iContext.willSaveContext();
          model = iContext.get('model');
          if ( saveAll || !SC.none(model.get('externalDocumentId'))) {
            callback(model, model.toArchive(true));
          }
        });
      deferred.resolve();
    });
    return deferred;
  }

});

<commit_msg>Improve document validation.<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.
// ==========================================================================

/** @class

  Coordinating controller for the document.

  @extends SC.Object
*/

/*global CSV: 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') {
              symbols.push(Number(value));
              return true;
            } else if (key === '_links_') {
              DG.ObjectMap.forEach(value, function(k, v) {
                references.push(Number(v.id));
              });
              return true;
            } else if (key === 'contextStorage') { // context storage is private to data interactive
              return false;
            }

          });
        });
        references.forEach(function (ref) {
          if (symbols.indexOf(Number(ref)) < 0) {
            errors.push('DG.AppController.validateDocument.unresolvedID'.loc(ref));
          }
        });
      }
      var expectedProperties = [
        'appBuildNum',
        'appName',
        'appVersion',
        'components',
        'contexts',
        'globalValues',
        'guid',
        'name',
        '_permissions'
      ];
      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 (DG.ExternalDocumentCache) {
        subDocs = DG.ExternalDocumentCache.fetchAll().map(function (iFragment) {
          var parsedFragment = {};
          if (typeof iDocument === 'string') {
            try {
              parsedFragment = JSON.parse(iFragment);
            } catch (ex) {
              errors.push('DG.AppController.validateDocument.parseError'.loc(ex));
            }
          } else {
            parsedFragment = iFragment;
          }
          return parsedFragment;
        });
      }

      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) {
              errors.push('DG.AppController.validateDocument.unexpectedProperty'.loc(prop));
            }
          }
        );
        validateInternalRefs(doc);
      }
      DG.log('Document validation: ' + (errors.length? JSON.stringify(errors): 'No Errors'));
      return errors;
    },

  /**
    Open the specified document text as a new document, returning the newly-created document.
   */
  openDocument: function( iStore, iDocText) {
    var deferred = $.Deferred(),
    externalDocIds = DG.StringUtilities.scan(iDocText, /"externalDocumentId": ?"?(\d+)"?/g, function(m) { return m[1]; }),
        promises = DG.authorizationController.loadExternalDocuments(externalDocIds);

    Promise.all(promises).then(function() {
      try {
        var docArchive = SC.json.decode( iDocText),
          validationErrors;

        validationErrors = this.isValidJsonDocument(docArchive);
        if (validationErrors.length > 0) {
          deferred.reject(new Error('DG.AppController.validateDocument.invalidDocument'.loc(
            JSON.stringify(validationErrors))));
        }
        else {
          DG.store = DG.ModelStore.create();
          deferred.resolve(DG.Document.createDocument(docArchive));
        }
      } catch (ex) {
        deferred.reject(ex);
      }
      DG.ExternalDocumentCache.clear();
      DG.busyCursor.hide();
    }.bind(this));
    return deferred;
  },

  importCSV: function (iText, iFileName) {

    // trims empty columns from right side of
    function trimTrailingColumns(arr) {
      var newArr = [];
      arr.forEach(function (row) {
        var value;
        if (Array.isArray(row)) {
          do {
            value = row.pop();
          } while(value === '');
          if (!SC.empty(value)) row.push(value);
        }
        if (row.length) {
          newArr.push(row);
        }
      });
      return newArr;
    }
    function parseText() {
      var tValuesArray,
        tCollectionRow,
        tChildName = 'children',// Child Collection Name: should be first
                                // line of CSV
        tAttrNamesRow,// Column Header Names: should be second row
        tDoc = {
          name: 'DG.Document.defaultDocumentName'.loc(),
          components: [],
          contexts: [
            {
              "type": "DG.DataContext",
              "collections": [
                {
                  "attrs": [],
                  "cases": []
                }
              ],
              "contextStorage": {
                "gameUrl": iFileName
              }
            }
          ],
          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;

      CSV.RELAXED = true;
      CSV.IGNORE_RECORD_LENGTH = true;
      tValuesArray = CSV.parse(iText);
      if (tValuesArray && tValuesArray.length >= 2) {
        tValuesArray = trimTrailingColumns(tValuesArray);
        tCollectionRow = tValuesArray.shift();
        tAttrNamesRow = tValuesArray[0];

        if (Array.isArray(tCollectionRow)) {
          // check if it looks like name row is missing
          if ((tAttrNamesRow.length === tCollectionRow.length) && (tAttrNamesRow.length > 1)) {
            tAttrNamesRow = tCollectionRow;
          } else {
            tChildName = tCollectionRow[0];
            tValuesArray.shift();
          }
        }
        else {
          tChildName = tCollectionRow;
          tValuesArray.shift();
        }
        tDoc.contexts[0].collections[0].name = tChildName;

        tAttrNamesRow.forEach(function (iName) {
          tAttrsArray.push( {
            name: iName
          });
        });

        tValuesArray.forEach( function( iValues) {
          var tCase = {
            values: {}
          };
          tAttrNamesRow.forEach( function( iName, iIndex) {
            tCase.values[ iName] = iValues[ iIndex];
          });
          tCasesArray.push( tCase);
        });

        return tDoc;
      }
    } // parseText

    var docArchive = parseText( );
    return docArchive;
  },
  /**
   *
   * @param iText - csv or tab-delimited
   * @returns {DG.Document}
   */
  importTextIntoDocument: function( iText, iName) {
    var docArchive = this.importCSV(iText, iName);

    return DG.Document.createDocument(docArchive);
  },

  /**
   *
   * @param iURL - URL of data interactive
   * @returns {DG.Document}
   */
  importURLIntoDocument: 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);
  },

  /**
    Save the specified document in its JSON-text form.
    @param    {DG.Document}   iDocument   The document whose contents are to be archived

    signature of `callback`:
    @param  {Object} docArchive      An object suitable for JSON encoding
   */
  saveDocument: function( iDocument, callback, fullData) {

    // Prepare the context-specific storage for saving.
    // Start by saving the state of the current game in the appropriate context.
    // Callback below executes after the state has been saved
    DG.currDocumentController().saveCurrentGameState(function() {
      var docController = DG.currDocumentController();
      DG.DataContext.forEachContextInMap( iDocument.get('id'),
        function( iContextID, iContext) {
          iContext.willSaveContext();
        });
      if( docController) {
        // Prepare the component-specific storage for saving
        DG.ObjectMap.forEach( docController.componentControllersMap,
          function( iComponentID, iController) {
            iController.willSaveComponent();
          });
      }

      callback(iDocument.toArchive(fullData));
    });
  },

  saveDataContexts: function( iDocument, callback, saveAll) {
    var model,
        deferred = $.Deferred();
    DG.currDocumentController().saveCurrentGameState(function() {
      DG.DataContext.forEachContextInMap( iDocument.get('id'),
        function( iContextID, iContext) {
          iContext.willSaveContext();
          model = iContext.get('model');
          if ( saveAll || !SC.none(model.get('externalDocumentId'))) {
            callback(model, model.toArchive(true));
          }
        });
      deferred.resolve();
    });
    return deferred;
  }

});

<|endoftext|>


<commit_before>/**
 * Created by IntelliJ IDEA.
 * User: sbortman
 * Date: 1/30/12
 * Time: 4:57 PM
 * To change this template use File | Settings | File Templates.
 */

(function ()
{
    var Dom = YAHOO.util.Dom,
            Event = YAHOO.util.Event;

    Event.onDOMReady( function ()
    {
        var outerLayout = new YAHOO.widget.Layout( {
            minWidth:1000,
            minHeight:500,
            units:[
                { position:'top', height:50, body:'top1', scroll:null, zIndex:2 },
                { position:'right', header:'', width:200, resize:true, gutter:'0px', collapse:true, scroll:true, body:'right1', animate:false },
                { position:'bottom', height:25, body:'bottom1' },
                { position:'left', header:'', width:200, resize:true, body:'left1', gutter:'0px', collapse:true, scroll:true, animate:false },
                { position:'center', minWidth:400, minHeight:200 }
            ]
        } );
        outerLayout.on( 'render', function ()
        {
            var el = outerLayout.getUnitByPosition( 'center' ).get( 'wrap' );
            var innerLayout = new YAHOO.widget.Layout( el, {
                parent:outerLayout,
                minWidth:400,
                minHeight:200,
                units:[
                    { position:'top', height:35, proxy:false, body:'top2' },
                    { position:'bottom', height:30, proxy:false, body:'bottom2' },
                    { position:'center', body:'center2', gutter:'0px' }
                ]
            } );
            innerLayout.render();
            init();
            Dom.setStyle( document.body, 'visibility', 'visible' );
        } );
        outerLayout.render();
        outerLayout.getUnitByPosition( "left" ).on( "collapse", function ()
        {
            bodyOnResize();
        } );

        outerLayout.getUnitByPosition( "left" ).on( "expand", function ()
        {
            bodyOnResize();
        } );

        outerLayout.getUnitByPosition( "right" ).on( "collapse", function ()
        {
            bodyOnResize();
        } );

        outerLayout.getUnitByPosition( "right" ).on( "expand", function ()
        {
            bodyOnResize();
        } );
    } );
})();

var bodyOnResize = function ()
{
    omar.changeMapSize();
};
<commit_msg>Added window resize event listener to resize the map div<commit_after>/**
 * Created by IntelliJ IDEA.
 * User: sbortman
 * Date: 1/30/12
 * Time: 4:57 PM
 * To change this template use File | Settings | File Templates.
 */

(function ()
{
    var Dom = YAHOO.util.Dom,
            Event = YAHOO.util.Event;

    Event.addListener(window, 'resize', bodyOnResize);

    Event.onDOMReady( function ()
    {
        var outerLayout = new YAHOO.widget.Layout( {
            minWidth:1000,
            minHeight:500,
            units:[
                { position:'top', height:50, body:'top1', scroll:null, zIndex:2 },
                { position:'right', header:'', width:200, resize:true, gutter:'0px', collapse:true, scroll:true, body:'right1', animate:false },
                { position:'bottom', height:25, body:'bottom1' },
                { position:'left', header:'', width:200, resize:true, body:'left1', gutter:'0px', collapse:true, scroll:true, animate:false },
                { position:'center', minWidth:400, minHeight:200 }
            ]
        } );
        outerLayout.on( 'render', function ()
        {
            var el = outerLayout.getUnitByPosition( 'center' ).get( 'wrap' );
            var innerLayout = new YAHOO.widget.Layout( el, {
                parent:outerLayout,
                minWidth:400,
                minHeight:200,
                units:[
                    { position:'top', height:35, proxy:false, body:'top2' },
                    { position:'bottom', height:30, proxy:false, body:'bottom2' },
                    { position:'center', body:'center2', gutter:'0px' }
                ]
            } );
            innerLayout.render();
            init();
            Dom.setStyle( document.body, 'visibility', 'visible' );
        } );
        outerLayout.render();
        outerLayout.getUnitByPosition( "left" ).on( "collapse", function ()
        {
            bodyOnResize();
        } );

        outerLayout.getUnitByPosition( "left" ).on( "expand", function ()
        {
            bodyOnResize();
        } );

        outerLayout.getUnitByPosition( "right" ).on( "collapse", function ()
        {
            bodyOnResize();
        } );

        outerLayout.getUnitByPosition( "right" ).on( "expand", function ()
        {
            bodyOnResize();
        } );
    } );
})();

var bodyOnResize = function ()
{
    omar.changeMapSize();
};
<|endoftext|>


<commit_before>/*!
 * VIZABI INDICATOR PICKER
 * Reusable indicator picker component
 */

(function () {

    "use strict";

    var root = this;
    var Vizabi = root.Vizabi;
    var utils = Vizabi.utils;

    var INDICATOR = "which";
    var MIN = "min";
    var MAX = "max";
    var SCALETYPE = "scaleType";
    var MODELTYPE_COLOR = "color";

    //warn client if d3 is not defined
    if (!Vizabi._require('d3')) {
        return;
    }

    var availOpts = {
        'geo.region': {use: 'property',unit: '',scales: ['ordinal'] },
        'geo': {use: 'property',unit: '',scales: ['ordinal'] },
        'time': {use: 'indicator',unit: 'time',scales: ['time'] },
        'lex': {use: 'indicator',unit: 'lex',scales: ['linear'] },
        'gdp_per_cap': {use: 'indicator',unit: 'gdp_per_cap',scales: ['log', 'linear'] },
        'pop': {use: 'indicator',unit: '',scales: ['linear', 'log'] },
        '_default': {use: 'value',unit: '',scales: ['linear', 'log'] }
    };

    Vizabi.Component.extend('gapminder-indicatorpicker', {

        /**
         * Initializes the Indicator Picker.
         * Executed once before any template is rendered.
         * @param config The options passed to the component
         * @param context The component's parent
         */
        init: function (config, context) {

            this.template = '<select class="vzb-ip-indicator"></select><select class="vzb-ip-scaletype"></select>
 <input type="text" class="vzb-ip-domainmin" name="min">  <input type="text" class="vzb-ip-domainmax" name="max">';
            var _this = this;

            this.model_expects = [{
                name: "axis"
                    //TODO: learn how to expect model "axis" or "size" or "color"
            }, {
                name: "language",
                type: "language"
            }];

            this.model_binds = {
                "change:axis": function (evt) {
                    _this.updateView();
                },
                "change:language": function (evt) {
                    _this.updateView();
                }
            }

            //contructor is the same as any component
            this._super(config, context);

            this.ui = utils.extend({
                selectIndicator: true,
                selectScaletype: true,
                selectMinMax: false,
            }, this.ui.getObject());

        },

        ready: function () {
            this.updateView();
        },

        readyOnce: function () {
            var _this = this;

            this.element = d3.select(this.element);
            this.el_select_indicator = this.element.select('.vzb-ip-indicator');
            this.el_select_scaletype = this.element.select('.vzb-ip-scaletype');

            this.el_domain_labelMin = this.element.select('.vzb-ip-domainmin-label');
            this.el_domain_labelMax = this.element.select('.vzb-ip-domainmax-label');
            this.el_domain_fieldMin = this.element.select('.vzb-ip-domainmin');
            this.el_domain_fieldMax = this.element.select('.vzb-ip-domainmax');

            this.el_select_indicator.on("change", function() { _this._setModel(INDICATOR, this.value)});
            this.el_select_scaletype.on("change", function() { _this._setModel(SCALETYPE, this.value)});

            _this.el_domain_fieldMin.on("change", function(){_this._setModel(MIN, this.value)});
            _this.el_domain_fieldMax.on("change", function(){_this._setModel(MAX, this.value)});
        },

        updateView: function () {
            var _this = this;
            this.translator = this.model.language.getTFunction();

            this.el_domain_labelMin.text(this.translator("min") + ":")
            this.el_domain_labelMax.text(this.translator("max") + ":")

            var pointer = "_default";

            var data = {};
            data[INDICATOR] = Object.keys(availOpts);

            if (data[INDICATOR].indexOf(this.model.axis[INDICATOR]) > -1) pointer = this.model.axis[INDICATOR];

            data[SCALETYPE] = availOpts[pointer].scales;

            //bind the data to the selector lists
            var elOptionsIndicator = this.el_select_indicator.selectAll("option")
                .data(data[INDICATOR], function (d) {return d});
            var elOptionsScaletype = this.el_select_scaletype.selectAll("option")
                .data(data[SCALETYPE], function (d) {return d});

            //remove irrelevant options
            elOptionsIndicator.exit().remove();
            elOptionsScaletype.exit().remove();

            //populate options into the list
            elOptionsIndicator.enter().append("option").attr("value", function (d) {return d});
            elOptionsScaletype.enter().append("option").attr("value", function (d) {return d});

            //show translated UI string
            elOptionsIndicator.text(function (d) { return _this.translator("indicator/" + d) })
            elOptionsScaletype.text(function (d) { return _this.translator("scaletype/" + d) })

            //set the selected option
            this.el_select_indicator[0][0].value = this.model.axis[INDICATOR];
            this.el_select_scaletype[0][0].value = this.model.axis[SCALETYPE];

            //disable the selector in case if there is only one option, hide if so requested by the UI setings
            this.el_select_indicator
                .style('display', this.ui.selectIndicator ? "auto" : "none")
                .attr('disabled', data[INDICATOR].length <= 1 ? "true" : null)
            this.el_select_scaletype
                .style('display', this.ui.selectScaletype ? "auto" : "none")
                .attr('disabled', data[SCALETYPE].length <= 1 ? "true" : null)

            this.el_domain_labelMin.style('display', this.ui.selectMinMax ? "auto" : "none");
            this.el_domain_labelMax.style('display', this.ui.selectMinMax ? "auto" : "none");
            this.el_domain_fieldMin.style('display', this.ui.selectMinMax ? "auto" : "none");
            this.el_domain_fieldMax.style('display', this.ui.selectMinMax ? "auto" : "none");

            var formatter = d3.format(".2r");
            this.el_domain_fieldMin.property("value", formatter(this.model.axis.getScale().domain()[0]));
            this.el_domain_fieldMax.property("value", formatter(this.model.axis.getScale().domain()[1]));
        },

        _setModel: function (what, value) {
            if(what==MIN || what==MAX) value = utils.strToFloat(value);

            var mdl = this.model.axis;

            var obj = {};
            obj[what] = value;

            if (what == INDICATOR) {
                obj.use = availOpts[value].use;
                obj.unit = availOpts[value].unit;

                if (availOpts[value].scales.indexOf(mdl.scaleType) == -1) {
                    obj.scaleType = availOpts[value].scales[0];
                }
            }

            mdl.set(obj);
        }

    });

}).call(this);<commit_msg>Fixed #259 MC_40: Drop-down values are not select-able<commit_after>/*!
 * VIZABI INDICATOR PICKER
 * Reusable indicator picker component
 */

(function () {

    "use strict";

    var root = this;
    var Vizabi = root.Vizabi;
    var utils = Vizabi.utils;

    var INDICATOR = "which";
    var MIN = "min";
    var MAX = "max";
    var SCALETYPE = "scaleType";
    var MODELTYPE_COLOR = "color";

    //warn client if d3 is not defined
    if (!Vizabi._require('d3')) {
        return;
    }

    var availOpts = {
        'geo.region': {use: 'property', unit: '', scales: ['ordinal']},
        'geo': {use: 'property', unit: '', scales: ['ordinal']},
        'time': {use: 'indicator', unit: 'time', scales: ['time']},
        'lex': {use: 'indicator', unit: 'lex', scales: ['linear']},
        'gdp_per_cap': {use: 'indicator', unit: 'gdp_per_cap', scales: ['log', 'linear']},
        'pop': {use: 'indicator', unit: '', scales: ['linear', 'log']},
        '_default': {use: 'value', unit: '', scales: []}
    };

    Vizabi.Component.extend('gapminder-indicatorpicker', {

        /**
         * Initializes the Indicator Picker.
         * Executed once before any template is rendered.
         * @param config The options passed to the component
         * @param context The component's parent
         */
        init: function (config, context) {

            this.template = '<select class="vzb-ip-indicator"></select><select class="vzb-ip-scaletype"></select>
 <input type="text" class="vzb-ip-domainmin" name="min">  <input type="text" class="vzb-ip-domainmax" name="max">';
            var _this = this;

            this.model_expects = [{
                name: "axis"
                //TODO: learn how to expect model "axis" or "size" or "color"
            }, {
                name: "language",
                type: "language"
            }];

            this.model_binds = {
                "change:axis": function (evt) {
                    _this.updateView();
                },
                "change:language": function (evt) {
                    _this.updateView();
                }
            }

            //contructor is the same as any component
            this._super(config, context);

            this.ui = utils.extend({
                selectIndicator: true,
                selectScaletype: true,
                selectMinMax: false,
            }, this.ui.getObject());

        },

        ready: function () {
            this.updateView();
        },

        readyOnce: function () {
            var _this = this;

            this.element = d3.select(this.element);
            this.el_select_indicator = this.element.select('.vzb-ip-indicator');
            this.el_select_scaletype = this.element.select('.vzb-ip-scaletype');

            this.el_domain_labelMin = this.element.select('.vzb-ip-domainmin-label');
            this.el_domain_labelMax = this.element.select('.vzb-ip-domainmax-label');
            this.el_domain_fieldMin = this.element.select('.vzb-ip-domainmin');
            this.el_domain_fieldMax = this.element.select('.vzb-ip-domainmax');

            this.el_select_indicator.on("change", function () {
                _this._setModel(INDICATOR, this.value)
            });
            this.el_select_scaletype.on("change", function () {
                _this._setModel(SCALETYPE, this.value)
            });

            _this.el_domain_fieldMin.on("change", function () {
                _this._setModel(MIN, this.value)
            });
            _this.el_domain_fieldMax.on("change", function () {
                _this._setModel(MAX, this.value)
            });
        },

        updateView: function () {
            var _this = this;
            this.translator = this.model.language.getTFunction();

            this.el_domain_labelMin.text(this.translator("min") + ":")
            this.el_domain_labelMax.text(this.translator("max") + ":")

            var pointer = "_default";

            var data = {};
            data[INDICATOR] = Object.keys(availOpts);

            if (data[INDICATOR].indexOf(this.model.axis[INDICATOR]) > -1) pointer = this.model.axis[INDICATOR];

            data[SCALETYPE] = availOpts[pointer].scales;

            //bind the data to the selector lists
            var elOptionsIndicator = this.el_select_indicator.selectAll("option")
                .data(data[INDICATOR], function (d) {
                    return d
                });
            var elOptionsScaletype = this.el_select_scaletype.selectAll("option")
                .data(data[SCALETYPE], function (d) {
                    return d
                });

            //remove irrelevant options
            elOptionsIndicator.exit().remove();
            elOptionsScaletype.exit().remove();

            //populate options into the list
            elOptionsIndicator.enter().append("option").attr("value", function (d) {
                return d
            });
            elOptionsScaletype.enter().append("option").attr("value", function (d) {
                return d
            });

            //show translated UI string
            elOptionsIndicator.text(function (d) {
                return _this.translator("indicator/" + d)
            })
            elOptionsScaletype.text(function (d) {
                return _this.translator("scaletype/" + d)
            })

            //set the selected option
            this.el_select_indicator[0][0].value = this.model.axis[INDICATOR];
            this.el_select_scaletype[0][0].value = this.model.axis[SCALETYPE];

            //disable the selector in case if there is only one option, hide if so requested by the UI setings
            this.el_select_indicator
                .style('display', this.ui.selectIndicator ? "auto" : "none")
                .attr('disabled', data[INDICATOR].length <= 1 ? "true" : null)

            this.el_select_scaletype
                .style('display', data[SCALETYPE].length == 0 ? "none" : "inline")
                .style('display', this.ui.selectScaletype ? "auto" : "none")
                .attr('disabled', data[SCALETYPE].length <= 1 ? "true" : null)

            this.el_domain_labelMin.style('display', this.ui.selectMinMax ? "auto" : "none");
            this.el_domain_labelMax.style('display', this.ui.selectMinMax ? "auto" : "none");
            this.el_domain_fieldMin.style('display', this.ui.selectMinMax ? "auto" : "none");
            this.el_domain_fieldMax.style('display', this.ui.selectMinMax ? "auto" : "none");

            var formatter = d3.format(".2r");
            this.el_domain_fieldMin.property("value", formatter(this.model.axis.getScale().domain()[0]));
            this.el_domain_fieldMax.property("value", formatter(this.model.axis.getScale().domain()[1]));
        },

        _setModel: function (what, value) {
            if (what == MIN || what == MAX) value = utils.strToFloat(value);

            var mdl = this.model.axis;

            var obj = {};
            obj[what] = value;

            if (what == INDICATOR) {
                obj.use = availOpts[value].use;
                obj.unit = availOpts[value].unit;

                if (availOpts[value].scales.indexOf(mdl.scaleType) == -1) {
                    obj.scaleType = availOpts[value].scales[0];
                }
            }

            mdl.set(obj);
        }

    });

}).call(this);<|endoftext|>


<commit_before>import React from "react";
const DraggingLine = ({
  id,
  width,
  height,
  components,
  connectingFrom,
  connectingTo,
  selected,
  loc = {},
  onClick = () => {},
  color,
  stroke
}) => {
  const comp = components.find(c => c.id === connectingFrom);
  const toComp = components.find(c => c.id === connectingTo);
  let { x, y } = loc;
  if (toComp) {
    x = toComp.x * width;
    y = toComp.y * height;
  }
  const offset = 5;
  return (
    <path
      className="drag-line"
      onClick={() => {
        onClick(id);
      }}
      d={`M${comp.x * width + 5} ${comp.y * height + 5} L ${x + 5} ${y + 5}`}
      fill="transparent"
      stroke={color || "white"}
      strokeWidth={selected ? 7 : stroke || 3}
    />
  );
};

export default DraggingLine;
<commit_msg>Minor fix<commit_after>import React from "react";
const DraggingLine = ({
  id,
  width,
  height,
  components,
  connectingFrom,
  connectingTo,
  selected,
  loc = {},
  onClick = () => {},
  color,
  stroke
}) => {
  const comp = components.find(c => c.id === connectingFrom);
  const toComp = components.find(c => c.id === connectingTo);
  let { x, y } = loc;
  if (toComp) {
    x = toComp.x * width;
    y = toComp.y * height;
  }
  const offset = 5;
  return (
    <path
      className="drag-line"
      onClick={() => {
        onClick(id);
      }}
      d={`M${comp.x * width + offset} ${comp.y * height + offset} L ${x +
        offset} ${y + offset}`}
      fill="transparent"
      stroke={color || "white"}
      strokeWidth={selected ? 7 : stroke || 3}
    />
  );
};

export default DraggingLine;
<|endoftext|>


<commit_before>/*!
    Copyright (C) 2014 Google Inc., authors, and contributors <see AUTHORS file>
    Licensed under http://www.apache.org/licenses/LICENSE-2.0 <see LICENSE file>
    Created By: dan@reciprocitylabs.com
    Maintained By: dan@reciprocitylabs.com
*/

(function(can) {

  var _mustache_path,
      overdue_compute;

  overdue_compute = can.compute(function(val) {
    if (this.attr("status") === "Verified") {
      return "";
    }
    var date = moment(this.attr("next_due_date") || this.attr("end_date"));
    if(date && date.isBefore(new Date())){
      return "overdue";
    }
    return "";
  });

  function refresh_attr(instance, attr){
    if (instance.attr(attr).reify().selfLink) {
      instance.attr(attr).reify().refresh();
    }
  }

  function refresh_attr_wrap(attr){
    return function(ev, instance) {
      if (instance instanceof this) {
        refresh_attr(instance, attr);
      }
    };
  }

  _mustache_path = GGRC.mustache_path + "/cycles";
  can.Model.Cacheable("CMS.Models.Cycle", {
    root_object: "cycle",
    root_collection: "cycles",
    category: "workflow",
    findAll: "GET /api/cycles",
    findOne: "GET /api/cycles/{id}",
    create: "POST /api/cycles",
    update: "PUT /api/cycles/{id}",
    destroy: "DELETE /api/cycles/{id}",

    attributes: {
      workflow: "CMS.Models.Workflow.stub",
      cycle_task_groups: "CMS.Models.CycleTaskGroup.stubs",
      modified_by: "CMS.Models.Person.stub",
      context: "CMS.Models.Context.stub"
    },

    tree_view_options: {
      show_view: _mustache_path + "/tree.mustache",
      header_view : _mustache_path + "/tree_header.mustache",
      draw_children: true,
      child_options: [
        {
          model: "CycleTaskGroup",
          mapping: "cycle_task_groups",
          allow_creating: false
        }
      ]
    },
    init: function(){
      var that = this;
      this._super.apply(this, arguments);
      this.bind("created", refresh_attr_wrap('workflow').bind(this));
      this.bind("destroyed", function(ev, inst) {
        if(inst instanceof that) {
          can.each(inst.cycle_task_groups, function(cycle_task_group) {
            if (!cycle_task_group) {
              return;
            }
            cycle_task_group = cycle_task_group.reify();
            can.trigger(cycle_task_group, "destroyed");
            can.trigger(cycle_task_group.constructor, "destroyed", cycle_task_group);
          });
        }
      });
    }
  }, {
    init: function() {
      var that = this;
      this._super.apply(this, arguments);
      this.bind("status", function(ev, newVal) {
        if (newVal === 'Verified') {
          new RefreshQueue().enqueue(this.workflow.reify()).trigger().then(function(wfs) {
            return wfs[0].get_binding("owners").refresh_instances();
          }).then(function(wf_owner_bindings) {
            var current_user = CMS.Models.get_instance(GGRC.current_user);
            if(~can.inArray(
              current_user,
              can.map(wf_owner_bindings, function(wf_owner_binding) {
                return wf_owner_binding.instance;
              })
            )) {
              that.refresh().then(function() {
                if(that.attr("is_current")) {
                  that.attr("is_current", false);
                  that.save();
                }
              });
            }
          });
        }
      });
    },
    overdue: overdue_compute,
  });

  _mustache_path = GGRC.mustache_path + "/cycle_task_entries";
  can.Model.Cacheable("CMS.Models.CycleTaskEntry", {
    root_object: "cycle_task_entry",
    root_collection: "cycle_task_entries",
    category: "workflow",
    findAll: "GET /api/cycle_task_entries",
    findOne: "GET /api/cycle_task_entries/{id}",
    create: "POST /api/cycle_task_entries",
    update: "PUT /api/cycle_task_entries/{id}",
    destroy: "DELETE /api/cycle_task_entries/{id}",

    attributes: {
      cycle_task_group_object_task: "CMS.Models.CycleTaskGroupObjectTask.stub",
      modified_by: "CMS.Models.Person.stub",
      context: "CMS.Models.Context.stub",
      object_documents: "CMS.Models.ObjectDocument.stubs",
      documents: "CMS.Models.Document.stubs",
      cycle: "CMS.Models.Cycle.stub"
    },

    tree_view_options: {
      show_view: _mustache_path + "/tree.mustache",
      footer_view: _mustache_path + "/tree_footer.mustache",
      child_options: [{
        //0: Documents
        model: "Document",
        mapping: "documents",
        show_view: _mustache_path + "/documents.mustache",
        footer_view: _mustache_path + "/documents_footer.mustache"
      }],
    },
    init: function(){
      this._super.apply(this, arguments);
      this.bind("created",
        refresh_attr_wrap("cycle_task_group_object_task").bind(this));
      this.validateNonBlank("description");
    }
  }, {
    workflowFolder: function() {
      return this.refresh_all('cycle', 'workflow').then(function(workflow){
        if (workflow.has_binding('folders')) {
          return workflow.refresh_all('folders').then(function(folders){
            if (folders.length === 0) {
              return null;  // workflow folder has not been assigned
            }
            return folders[0].instance;
          }, function(result) {
            return result;
          });
        }
      });
    }
  });

  _mustache_path = GGRC.mustache_path + "/cycle_task_groups";
  can.Model.Cacheable("CMS.Models.CycleTaskGroup", {
    root_object: "cycle_task_group",
    root_collection: "cycle_task_groups",
    category: "workflow",
    findAll: "GET /api/cycle_task_groups",
    findOne: "GET /api/cycle_task_groups/{id}",
    create: "POST /api/cycle_task_groups",
    update: "PUT /api/cycle_task_groups/{id}",
    destroy: "DELETE /api/cycle_task_groups/{id}",

    attributes: {
      cycle: "CMS.Models.Cycle.stub",
      task_group: "CMS.Models.TaskGroup.stub",
      cycle_task_group_tasks: "CMS.Models.CycleTaskGroupObjectTask.stubs",
      modified_by: "CMS.Models.Person.stub",
      context: "CMS.Models.Context.stub"
    },

    tree_view_options: {
      sort_property: 'sort_index',
      show_view: _mustache_path + "/tree.mustache",
      //footer_view: _mustache_path + "/tree_footer.mustache",
      draw_children: true,
      child_options: [
        {
          title: 'Tasks',
          model: "CycleTaskGroupObjectTask",
          mapping: "cycle_task_group_tasks",
          allow_creating: false
        }
      ]
    },

    init: function() {
      var that = this;
      this._super.apply(this, arguments);

      this.validateNonBlank("contact");
      this.validateContact(["_transient.contact", "contact"]);
      this.bind("updated", function(ev, instance) {
        if (instance instanceof that) {
          var dfd = instance.refresh_all_force('cycle', 'workflow');
          dfd.then(function(){
            return $.when(
              instance.refresh_all_force('related_objects'),
              instance.refresh_all_force('cycle_task_group_tasks')
            );
          });
        }
      });
      this.bind("destroyed", function(ev, inst) {
        if(inst instanceof that) {
          can.each(inst.cycle_task_group_tasks, function(ctgt) {
            if (!ctgt) {
              return;
            }
            ctgt = ctgt.reify();
            can.trigger(ctgt, "destroyed");
            can.trigger(ctgt.constructor, "destroyed", ctgt);
          });
        }
      });
    }
  }, {
    overdue: overdue_compute
  });

  _mustache_path = GGRC.mustache_path + "/cycle_task_group_object_tasks";
  can.Model.Cacheable("CMS.Models.CycleTaskGroupObjectTask", {
    root_object: "cycle_task_group_object_task",
    root_collection: "cycle_task_group_object_tasks",
    category: "workflow",
    findAll: "GET /api/cycle_task_group_object_tasks",
    findOne: "GET /api/cycle_task_group_object_tasks/{id}",
    create: "POST /api/cycle_task_group_object_tasks",
    update: "PUT /api/cycle_task_group_object_tasks/{id}",
    destroy: "DELETE /api/cycle_task_group_object_tasks/{id}",
    title_singular: "Cycle Task",

    attributes: {
      cycle_task_group: "CMS.Models.CycleTaskGroup.stub",
      task_group_task: "CMS.Models.TaskGroupTask.stub",
      cycle_task_entries: "CMS.Models.CycleTaskEntry.stubs",
      modified_by: "CMS.Models.Person.stub",
      contact: "CMS.Models.Person.stub",
      context: "CMS.Models.Context.stub",
      cycle: "CMS.Models.Cycle.stub"
    },
    permalink_options: {
      url: "<%= base.viewLink %>#current_widget/cycle/<%= instance.cycle.id %>/cycle_task_group/<%= instance.cycle_task_group.id %>/cycle_task_group_object_task/<%= instance.id %>",
      base: "cycle:workflow"
    },
    info_pane_options: {
      mapped_objects: {
        model: can.Model.Cacheable,
        mapping: 'info_related_objects',
        show_view: GGRC.mustache_path + '/base_templates/subtree.mustache'
      }
    },
    tree_view_options: {
      sort_property: 'sort_index',
      show_view: _mustache_path + "/tree.mustache",
      attr_list: [
        {attr_title: 'Title', attr_name: 'title'},
        {attr_title: 'Workflow', attr_name: 'workflow', attr_sort_field: 'cycle.workflow.title'},
        {attr_title: 'State', attr_name: 'status'},
        {attr_title: 'Assignee', attr_name: 'assignee', attr_sort_field: 'contact.name|email'},
        {attr_title: 'Start Date', attr_name: 'start_date'},
        {attr_title: 'End Date', attr_name: 'end_date'},
        {attr_title: 'Last Updated', attr_name: 'updated_at'}
      ],
      display_attr_names: ['title', 'mapped_object', 'workflow'],
      mandatory_attr_name: ['title'],
      draw_children: true,
      child_options: [
        {
          model: "CycleTaskEntry",
          mapping: "cycle_task_entries",
          allow_creating: true
        },
        {
          model: can.Model.Cacheable,
          mapping: 'info_related_objects',
          allow_creating: true
        }
      ]
    },

    init: function() {
      var that = this;
      this._super.apply(this, arguments);
      this.validateNonBlank("title");
      this.validateNonBlank("contact");
      this.validateContact(["_transient.contact", "contact"]);

      this.validate(['start_date', 'end_date'], function (newVal, prop) {
        if (!(this.start_date && this.end_date)) {
          return "Start and/or end date is invalid";
        }
      });

      this.bind("updated", function (ev, instance) {
        if (instance instanceof that) {
          instance.refresh_all_force('related_objects').then(function (object) {
            return instance.refresh_all_force('cycle_task_group', 'cycle', 'workflow');
          });
        }
      });
    }
  }, {
    overdue: overdue_compute,
    workflow: function () {
      return this.refresh_all('cycle', 'workflow').then(function (workflow) {
        return workflow;
      });
    },
    object: function () {
      return this.refresh_all('task_group_object', 'object').then(function (object) {
        return object;
      });
    },
    response_options_csv: can.compute(function (val) {
      if(val != null) {
        this.attr("response_options", $.map(val.split(","), $.proxy("".trim.call, "".trim)));
      } else {
        return (this.attr("response_options") || []).join(", ");
      }
    }),

    selected_response_options_csv: can.compute(function(val) {
      if(val != null) {
        this.attr("selected_response_options", $.map(val.split(","), $.proxy("".trim.call, "".trim)));
      } else {
        return (this.attr("selected_response_options") || []).join(", ");
      }
    }),

    get_filter_vals: function () {
      var filter_vals = can.Model.Cacheable.prototype.get_filter_vals;
      var mappings = jQuery.extend({}, this.class.filter_mappings, {
        'task title': 'title'
      });

      var vals = filter_vals.apply(this, [this.class.filter_keys, mappings]);

      try {
        vals['workflows'] = this.cycle.reify().workflow.reify().title;
      } catch (e) {}

      return vals;
    }

  });

})(window.can);
<commit_msg>Show assignee and start_date by default on treeview for tasks<commit_after>/*!
    Copyright (C) 2014 Google Inc., authors, and contributors <see AUTHORS file>
    Licensed under http://www.apache.org/licenses/LICENSE-2.0 <see LICENSE file>
    Created By: dan@reciprocitylabs.com
    Maintained By: dan@reciprocitylabs.com
*/

(function(can) {

  var _mustache_path,
      overdue_compute;

  overdue_compute = can.compute(function(val) {
    if (this.attr("status") === "Verified") {
      return "";
    }
    var date = moment(this.attr("next_due_date") || this.attr("end_date"));
    if(date && date.isBefore(new Date())){
      return "overdue";
    }
    return "";
  });

  function refresh_attr(instance, attr){
    if (instance.attr(attr).reify().selfLink) {
      instance.attr(attr).reify().refresh();
    }
  }

  function refresh_attr_wrap(attr){
    return function(ev, instance) {
      if (instance instanceof this) {
        refresh_attr(instance, attr);
      }
    };
  }

  _mustache_path = GGRC.mustache_path + "/cycles";
  can.Model.Cacheable("CMS.Models.Cycle", {
    root_object: "cycle",
    root_collection: "cycles",
    category: "workflow",
    findAll: "GET /api/cycles",
    findOne: "GET /api/cycles/{id}",
    create: "POST /api/cycles",
    update: "PUT /api/cycles/{id}",
    destroy: "DELETE /api/cycles/{id}",

    attributes: {
      workflow: "CMS.Models.Workflow.stub",
      cycle_task_groups: "CMS.Models.CycleTaskGroup.stubs",
      modified_by: "CMS.Models.Person.stub",
      context: "CMS.Models.Context.stub"
    },

    tree_view_options: {
      show_view: _mustache_path + "/tree.mustache",
      header_view : _mustache_path + "/tree_header.mustache",
      draw_children: true,
      child_options: [
        {
          model: "CycleTaskGroup",
          mapping: "cycle_task_groups",
          allow_creating: false
        }
      ]
    },
    init: function(){
      var that = this;
      this._super.apply(this, arguments);
      this.bind("created", refresh_attr_wrap('workflow').bind(this));
      this.bind("destroyed", function(ev, inst) {
        if(inst instanceof that) {
          can.each(inst.cycle_task_groups, function(cycle_task_group) {
            if (!cycle_task_group) {
              return;
            }
            cycle_task_group = cycle_task_group.reify();
            can.trigger(cycle_task_group, "destroyed");
            can.trigger(cycle_task_group.constructor, "destroyed", cycle_task_group);
          });
        }
      });
    }
  }, {
    init: function() {
      var that = this;
      this._super.apply(this, arguments);
      this.bind("status", function(ev, newVal) {
        if (newVal === 'Verified') {
          new RefreshQueue().enqueue(this.workflow.reify()).trigger().then(function(wfs) {
            return wfs[0].get_binding("owners").refresh_instances();
          }).then(function(wf_owner_bindings) {
            var current_user = CMS.Models.get_instance(GGRC.current_user);
            if(~can.inArray(
              current_user,
              can.map(wf_owner_bindings, function(wf_owner_binding) {
                return wf_owner_binding.instance;
              })
            )) {
              that.refresh().then(function() {
                if(that.attr("is_current")) {
                  that.attr("is_current", false);
                  that.save();
                }
              });
            }
          });
        }
      });
    },
    overdue: overdue_compute,
  });

  _mustache_path = GGRC.mustache_path + "/cycle_task_entries";
  can.Model.Cacheable("CMS.Models.CycleTaskEntry", {
    root_object: "cycle_task_entry",
    root_collection: "cycle_task_entries",
    category: "workflow",
    findAll: "GET /api/cycle_task_entries",
    findOne: "GET /api/cycle_task_entries/{id}",
    create: "POST /api/cycle_task_entries",
    update: "PUT /api/cycle_task_entries/{id}",
    destroy: "DELETE /api/cycle_task_entries/{id}",

    attributes: {
      cycle_task_group_object_task: "CMS.Models.CycleTaskGroupObjectTask.stub",
      modified_by: "CMS.Models.Person.stub",
      context: "CMS.Models.Context.stub",
      object_documents: "CMS.Models.ObjectDocument.stubs",
      documents: "CMS.Models.Document.stubs",
      cycle: "CMS.Models.Cycle.stub"
    },

    tree_view_options: {
      show_view: _mustache_path + "/tree.mustache",
      footer_view: _mustache_path + "/tree_footer.mustache",
      child_options: [{
        //0: Documents
        model: "Document",
        mapping: "documents",
        show_view: _mustache_path + "/documents.mustache",
        footer_view: _mustache_path + "/documents_footer.mustache"
      }],
    },
    init: function(){
      this._super.apply(this, arguments);
      this.bind("created",
        refresh_attr_wrap("cycle_task_group_object_task").bind(this));
      this.validateNonBlank("description");
    }
  }, {
    workflowFolder: function() {
      return this.refresh_all('cycle', 'workflow').then(function(workflow){
        if (workflow.has_binding('folders')) {
          return workflow.refresh_all('folders').then(function(folders){
            if (folders.length === 0) {
              return null;  // workflow folder has not been assigned
            }
            return folders[0].instance;
          }, function(result) {
            return result;
          });
        }
      });
    }
  });

  _mustache_path = GGRC.mustache_path + "/cycle_task_groups";
  can.Model.Cacheable("CMS.Models.CycleTaskGroup", {
    root_object: "cycle_task_group",
    root_collection: "cycle_task_groups",
    category: "workflow",
    findAll: "GET /api/cycle_task_groups",
    findOne: "GET /api/cycle_task_groups/{id}",
    create: "POST /api/cycle_task_groups",
    update: "PUT /api/cycle_task_groups/{id}",
    destroy: "DELETE /api/cycle_task_groups/{id}",

    attributes: {
      cycle: "CMS.Models.Cycle.stub",
      task_group: "CMS.Models.TaskGroup.stub",
      cycle_task_group_tasks: "CMS.Models.CycleTaskGroupObjectTask.stubs",
      modified_by: "CMS.Models.Person.stub",
      context: "CMS.Models.Context.stub"
    },

    tree_view_options: {
      sort_property: 'sort_index',
      show_view: _mustache_path + "/tree.mustache",
      //footer_view: _mustache_path + "/tree_footer.mustache",
      draw_children: true,
      child_options: [
        {
          title: 'Tasks',
          model: "CycleTaskGroupObjectTask",
          mapping: "cycle_task_group_tasks",
          allow_creating: false
        }
      ]
    },

    init: function() {
      var that = this;
      this._super.apply(this, arguments);

      this.validateNonBlank("contact");
      this.validateContact(["_transient.contact", "contact"]);
      this.bind("updated", function(ev, instance) {
        if (instance instanceof that) {
          var dfd = instance.refresh_all_force('cycle', 'workflow');
          dfd.then(function(){
            return $.when(
              instance.refresh_all_force('related_objects'),
              instance.refresh_all_force('cycle_task_group_tasks')
            );
          });
        }
      });
      this.bind("destroyed", function(ev, inst) {
        if(inst instanceof that) {
          can.each(inst.cycle_task_group_tasks, function(ctgt) {
            if (!ctgt) {
              return;
            }
            ctgt = ctgt.reify();
            can.trigger(ctgt, "destroyed");
            can.trigger(ctgt.constructor, "destroyed", ctgt);
          });
        }
      });
    }
  }, {
    overdue: overdue_compute
  });

  _mustache_path = GGRC.mustache_path + "/cycle_task_group_object_tasks";
  can.Model.Cacheable("CMS.Models.CycleTaskGroupObjectTask", {
    root_object: "cycle_task_group_object_task",
    root_collection: "cycle_task_group_object_tasks",
    category: "workflow",
    findAll: "GET /api/cycle_task_group_object_tasks",
    findOne: "GET /api/cycle_task_group_object_tasks/{id}",
    create: "POST /api/cycle_task_group_object_tasks",
    update: "PUT /api/cycle_task_group_object_tasks/{id}",
    destroy: "DELETE /api/cycle_task_group_object_tasks/{id}",
    title_singular: "Cycle Task",

    attributes: {
      cycle_task_group: "CMS.Models.CycleTaskGroup.stub",
      task_group_task: "CMS.Models.TaskGroupTask.stub",
      cycle_task_entries: "CMS.Models.CycleTaskEntry.stubs",
      modified_by: "CMS.Models.Person.stub",
      contact: "CMS.Models.Person.stub",
      context: "CMS.Models.Context.stub",
      cycle: "CMS.Models.Cycle.stub"
    },
    permalink_options: {
      url: "<%= base.viewLink %>#current_widget/cycle/<%= instance.cycle.id %>/cycle_task_group/<%= instance.cycle_task_group.id %>/cycle_task_group_object_task/<%= instance.id %>",
      base: "cycle:workflow"
    },
    info_pane_options: {
      mapped_objects: {
        model: can.Model.Cacheable,
        mapping: 'info_related_objects',
        show_view: GGRC.mustache_path + '/base_templates/subtree.mustache'
      }
    },
    tree_view_options: {
      sort_property: 'sort_index',
      show_view: _mustache_path + "/tree.mustache",
      attr_list: [
        {attr_title: 'Title', attr_name: 'title'},
        {attr_title: 'Workflow', attr_name: 'workflow', attr_sort_field: 'cycle.workflow.title'},
        {attr_title: 'State', attr_name: 'status'},
        {attr_title: 'Assignee', attr_name: 'assignee', attr_sort_field: 'contact.name|email'},
        {attr_title: 'Start Date', attr_name: 'start_date'},
        {attr_title: 'End Date', attr_name: 'end_date'},
        {attr_title: 'Last Updated', attr_name: 'updated_at'}
      ],
      display_attr_names: ['title', 'assignee', 'start_date'],
      mandatory_attr_name: ['title'],
      draw_children: true,
      child_options: [
        {
          model: "CycleTaskEntry",
          mapping: "cycle_task_entries",
          allow_creating: true
        },
        {
          model: can.Model.Cacheable,
          mapping: 'info_related_objects',
          allow_creating: true
        }
      ]
    },

    init: function() {
      var that = this;
      this._super.apply(this, arguments);
      this.validateNonBlank("title");
      this.validateNonBlank("contact");
      this.validateContact(["_transient.contact", "contact"]);

      this.validate(['start_date', 'end_date'], function (newVal, prop) {
        if (!(this.start_date && this.end_date)) {
          return "Start and/or end date is invalid";
        }
      });

      this.bind("updated", function (ev, instance) {
        if (instance instanceof that) {
          instance.refresh_all_force('related_objects').then(function (object) {
            return instance.refresh_all_force('cycle_task_group', 'cycle', 'workflow');
          });
        }
      });
    }
  }, {
    overdue: overdue_compute,
    workflow: function () {
      return this.refresh_all('cycle', 'workflow').then(function (workflow) {
        return workflow;
      });
    },
    object: function () {
      return this.refresh_all('task_group_object', 'object').then(function (object) {
        return object;
      });
    },
    response_options_csv: can.compute(function (val) {
      if(val != null) {
        this.attr("response_options", $.map(val.split(","), $.proxy("".trim.call, "".trim)));
      } else {
        return (this.attr("response_options") || []).join(", ");
      }
    }),

    selected_response_options_csv: can.compute(function(val) {
      if(val != null) {
        this.attr("selected_response_options", $.map(val.split(","), $.proxy("".trim.call, "".trim)));
      } else {
        return (this.attr("selected_response_options") || []).join(", ");
      }
    }),

    get_filter_vals: function () {
      var filter_vals = can.Model.Cacheable.prototype.get_filter_vals;
      var mappings = jQuery.extend({}, this.class.filter_mappings, {
        'task title': 'title'
      });

      var vals = filter_vals.apply(this, [this.class.filter_keys, mappings]);

      try {
        vals['workflows'] = this.cycle.reify().workflow.reify().title;
      } catch (e) {}

      return vals;
    }

  });

})(window.can);
<|endoftext|>


<commit_before>function DashboardController($scope, $cookies, $http, $mdDialog) {
	$scope.incidents = [];
	$scope.fetching = false;
	$scope.stats = {};
	$scope.raw_stats = {};
	$scope.byStats = {};
	var hostMap = {};
	$scope.hostMap = hostMap;
	var stopper = null;

	this.selected = 0;
	this.getSelected = function() {
		var s = $cookies.get("dash:tab");
		if (s) {
			this.selected = s;
		}
		return this.selected;
	}
	this.updateSelected = function(name) {
		$cookies.put("dash:tab", name);
		this.selected = name;
	}

	this.showResolveDialog = function($mdOpen,e) {
		$mdOpen();
	}

	constructMap = function(host, val, target) {
		host = host.split(".")
		next = {}
		if (target[host[host.length -1]]) {
			next = target[host[host.length-1]]
		} else {
			target[host[host.length - 1]] = next
		}
		if (!(next["count"])) {
			next["count"] = 0
		}
		next["count"] += val
		for (var i = host.length - 2; i >= 0; i--) {
			if (!next[host[i]]) {
				next[host[i]] = {}
				next = next[host[i]]
			} else {
				next = next[host[i]]
			}
			if (!(next["count"])) {
				next["count"] = 0
			}
			next["count"]+= val;
		};
	}

	buildHostMap = function() {
		hostMap = {}
		for (host in $scope.raw_stats.by_host) {
			constructMap(host, $scope.raw_stats.by_host[host], hostMap)
		}
		$scope.hostMap = hostMap;
		$scope.byStats["Hosts"] = hostMap;
	}

	buildServiceMap = function() {
		serviceMap = {}
		for (service in $scope.raw_stats.by_service) {
			constructMap(service.split(".").reverse().join("."), $scope.raw_stats.by_service[service], serviceMap)
		}
		$scope.serviceMap = serviceMap;
		$scope.byStats["Services"] = serviceMap;
	}
	buildSubServiceMap = function() {
		subServiceMap = {}
		for (subService in $scope.raw_stats.by_sub_service) {
			constructMap(subService.split(".").reverse().join("."), $scope.raw_stats.by_sub_service[subService], subServiceMap)
		}
		$scope.subServiceMap = subServiceMap;
		$scope.byStats["Sub Services"] = subServiceMap;
	}

	$scope.startFetching = function() {
		$scope.fetchIncidents();
		$scope.fetchStats();

		if (!$scope.fetching) {
			$scope.fetching = true;
			stopper = setInterval(function(){
				$scope.fetchIncidents();
				$scope.fetchStats();
			}, 5000)
		}
	}

	$scope.lastTotal = 0;
	$scope.fetchStats = function() {
		$http.get("api/stats/event").success(function(data) {
			$scope.raw_stats = data;
			buildHostMap()
			buildServiceMap()
			buildSubServiceMap()
			$scope.stats["Total Events"] = data.total_events
			$scope.stats["Events/s"] = (data.total_events - $scope.lastTotal) / 5
			$scope.lastTotal = data.total_events
			$scope.stats["Hosts"] = Object.keys(data.by_host).length
			$scope.stats["Services"] = Object.keys(data.by_service).length
			$scope.stats["Sub Services"] = Object.keys(data.by_sub_service).length
		});

		$http.get("api/stats/system").success(function(data) {
			$scope.stats["Uptime"] = data["uptime"];
		})
	}

	$scope.forgetHost = function(hostname) {
		$http.delete("api/host/"+hostname).success(function() {
			$mdDialog.show($mdDialog.alert().title("Removed host" + hostname).content("").ok("Ok"))
		});
	}

	$scope.stopFetching = function() {
		clearInterval(stopper);
		$scope.fetching = false;
	}

	$scope.resolveIncident = function(id) {
		$http.delete("api/incident/" + id, null).success(function(){
			$scope.fetchIncidents();
		});
	}

	$scope.formatDescription = function(incident) {
		return incident.service + (incident.sub_service ? "." + incident.sub_service : " ") + " on " + incident.host + " is " + incident.metric.toFixed(2) + " at " + new Date(incident.time * 1000).format("h:M:sTT mmmm-dd-yyyy") + " triggered by " + incident.policy;
	}

	var codes = {
		"0": "OK",
		"1": "WARNING",
		"2": "CRITICAL"
	}

	$scope.getStatusCode = function(status) {
		return codes[status];
	}

	var colors = {
		"0": "green",
		"1": "#FFFD82",
		"2": "#FB5C5C"
	}

	$scope.getStatusColor = function(status) {
		return colors[status]
	}

	$scope.fetchIncidents = function() {
		$http.get("api/incident/*").success(function(data) {
			var ins = [];
			for (k in data) {
				ins.push({key:k, val:data[k]})
			}

			ins.sort(function(x,y) {
				if (x.val.status != y.val.status) {
					return y.val.status - x.val.status
				}
				return y.val.time - x.val.time;
			})
			$scope.incidents = ins;
		});
	}
}

angular.module("bangarang").controller("DashboardController", DashboardController);
<commit_msg>pad seconds and minutes<commit_after>function DashboardController($scope, $cookies, $http, $mdDialog) {
	$scope.incidents = [];
	$scope.fetching = false;
	$scope.stats = {};
	$scope.raw_stats = {};
	$scope.byStats = {};
	var hostMap = {};
	$scope.hostMap = hostMap;
	var stopper = null;

	this.selected = 0;
	this.getSelected = function() {
		var s = $cookies.get("dash:tab");
		if (s) {
			this.selected = s;
		}
		return this.selected;
	}
	this.updateSelected = function(name) {
		$cookies.put("dash:tab", name);
		this.selected = name;
	}

	this.showResolveDialog = function($mdOpen,e) {
		$mdOpen();
	}

	constructMap = function(host, val, target) {
		host = host.split(".")
		next = {}
		if (target[host[host.length -1]]) {
			next = target[host[host.length-1]]
		} else {
			target[host[host.length - 1]] = next
		}
		if (!(next["count"])) {
			next["count"] = 0
		}
		next["count"] += val
		for (var i = host.length - 2; i >= 0; i--) {
			if (!next[host[i]]) {
				next[host[i]] = {}
				next = next[host[i]]
			} else {
				next = next[host[i]]
			}
			if (!(next["count"])) {
				next["count"] = 0
			}
			next["count"]+= val;
		};
	}

	buildHostMap = function() {
		hostMap = {}
		for (host in $scope.raw_stats.by_host) {
			constructMap(host, $scope.raw_stats.by_host[host], hostMap)
		}
		$scope.hostMap = hostMap;
		$scope.byStats["Hosts"] = hostMap;
	}

	buildServiceMap = function() {
		serviceMap = {}
		for (service in $scope.raw_stats.by_service) {
			constructMap(service.split(".").reverse().join("."), $scope.raw_stats.by_service[service], serviceMap)
		}
		$scope.serviceMap = serviceMap;
		$scope.byStats["Services"] = serviceMap;
	}
	buildSubServiceMap = function() {
		subServiceMap = {}
		for (subService in $scope.raw_stats.by_sub_service) {
			constructMap(subService.split(".").reverse().join("."), $scope.raw_stats.by_sub_service[subService], subServiceMap)
		}
		$scope.subServiceMap = subServiceMap;
		$scope.byStats["Sub Services"] = subServiceMap;
	}

	$scope.startFetching = function() {
		$scope.fetchIncidents();
		$scope.fetchStats();

		if (!$scope.fetching) {
			$scope.fetching = true;
			stopper = setInterval(function(){
				$scope.fetchIncidents();
				$scope.fetchStats();
			}, 5000)
		}
	}

	$scope.lastTotal = 0;
	$scope.fetchStats = function() {
		$http.get("api/stats/event").success(function(data) {
			$scope.raw_stats = data;
			buildHostMap()
			buildServiceMap()
			buildSubServiceMap()
			$scope.stats["Total Events"] = data.total_events
			$scope.stats["Events/s"] = (data.total_events - $scope.lastTotal) / 5
			$scope.lastTotal = data.total_events
			$scope.stats["Hosts"] = Object.keys(data.by_host).length
			$scope.stats["Services"] = Object.keys(data.by_service).length
			$scope.stats["Sub Services"] = Object.keys(data.by_sub_service).length
		});

		$http.get("api/stats/system").success(function(data) {
			$scope.stats["Uptime"] = data["uptime"];
		})
	}

	$scope.forgetHost = function(hostname) {
		$http.delete("api/host/"+hostname).success(function() {
			$mdDialog.show($mdDialog.alert().title("Removed host" + hostname).content("").ok("Ok"))
		});
	}

	$scope.stopFetching = function() {
		clearInterval(stopper);
		$scope.fetching = false;
	}

	$scope.resolveIncident = function(id) {
		$http.delete("api/incident/" + id, null).success(function(){
			$scope.fetchIncidents();
		});
	}

	$scope.formatDescription = function(incident) {
		return incident.service + (incident.sub_service ? "." + incident.sub_service : " ") + " on " + incident.host + " is " + incident.metric.toFixed(2) + " at " + new Date(incident.time * 1000).format("h:MM:ssTT mmmm-dd-yyyy") + " triggered by " + incident.policy;
	}

	var codes = {
		"0": "OK",
		"1": "WARNING",
		"2": "CRITICAL"
	}

	$scope.getStatusCode = function(status) {
		return codes[status];
	}

	var colors = {
		"0": "green",
		"1": "#FFFD82",
		"2": "#FB5C5C"
	}

	$scope.getStatusColor = function(status) {
		return colors[status]
	}

	$scope.fetchIncidents = function() {
		$http.get("api/incident/*").success(function(data) {
			var ins = [];
			for (k in data) {
				ins.push({key:k, val:data[k]})
			}

			ins.sort(function(x,y) {
				if (x.val.status != y.val.status) {
					return y.val.status - x.val.status
				}
				return y.val.time - x.val.time;
			})
			$scope.incidents = ins;
		});
	}
}

angular.module("bangarang").controller("DashboardController", DashboardController);
<|endoftext|>


<commit_before>var exec = require('child_process').exec,
      fs = require('fs'),
      bundleStats = require('../../bundle-stats.js');

describe("BundleStatsCollector - ", function() {

    var validateBundle,
        bundle1 = 'bundle1',
        string1 = 'An ab config',
        bundle2 = 'bundle2',
        string2 = 'A different ab config.',
        bundle3 = 'bundle3',
        string3 = 'A third ab config.',
        stats;

  beforeEach(function () {

      stats = new bundleStats.BundleStatsCollector(null);
      stats.LocalizedStrings = {};
      validateBundle = function(bundleName, abconfigs)
      {
          expect(stats.AbConfigs[bundleName].length).toBe(abconfigs.length);

          for(var i=0; i<abconfigs.length; i++) {
              expect(stats.AbConfigs[bundleName][i]).toBe(abconfigs[i]);
          }
      };
  });

 describe("ParseJsForStats: ", function() {

  var getAbConfig = function(ab) {
          return "AB.isOn('" + ab + "')";
      },
  var getAbVariant = function(ab) {
          return "AB.getVariant('" + ab + "')";
      },
      ab1 = getAbConfig(string1),
      ab2 = getAbConfig(string2),
      ab3 = getAbVariant(string3);

  it("Adds ab configs and variants to the collection.", function() {

      stats.ParseJsForStats(bundle1, ab1);

      validateBundle(bundle1, [ string1 ]);

      stats.ParseJsForStats(bundle1, ab2);

      validateBundle(bundle1, [ string1, string2 ]);

      stats.ParseJsForStats(bundle1, ab3);

      validateBundle(bundle1, [ string1, string2, string3 ]);
  });

    it("Text with multiple ab configs adds them all.", function() {

        stats.ParseJsForStats(bundle1, ab1 + ab2 + ab3);
        validateBundle(bundle1, [ string1, string2, string3 ]);
    });

    it("Doesn't break if there are no ab configs.", function() {

        stats.ParseJsForStats(bundle1, 'var x = "This has no ab configs"; \n x = x + "l";');
        expect(stats.AbConfigs[bundle1]).toBe(undefined);
    });

    it("Clearing a bundle removes all ab configs.", function() {

        stats.ParseJsForStats(bundle1, ab1);
        stats.ParseJsForStats(bundle1, ab2);

        validateBundle(bundle1, [ string1, string2 ]);

        stats.ClearStatsForBundle(bundle1);

        validateBundle(bundle1, [ ]);
    });

    it("Duplicate ab configs are not added.", function() {

        stats.ParseJsForStats(bundle1, ab1);

        validateBundle(bundle1, [ string1 ]);

        stats.ParseJsForStats(bundle1, ab1);

        validateBundle(bundle1, [ string1 ]);

        stats.ParseJsForStats(bundle1, ab1);

        validateBundle(bundle1, [ string1 ]);
    });

    it("ab configs added are isolated to their collection.", function() {

        stats.ParseJsForStats(bundle1, ab1);
        stats.ParseJsForStats(bundle2, ab2);
        stats.ParseJsForStats(bundle3, ab3);

        validateBundle(bundle1, [ string1 ]);
        validateBundle(bundle2, [ string2 ]);
        validateBundle(bundle3, [ string3 ]);
    });

 });

});<commit_msg>Fix busted test.<commit_after>var exec = require('child_process').exec,
      fs = require('fs'),
      bundleStats = require('../../bundle-stats.js');

describe("BundleStatsCollector - ", function() {

    var validateBundle,
        bundle1 = 'bundle1',
        string1 = 'An ab config',
        bundle2 = 'bundle2',
        string2 = 'A different ab config.',
        bundle3 = 'bundle3',
        string3 = 'A third ab config.',
        stats;

  beforeEach(function () {

      stats = new bundleStats.BundleStatsCollector(null);
      stats.LocalizedStrings = {};
      validateBundle = function(bundleName, abconfigs)
      {
          expect(stats.AbConfigs[bundleName].length).toBe(abconfigs.length);

          for(var i=0; i<abconfigs.length; i++) {
              expect(stats.AbConfigs[bundleName][i]).toBe(abconfigs[i]);
          }
      };
  });

 describe("ParseJsForStats: ", function() {

  var getAbConfig = function(ab) {
          return "AB.isOn('" + ab + "')";
      },
      getAbVariant = function(ab) {
          return "AB.getVariant('" + ab + "')";
      },
      ab1 = getAbConfig(string1),
      ab2 = getAbConfig(string2),
      ab3 = getAbVariant(string3);

  it("Adds ab configs and variants to the collection.", function() {

      stats.ParseJsForStats(bundle1, ab1);

      validateBundle(bundle1, [ string1 ]);

      stats.ParseJsForStats(bundle1, ab2);

      validateBundle(bundle1, [ string1, string2 ]);

      stats.ParseJsForStats(bundle1, ab3);

      validateBundle(bundle1, [ string1, string2, string3 ]);
  });

    it("Text with multiple ab configs adds them all.", function() {

        stats.ParseJsForStats(bundle1, ab1 + ab2 + ab3);
        validateBundle(bundle1, [ string1, string2, string3 ]);
    });

    it("Doesn't break if there are no ab configs.", function() {

        stats.ParseJsForStats(bundle1, 'var x = "This has no ab configs"; \n x = x + "l";');
        expect(stats.AbConfigs[bundle1]).toBe(undefined);
    });

    it("Clearing a bundle removes all ab configs.", function() {

        stats.ParseJsForStats(bundle1, ab1);
        stats.ParseJsForStats(bundle1, ab2);

        validateBundle(bundle1, [ string1, string2 ]);

        stats.ClearStatsForBundle(bundle1);

        validateBundle(bundle1, [ ]);
    });

    it("Duplicate ab configs are not added.", function() {

        stats.ParseJsForStats(bundle1, ab1);

        validateBundle(bundle1, [ string1 ]);

        stats.ParseJsForStats(bundle1, ab1);

        validateBundle(bundle1, [ string1 ]);

        stats.ParseJsForStats(bundle1, ab1);

        validateBundle(bundle1, [ string1 ]);
    });

    it("ab configs added are isolated to their collection.", function() {

        stats.ParseJsForStats(bundle1, ab1);
        stats.ParseJsForStats(bundle2, ab2);
        stats.ParseJsForStats(bundle3, ab3);

        validateBundle(bundle1, [ string1 ]);
        validateBundle(bundle2, [ string2 ]);
        validateBundle(bundle3, [ string3 ]);
    });

 });

});<|endoftext|>


<commit_before>export { Calendar } from './TimeDropdown';
export { default } from './Timepicker';

<commit_msg>remove unneeded line from <Timepicker>'s index.js<commit_after>export { default } from './Timepicker';
<|endoftext|>


<commit_before>export default () => {
    $.ajax({
        'url': '/api/v1/articles/?format=json&featured=False',
        'method': 'GET',
        'data': {},
        success: function(data) {
            var output = '';
            var articles = data.results;
            var len = (articles.length > 6) ? 6 : articles.length; // Set length of loop to 6 if num articles is more than 6.

            // The loop
            for (var i = 0; i < len; i++) {
                output += '

'+articles[i].heading+'

';
            }

            // Appending
            $('#article-detaill-latest').html(output);
        }
    });
};
<commit_msg>eslint article details<commit_after>import $ from 'jquery';

const addRelatedArticles = (data) => {
  let output = '';
  const articles = data.results;
  // Set length of loop to 6 if num articles is more than 6.
  const len = (articles.length > 6) ? 6 : articles.length;

  for (let i = 0; i < len; i += 1) {
    output += `

${articles[i].heading}

`;
  }

  $('#article-detaill-latest').html(output);
};

export default () => {
  $.ajax({
    url: '/api/v1/articles/?format=json&featured=False',
    method: 'GET',
    data: {},
    addRelatedArticles,
  });
};
<|endoftext|>


<commit_before>/*
---
name: Facebook Angularjs

description: Provides an easier way to make use of Facebook API with Angularjs

license: MIT-style license

authors:
  - Ciul

requires: [angular]
provides: [facebook]

...
*/
(function(window, angular, undefined) {
  'use strict';

  // settings object available for module settings/services definition.
  var settings = {};

  /**
   * Facebook module
   */
  angular.module('facebook', []).

    /**
     * Facebook provider
     */
    provider('Facebook', [function() {

      /**
       * Facebook appId
       * @type {Number}
       */
      settings.appId = null;

      this.setAppId = function(appId) {
        settings.appId = appId;
      };

      this.getAppId = function(appId) {
        return settings.appId;
      };

      /**
       * Locale language, english by default
       * @type {String}
       */
      settings.locale = 'en_US';

      this.setLocale = function(locale) {
        settings.locale = locale;
      };

      this.getLocale = function() {
        return settings.locale;
      };

      /**
       * Set if you want to check the authentication status
       * at the start up of the app
       * @type {Boolean}
       */
      settings.status = true;

      this.setStatus = function(status) {
        settings.status = status;
      };

      this.getStatus = function() {
        return settings.status;
      };

      /**
       * Adding a Channel File improves the performance of the javascript SDK,
       * by addressing issues with cross-domain communication in certain browsers.
       * @type {String}
       */
      settings.channelUrl = null;

      this.setChannel = function(channel) {
        settings.channelUrl = channel;
      };

      this.getChannel = function() {
        return settings.channelUrl;
      };

      /**
       * Enable cookies to allow the server to access the session
       * @type {Boolean}
       */
      settings.cookie = true;

      this.setCookie = function(cookie) {
        settings.cookie = cookie;
      };

      this.getCookie = function() {
        return settings.cookie;
      };

      /**
       * Parse XFBML
       * @type {Boolean}
       */
      settings.xbfml = true;

      this.setXfbml = function(enable) {
        settings.xbfml = enable;
      };

      this.getXfbml = function() {
        return settings.xbfml;
      };

      /**
       * Create Facebook root element in DOM
       */
      var addFBRoot = function() {
        var fbroot = document.getElementById('fb-root');

        if (!fbroot) {
          fbroot = document.createElement('div');
          fbroot.id = 'fb-root';
          document.body.insertBefore(fbroot, document.body.childNodes[0]);
        }

        return fbroot;
      };

      /**
       * Init Facebook API required stuff
       * This will prepare the app earlier (on settingsuration)
       */
      this.init = function(customAppId) {
        settings.appId = customAppId || settings.appId; // Pass appId on init is also possible.

        // Add Facebook root element
        addFBRoot();
      };

      /**
       * This defines the Facebook Service on run.
       */
      this.$get = ['$q', '$rootScope', function($q, $rootScope) {

        /**
         * Create a new scope for listening to broadcasted events,
         * this is for better approach of asynchronous Facebook API usage.
         * @type {Object}
         */
        var facebookScope = $rootScope.$new(),

        /**
         * Create a deferred instance to implement asynchronous calls
         * @type {Object}
         */
        deferred  = $q.defer(),

        /**
         * Ready state
         * @type {Boolean}
         */
        ready = false;

        /**
         * Facebook API is ready to use
         * @param  {Object} ev
         * @param  {Object} FB
         */
        facebookScope.$on('Facebook:load', function(ev, FB) {
          ready = true;
          $rootScope.$apply(function() {
            deferred.resolve(FB);
          });
        });

        /**
         * This is the NgFacebook class to be retrieved on Facebook Service request.
         */
        function NgFacebook() {
          this.appId = settings.appId;
        }

        /**
         * Ready state method
         * @return {Boolean}
         */
        NgFacebook.prototype.isReady = function() {
          return ready;
        };

        /**
         * Map some asynchronous Facebook sdk methods to NgFacebook
         */
        angular.forEach([
          'login',
          'logout',
          'api',
          'ui',
          'getLoginStatus'
        ], function(name) {
          NgFacebook.prototype[name] = function() {
            if (angular.isUndefined(window.FB)) {
              deferred.promise.then(function() {
                FB[name].apply(FB, arguments);
              });
            } else {
              FB[name].apply(FB, arguments);
            }
          };
        });

        /**
         * Map Facebook sdk subscribe method to NgFacebook. Renamed as subscribe
         * Thus, use it as Facebook.subscribe in the service.
         */
        NgFacebook.prototype.subscribe = function() {
          if (angular.isUndefined(window.FB)) {
            deferred.promise.then(function() {
              FB.Event.subscribe.apply(FB, arguments);
            });
          } else {
            FB.Event.subscribe.apply(FB, arguments);
          }
        };

        /**
         * Map Facebook sdk unsubscribe method to NgFacebook. Renamed as unsubscribe
         * Thus, use it as Facebook.unsubscribe in the service.
         */
        NgFacebook.prototype.unsubscribe = function() {
          if (angular.isUndefined(window.FB)) {
            deferred.promise.then(function() {
              FB.Event.unsubscribe.apply(FB, arguments);
            });
          } else {
            FB.Event.unsubscribe.apply(FB, arguments);
          }
        };

        return new NgFacebook();
      }];
  }]).

  // Initialization of module
  run(['$rootScope', '$q','$window', function($rootScope, $q, $window) {

    // Define fbAsyncInit
    $window.fbAsyncInit = function() {
      // Initialize our Facebook app
      $rootScope.$apply(function() {
        FB.init(settings);
      });

      // Broadcast Facebook:load event.
      $rootScope.$broadcast('Facebook:load', FB);

      /**
       * Subscribe to Facebook API events and broadcast through app.
       */
      angular.forEach({
        'auth.login': 'login',
        'auth.logout': 'logout',
        'auth.prompt': 'prompt',
        'auth.sessionChange': 'sessionChange',
        'auth.statusChange': 'statusChange',
        'xfbml.render': 'xfbmlRender',
        'edge.create': 'like',
        'edge.remove': 'unlike',
        'comment.create': 'comment',
        'comment.remove': 'uncomment'
      }, function(mapped, name) {
        FB.Event.subscribe(name, function(response) {
          $rootScope.$broadcast('Facebook:' + mapped, response);
        });
      });
    };

    // Inject Facebook sdk script into the document head
    var src = '//connect.facebook.net/'.concat(settings.locale, '/all.js'),
    script = document.createElement('script');
    script.id = 'facebook-jssdk';
    script.async = true;
    script.src = src;
    document.head.appendChild(script);
  }]);

})(window, angular);
<commit_msg>mapped Fb.XFBML.parse() to NgFacebook<commit_after>/*
---
name: Facebook Angularjs

description: Provides an easier way to make use of Facebook API with Angularjs

license: MIT-style license

authors:
  - Ciul

requires: [angular]
provides: [facebook]

...
*/
(function(window, angular, undefined) {
  'use strict';

  // settings object available for module settings/services definition.
  var settings = {};

  /**
   * Facebook module
   */
  angular.module('facebook', []).

    /**
     * Facebook provider
     */
    provider('Facebook', [function() {

      /**
       * Facebook appId
       * @type {Number}
       */
      settings.appId = null;

      this.setAppId = function(appId) {
        settings.appId = appId;
      };

      this.getAppId = function(appId) {
        return settings.appId;
      };

      /**
       * Locale language, english by default
       * @type {String}
       */
      settings.locale = 'en_US';

      this.setLocale = function(locale) {
        settings.locale = locale;
      };

      this.getLocale = function() {
        return settings.locale;
      };

      /**
       * Set if you want to check the authentication status
       * at the start up of the app
       * @type {Boolean}
       */
      settings.status = true;

      this.setStatus = function(status) {
        settings.status = status;
      };

      this.getStatus = function() {
        return settings.status;
      };

      /**
       * Adding a Channel File improves the performance of the javascript SDK,
       * by addressing issues with cross-domain communication in certain browsers.
       * @type {String}
       */
      settings.channelUrl = null;

      this.setChannel = function(channel) {
        settings.channelUrl = channel;
      };

      this.getChannel = function() {
        return settings.channelUrl;
      };

      /**
       * Enable cookies to allow the server to access the session
       * @type {Boolean}
       */
      settings.cookie = true;

      this.setCookie = function(cookie) {
        settings.cookie = cookie;
      };

      this.getCookie = function() {
        return settings.cookie;
      };

      /**
       * Parse XFBML
       * @type {Boolean}
       */
      settings.xbfml = true;

      this.setXfbml = function(enable) {
        settings.xbfml = enable;
      };

      this.getXfbml = function() {
        return settings.xbfml;
      };

      /**
       * Create Facebook root element in DOM
       */
      var addFBRoot = function() {
        var fbroot = document.getElementById('fb-root');

        if (!fbroot) {
          fbroot = document.createElement('div');
          fbroot.id = 'fb-root';
          document.body.insertBefore(fbroot, document.body.childNodes[0]);
        }

        return fbroot;
      };

      /**
       * Init Facebook API required stuff
       * This will prepare the app earlier (on settingsuration)
       */
      this.init = function(customAppId) {
        settings.appId = customAppId || settings.appId; // Pass appId on init is also possible.

        // Add Facebook root element
        addFBRoot();
      };

      /**
       * This defines the Facebook Service on run.
       */
      this.$get = ['$q', '$rootScope', function($q, $rootScope) {

        /**
         * Create a new scope for listening to broadcasted events,
         * this is for better approach of asynchronous Facebook API usage.
         * @type {Object}
         */
        var facebookScope = $rootScope.$new(),

        /**
         * Create a deferred instance to implement asynchronous calls
         * @type {Object}
         */
        deferred  = $q.defer(),

        /**
         * Ready state
         * @type {Boolean}
         */
        ready = false;

        /**
         * Facebook API is ready to use
         * @param  {Object} ev
         * @param  {Object} FB
         */
        facebookScope.$on('Facebook:load', function(ev, FB) {
          ready = true;
          $rootScope.$apply(function() {
            deferred.resolve(FB);
          });
        });

        /**
         * This is the NgFacebook class to be retrieved on Facebook Service request.
         */
        function NgFacebook() {
          this.appId = settings.appId;
        }

        /**
         * Ready state method
         * @return {Boolean}
         */
        NgFacebook.prototype.isReady = function() {
          return ready;
        };

        /**
         * Map some asynchronous Facebook sdk methods to NgFacebook
         */
        angular.forEach([
          'login',
          'logout',
          'api',
          'ui',
          'getLoginStatus'
        ], function(name) {
          NgFacebook.prototype[name] = function() {
            if (angular.isUndefined(window.FB)) {
              deferred.promise.then(function() {
                FB[name].apply(FB, arguments);
              });
            } else {
              FB[name].apply(FB, arguments);
            }
          };
        });

        /**
         * Map Facebook sdk XFBML.parse() to NgFacebook.
         */
        NgFacebook.prototype.parseXFBML = function() {
          if (angular.isUndefined(window.FB)) {
            deferred.promise.then(function() {
              FB.XFBML.parse();
            });
          } else {
            FB.XFBML.parse();
          }
        };

        /**
         * Map Facebook sdk subscribe method to NgFacebook. Renamed as subscribe
         * Thus, use it as Facebook.subscribe in the service.
         */
        NgFacebook.prototype.subscribe = function() {
          if (angular.isUndefined(window.FB)) {
            deferred.promise.then(function() {
              FB.Event.subscribe.apply(FB, arguments);
            });
          } else {
            FB.Event.subscribe.apply(FB, arguments);
          }
        };

        /**
         * Map Facebook sdk unsubscribe method to NgFacebook. Renamed as unsubscribe
         * Thus, use it as Facebook.unsubscribe in the service.
         */
        NgFacebook.prototype.unsubscribe = function() {
          if (angular.isUndefined(window.FB)) {
            deferred.promise.then(function() {
              FB.Event.unsubscribe.apply(FB, arguments);
            });
          } else {
            FB.Event.unsubscribe.apply(FB, arguments);
          }
        };

        return new NgFacebook();
      }];
  }]).

  // Initialization of module
  run(['$rootScope', '$q','$window', function($rootScope, $q, $window) {

    // Define fbAsyncInit
    $window.fbAsyncInit = function() {
      // Initialize our Facebook app
      $rootScope.$apply(function() {
        FB.init(settings);
      });

      // Broadcast Facebook:load event.
      $rootScope.$broadcast('Facebook:load', FB);

      /**
       * Subscribe to Facebook API events and broadcast through app.
       */
      angular.forEach({
        'auth.login': 'login',
        'auth.logout': 'logout',
        'auth.prompt': 'prompt',
        'auth.sessionChange': 'sessionChange',
        'auth.statusChange': 'statusChange',
        'xfbml.render': 'xfbmlRender',
        'edge.create': 'like',
        'edge.remove': 'unlike',
        'comment.create': 'comment',
        'comment.remove': 'uncomment'
      }, function(mapped, name) {
        FB.Event.subscribe(name, function(response) {
          $rootScope.$broadcast('Facebook:' + mapped, response);
        });
      });
    };

    // Inject Facebook sdk script into the document head
    var src = '//connect.facebook.net/'.concat(settings.locale, '/all.js'),
    script = document.createElement('script');
    script.id = 'facebook-jssdk';
    script.async = true;
    script.src = src;
    document.head.appendChild(script);
  }]);

})(window, angular);
<|endoftext|>