<commit_before>(function() {
  DocsApp
    .factory('codepenDataAdapter', CodepenDataAdapter)
    .factory('codepen', ['$demoAngularScripts', '$document', 'codepenDataAdapter', Codepen]);

  // Provides a service to open a code example in codepen.
  function Codepen($demoAngularScripts, $document, codepenDataAdapter) {

    // The following URL must be HTTP and not HTTPS to allow us to do localhost testing
    var CODEPEN_API = 'http://codepen.io/pen/define/';

    return {
      editOnCodepen: editOnCodepen
    };

    // Creates a codepen from the given demo model by posting to Codepen's API
    // using a hidden form.  The hidden form is necessary to avoid a CORS issue.
    // See http://blog.codepen.io/documentation/api/prefill
    function editOnCodepen(demo) {
      var data = codepenDataAdapter.translate(demo, $demoAngularScripts.all());
      var form = buildForm(data);
      $document.find('body').append(form);
      form[0].submit();
      form.remove();
    }

    // Builds a hidden form with data necessary to create a codepen.
    function buildForm(data) {
      var form = angular.element(
        '<form style="display: none;" method="post" target="_blank" action="' +
          CODEPEN_API +
          '"></form>'
      );
      var input = '<input type="hidden" name="data" value="' + escapeJsonQuotes(data) + '" />';
      form.append(input);
      return form;
    }

    // Recommended by Codepen to escape quotes.
    // See http://blog.codepen.io/documentation/api/prefill
    function escapeJsonQuotes(json) {
      return JSON.stringify(json)
        .replace(/'/g, "&apos;")
        .replace(/"/g, "&quot;");
    }
  }

  // Translates demo metadata and files into Codepen's post form data.  See api documentation for
  // additional fields not used by this service. http://blog.codepen.io/documentation/api/prefill
  function CodepenDataAdapter() {

    // The following URL's need to use `localhost` as these values get replaced during release
    var CORE_JS  = 'http://localhost:8080/angular-material.js';
    var CORE_CSS = 'http://localhost:8080/angular-material.css';
    var DOC_CSS  = 'http://localhost:8080/docs.css';              // CSS overrides for custom docs

    var LINK_FONTS_ROBOTO = '<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700,400italic">';

    var ASSET_CACHE_JS = 'https://s3-us-west-2.amazonaws.com/s.cdpn.io/t-114/assets-cache.js';

    return {
      translate: translate
    };

    // Translates a demo model to match Codepen's post data
    // See http://blog.codepen.io/documentation/api/prefill
    function translate(demo, externalScripts) {
      var files = demo.files;

      return {
        title: demo.title,
        html: processHtml(demo),
        head: LINK_FONTS_ROBOTO,

        js: processJs(files.js),
        css: mergeFiles( files.css ).join(' '),

        js_external: externalScripts.concat([CORE_JS, ASSET_CACHE_JS]).join(';'),
        css_external: [CORE_CSS, DOC_CSS].join(';')
      };
    }

    // Modifies index.html with necessary changes in order to display correctly in codepen
    // See each processor to determine how each modifies the html
    function processHtml(demo) {

      var allContent = demo.files.index.contents;

      var processors = [
        applyAngularAttributesToParentElement,
        insertTemplatesAsScriptTags,
        htmlEscapeAmpersand
      ];

      processors.forEach(function(processor) {
        allContent = processor(allContent, demo);
      });

      return allContent + buildLicenseHTML();
    }

    function buildLicenseHTML() {
      return '\n\n'+
        '<!--\n'+
        'Copyright 2016 Google Inc. All Rights Reserved. \n'+
        'Use of this source code is governed by an MIT-style license that can be in found'+
        'in the LICENSE file at http://material.angularjs.org/license.\n'+
        '-->';
    }

    // Applies modifications the javascript prior to sending to codepen.
    // Currently merges js files and replaces the module with the Codepen
    // module.  See documentation for replaceDemoModuleWithCodepenModule.
    function processJs(jsFiles) {
      var mergedJs = mergeFiles(jsFiles).join(' ');
      var script = replaceDemoModuleWithCodepenModule(mergedJs);
      return script;
    }

    // Maps file contents to an array
    function mergeFiles(files) {
      return files.map(function(file) {
        return file.contents;
      });
    }

    // Adds class to parent element so that styles are applied correctly
    // Adds ng-app attribute.  This is the same module name provided in the asset-cache.js
    function applyAngularAttributesToParentElement(html, demo) {
      var tmp;

      // Grab only the DIV for the demo...
      angular.forEach(angular.element(html), function(it,key){
        if ((it.nodeName != "SCRIPT") && (it.nodeName != "#text")) {
          tmp = angular.element(it);
        }
      });

      tmp.addClass(demo.id);
      tmp.attr('ng-app', 'MyApp');
      return tmp[0].outerHTML;
    }

    // Adds templates inline in the html, so that templates are cached in the example
    function insertTemplatesAsScriptTags(indexHtml, demo) {
      if (demo.files.html.length) {
        var tmp = angular.element(indexHtml);
        angular.forEach(demo.files.html, function(template) {
          tmp.append("<script type='text/ng-template' id='" +
                     template.name + "'>" +
                     template.contents +
                     "</script>");
        });
        return tmp[0].outerHTML;
      }
      return indexHtml;
    }

    // Escapes ampersands so that after codepen unescapes the html the escaped code block
    // uses the correct escaped characters
    function htmlEscapeAmpersand(html) {
      return html
        .replace(/&/g, "&");
    }

    // Required to make codepen work. Demos define their own module when running on the
    // docs site.  In order to ensure the codepen example can use the asset-cache, the
    // module needs to match so that the $templateCache is populated with the necessary
    // assets.
    function replaceDemoModuleWithCodepenModule(file) {
      var matchAngularModule =  /\.module\(('[^']*'|"[^"]*")\s*,(\s*\[([^\]]*)\]\s*\))/ig;

      // Include 'ngMessages' since the 'assets-cache.js' has the same dependencies
      // angular.module('MyApp', ['ngMaterial', 'ngMessages'])
      // See scripts.js for list of external Angular libraries used for the demos

      return file.replace(matchAngularModule, ".module('MyApp',['ngMaterial', 'ngMessages'])");
    }
  }
})();
<commit_msg>update(docs): improve use of License statement in CodePen demos<commit_after>(function() {
  DocsApp
    .factory('codepenDataAdapter', CodepenDataAdapter)
    .factory('codepen', ['$demoAngularScripts', '$document', 'codepenDataAdapter', Codepen]);

  // Provides a service to open a code example in codepen.
  function Codepen($demoAngularScripts, $document, codepenDataAdapter) {

    // The following URL must be HTTP and not HTTPS to allow us to do localhost testing
    var CODEPEN_API = 'http://codepen.io/pen/define/';

    return {
      editOnCodepen: editOnCodepen
    };

    // Creates a codepen from the given demo model by posting to Codepen's API
    // using a hidden form.  The hidden form is necessary to avoid a CORS issue.
    // See http://blog.codepen.io/documentation/api/prefill
    function editOnCodepen(demo) {
      var data = codepenDataAdapter.translate(demo, $demoAngularScripts.all());
      var form = buildForm(data);
      $document.find('body').append(form);
      form[0].submit();
      form.remove();
    }

    // Builds a hidden form with data necessary to create a codepen.
    function buildForm(data) {
      var form = angular.element(
        '<form style="display: none;" method="post" target="_blank" action="' +
          CODEPEN_API +
          '"></form>'
      );
      var input = '<input type="hidden" name="data" value="' + escapeJsonQuotes(data) + '" />';
      form.append(input);
      return form;
    }

    // Recommended by Codepen to escape quotes.
    // See http://blog.codepen.io/documentation/api/prefill
    function escapeJsonQuotes(json) {
      return JSON.stringify(json)
        .replace(/'/g, "&apos;")
        .replace(/"/g, "&quot;");
    }
  }

  // Translates demo metadata and files into Codepen's post form data.  See api documentation for
  // additional fields not used by this service. http://blog.codepen.io/documentation/api/prefill
  function CodepenDataAdapter() {

    // The following URL's need to use `localhost` as these values get replaced during release
    var CORE_JS  = 'http://localhost:8080/angular-material.js';
    var CORE_CSS = 'http://localhost:8080/angular-material.css';
    var DOC_CSS  = 'http://localhost:8080/docs.css';              // CSS overrides for custom docs

    var LINK_FONTS_ROBOTO = '<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700,400italic">';

    var ASSET_CACHE_JS = 'https://s3-us-west-2.amazonaws.com/s.cdpn.io/t-114/assets-cache.js';

    return {
      translate: translate
    };

    // Translates a demo model to match Codepen's post data
    // See http://blog.codepen.io/documentation/api/prefill
    function translate(demo, externalScripts) {
      var files = demo.files;

      return {
        title: demo.title,
        html: processHtml(demo),
        head: LINK_FONTS_ROBOTO,

        js: appendLicense(processJs(files.js), 'js'),
        css: appendLicense(mergeFiles( files.css ).join(' '), 'css'),

        js_external: externalScripts.concat([CORE_JS, ASSET_CACHE_JS]).join(';'),
        css_external: [CORE_CSS, DOC_CSS].join(';')
      };
    }

    // Modifies index.html with necessary changes in order to display correctly in codepen
    // See each processor to determine how each modifies the html
    function processHtml(demo) {

      var allContent = demo.files.index.contents;

      var processors = [
        applyAngularAttributesToParentElement,
        insertTemplatesAsScriptTags,
        htmlEscapeAmpersand
      ];

      processors.forEach(function(processor) {
        allContent = processor(allContent, demo);
      });

      return appendLicense(allContent, 'html');
    }

    /**
     * Append MIT License information to all CodePen source samples(HTML, JS, CSS)
     */
    function appendLicense(content, lang) {
      var commentStart = '', commentEnd = '';

      switch(lang) {
        case 'html' : commentStart = '<!--'; commentEnd = '-->'; break;
        case 'js'   : commentStart = '/**';  commentEnd = '**/'; break;
        case 'css'  : commentStart = '/*';   commentEnd = '*/';  break;
      }

      return content + '\n\n'+
        commentStart + '\n'+
        'Copyright 2016 Google Inc. All Rights Reserved. \n'+
        'Use of this source code is governed by an MIT-style license that can be in found'+
        'in the LICENSE file at http://material.angularjs.org/license.\n'+
        commentEnd;
    }

    // Applies modifications the javascript prior to sending to codepen.
    // Currently merges js files and replaces the module with the Codepen
    // module.  See documentation for replaceDemoModuleWithCodepenModule.
    function processJs(jsFiles) {
      var mergedJs = mergeFiles(jsFiles).join(' ');
      var script = replaceDemoModuleWithCodepenModule(mergedJs);
      return script;
    }

    // Maps file contents to an array
    function mergeFiles(files) {
      return files.map(function(file) {
        return file.contents;
      });
    }

    // Adds class to parent element so that styles are applied correctly
    // Adds ng-app attribute.  This is the same module name provided in the asset-cache.js
    function applyAngularAttributesToParentElement(html, demo) {
      var tmp;

      // Grab only the DIV for the demo...
      angular.forEach(angular.element(html), function(it,key){
        if ((it.nodeName != "SCRIPT") && (it.nodeName != "#text")) {
          tmp = angular.element(it);
        }
      });

      tmp.addClass(demo.id);
      tmp.attr('ng-app', 'MyApp');
      return tmp[0].outerHTML;
    }

    // Adds templates inline in the html, so that templates are cached in the example
    function insertTemplatesAsScriptTags(indexHtml, demo) {
      if (demo.files.html.length) {
        var tmp = angular.element(indexHtml);
        angular.forEach(demo.files.html, function(template) {
          tmp.append("<script type='text/ng-template' id='" +
                     template.name + "'>" +
                     template.contents +
                     "</script>");
        });
        return tmp[0].outerHTML;
      }
      return indexHtml;
    }

    // Escapes ampersands so that after codepen unescapes the html the escaped code block
    // uses the correct escaped characters
    function htmlEscapeAmpersand(html) {
      return html
        .replace(/&/g, "&");
    }

    // Required to make codepen work. Demos define their own module when running on the
    // docs site.  In order to ensure the codepen example can use the asset-cache, the
    // module needs to match so that the $templateCache is populated with the necessary
    // assets.
    function replaceDemoModuleWithCodepenModule(file) {
      var matchAngularModule =  /\.module\(('[^']*'|"[^"]*")\s*,(\s*\[([^\]]*)\]\s*\))/ig;

      // Include 'ngMessages' since the 'assets-cache.js' has the same dependencies
      // angular.module('MyApp', ['ngMaterial', 'ngMessages'])
      // See scripts.js for list of external Angular libraries used for the demos

      return file.replace(matchAngularModule, ".module('MyApp',['ngMaterial', 'ngMessages'])");
    }
  }
})();
<|endoftext|>


<commit_before>var React = require('react');
var DocsArticle = require('../../DocsArticle');
var Spinning = require('grommet/components/icons/Spinning');

var SpinningDoc = React.createClass({
  render: function() {
    return (
      <DocsArticle title="Spinning" colorIndex="neutral-3">

An indeterminate spinning/busy icon. This should be used sparingly.
        If at all possible, use Meter with % to indicate progress. For content
        loading situations, Meter, Chart, and Distribution already have
        visuals for when the data has not arrived yet. In general,
        there should not be more than one Spinning icon on the screen at a
        time.

Example

            <Spinning />

            {"<Spinning />"}

      </DocsArticle>
    );
  }
});

module.exports = SpinningDoc;
<commit_msg>Updated Spinning docs to show how to import it.<commit_after>var React = require('react');
var DocsArticle = require('../../DocsArticle');
var Spinning = require('grommet/components/icons/Spinning');

const inline = `
var Spinning = require('grommet/components/icons/Spinning');
//or var Spinning = Grommet.Icons.Spinning
`;

var SpinningDoc = React.createClass({
  render: function() {
    return (
      <DocsArticle title="Spinning" colorIndex="neutral-3">

An indeterminate spinning/busy icon. This should be used sparingly.
        If at all possible, use Meter with % to indicate progress. For content
        loading situations, Meter, Chart, and Distribution already have
        visuals for when the data has not arrived yet. In general,
        there should not be more than one Spinning icon on the screen at a
        time.

            {inline}

Example

            <Spinning />

            {"<Spinning />"}

      </DocsArticle>
    );
  }
});

module.exports = SpinningDoc;
<|endoftext|>


<commit_before>import {
  EditorPackage as TextureEditorPackage
} from 'substance-texture'

import ReproFigComponent from './ReproFigComponent'
import CellComponent from './CellComponent'
import CodeHighlightComponent from '../shared/CodeHighlightComponent'

import BooleanValueComponent from '../shared/BooleanValueComponent'
import NumberValueComponent from '../shared/NumberValueComponent'
import IntegerValueComponent from '../shared/IntegerValueComponent'
import StringValueComponent from '../shared/StringValueComponent'
import ArrayValueComponent from '../shared/ArrayValueComponent'
import ObjectValueComponent from '../shared/ObjectValueComponent'
import TableValueComponent from '../shared/TableValueComponent'
import TestValueComponent from '../shared/TestValueComponent'
import ImageValueComponent from '../shared/ImageValueComponent'
import PlotlyValueComponent from '../shared/PlotlyValueComponent'

import { SetLanguageCommand, ToggleAllCodeCommand, HideCellCodeCommand } from './DocumentCommands'
import FunctionUsageCommand from '../shared/FunctionUsageCommand'
import FunctionUsageTool from '../shared/FunctionUsageTool'

export default {
  name: 'editor',
  configure(config) {
    config.import(TextureEditorPackage)
    config.addComponent('repro-fig', ReproFigComponent)
    config.addComponent('cell', CellComponent)
    config.addComponent('code-highlight', CodeHighlightComponent)

    config.addComponent('value:boolean', BooleanValueComponent)
    config.addComponent('value:integer', IntegerValueComponent)
    config.addComponent('value:number', NumberValueComponent)
    config.addComponent('value:string', StringValueComponent)
    config.addComponent('value:array', ArrayValueComponent)
    config.addComponent('value:object', ObjectValueComponent)
    config.addComponent('value:table', TableValueComponent)
    config.addComponent('value:test', TestValueComponent)
    config.addComponent('value:image', ImageValueComponent)
    config.addComponent('value:plotly', PlotlyValueComponent)

    config.addCommand('function-usage', FunctionUsageCommand, {
      commandGroup: 'prompt'
    })
    config.addTool('function-usage', FunctionUsageTool)

    config.addToolPanel('toolbar', [
      {
        name: 'text-types',
        type: 'tool-dropdown',
        showDisabled: false,
        style: 'descriptive',
        commandGroups: ['text-types']
      },
      {
        name: 'persistence',
        type: 'tool-group',
        showDisabled: true,
        style: 'descriptive',
        commandGroups: ['persistence']
      },
      {
        name: 'annotations',
        type: 'tool-group',
        showDisabled: true,
        style: 'minimal',
        commandGroups: ['formatting']
      },
      {
        name: 'insert',
        type: 'tool-dropdown',
        showDisabled: true,
        style: 'descriptive',
        commandGroups: ['insert-xref', 'insert-block-element']
      },
      {
        name: 'view',
        type: 'tool-dropdown',
        showDisabled: false,
        style: 'descriptive',
        commandGroups: ['view']
      }
    ])

    config.addToolPanel('node-menu', [
      {
        name: 'cell-actions',
        type: 'tool-group',
        style: 'descriptive',
        showDisabled: false,
        commandGroups: ['cell-actions']
      }
    ])

    /*
      Cell Actions
    */

    // config.addCommand('force-cell-output', ToggleCodeCommand, { forceOutput: true, commandGroup: 'cell-actions' })

    config.addCommand('hide-cell-code', HideCellCodeCommand, { commandGroup: 'cell-actions' })
    config.addCommand('set-mini', SetLanguageCommand, { language: 'mini', commandGroup: 'cell-actions' })
    config.addCommand('set-js', SetLanguageCommand, { language: 'js', commandGroup: 'cell-actions' })
    config.addCommand('set-py', SetLanguageCommand, { language: 'py', commandGroup: 'cell-actions' })
    config.addCommand('set-r', SetLanguageCommand, { language: 'r', commandGroup: 'cell-actions' })
    config.addCommand('set-sql', SetLanguageCommand, { language: 'sql', commandGroup: 'cell-actions' })

    // Labels and icons
    config.addLabel('hide-cell-code', 'Hide code')
    config.addLabel('force-cell-output', 'Show output')
    config.addLabel('set-mini', 'Mini')
    config.addLabel('set-js', 'Javascript')
    config.addLabel('set-py', 'Python')
    config.addLabel('set-r', 'R')
    config.addLabel('set-sql', 'SQL')

    config.addIcon('ellipsis', { 'fontawesome': 'fa-ellipsis-v' })

    config.addLabel('view', 'View')
    config.addLabel('show-all-code', 'Show All Code')
    config.addLabel('hide-all-code', 'Hide All Code')

    // View Commands
    config.addCommand('hide-all-code', ToggleAllCodeCommand, {
      hideCode: true,
      commandGroup: 'view'
    })
    config.addCommand('show-all-code', ToggleAllCodeCommand, {
      hideCode: false,
      commandGroup: 'view'
    })

    config.addKeyboardShortcut('CommandOrControl+Alt+L', { command: 'show-all-code' })
    config.addKeyboardShortcut('CommandOrControl+Alt+O', { command: 'hide-all-code' })

  }
}
<commit_msg>Add icons for test values<commit_after>import {
  EditorPackage as TextureEditorPackage
} from 'substance-texture'

import ReproFigComponent from './ReproFigComponent'
import CellComponent from './CellComponent'
import CodeHighlightComponent from '../shared/CodeHighlightComponent'

import BooleanValueComponent from '../shared/BooleanValueComponent'
import NumberValueComponent from '../shared/NumberValueComponent'
import IntegerValueComponent from '../shared/IntegerValueComponent'
import StringValueComponent from '../shared/StringValueComponent'
import ArrayValueComponent from '../shared/ArrayValueComponent'
import ObjectValueComponent from '../shared/ObjectValueComponent'
import TableValueComponent from '../shared/TableValueComponent'
import TestValueComponent from '../shared/TestValueComponent'
import ImageValueComponent from '../shared/ImageValueComponent'
import PlotlyValueComponent from '../shared/PlotlyValueComponent'

import { SetLanguageCommand, ToggleAllCodeCommand, HideCellCodeCommand } from './DocumentCommands'
import FunctionUsageCommand from '../shared/FunctionUsageCommand'
import FunctionUsageTool from '../shared/FunctionUsageTool'

export default {
  name: 'editor',
  configure(config) {
    config.import(TextureEditorPackage)
    config.addComponent('repro-fig', ReproFigComponent)
    config.addComponent('cell', CellComponent)
    config.addComponent('code-highlight', CodeHighlightComponent)

    config.addComponent('value:boolean', BooleanValueComponent)
    config.addComponent('value:integer', IntegerValueComponent)
    config.addComponent('value:number', NumberValueComponent)
    config.addComponent('value:string', StringValueComponent)
    config.addComponent('value:array', ArrayValueComponent)
    config.addComponent('value:object', ObjectValueComponent)
    config.addComponent('value:table', TableValueComponent)
    config.addComponent('value:test', TestValueComponent)
    config.addComponent('value:image', ImageValueComponent)
    config.addComponent('value:plotly', PlotlyValueComponent)

    config.addCommand('function-usage', FunctionUsageCommand, {
      commandGroup: 'prompt'
    })
    config.addTool('function-usage', FunctionUsageTool)

    config.addToolPanel('toolbar', [
      {
        name: 'text-types',
        type: 'tool-dropdown',
        showDisabled: false,
        style: 'descriptive',
        commandGroups: ['text-types']
      },
      {
        name: 'persistence',
        type: 'tool-group',
        showDisabled: true,
        style: 'descriptive',
        commandGroups: ['persistence']
      },
      {
        name: 'annotations',
        type: 'tool-group',
        showDisabled: true,
        style: 'minimal',
        commandGroups: ['formatting']
      },
      {
        name: 'insert',
        type: 'tool-dropdown',
        showDisabled: true,
        style: 'descriptive',
        commandGroups: ['insert-xref', 'insert-block-element']
      },
      {
        name: 'view',
        type: 'tool-dropdown',
        showDisabled: false,
        style: 'descriptive',
        commandGroups: ['view']
      }
    ])

    config.addToolPanel('node-menu', [
      {
        name: 'cell-actions',
        type: 'tool-group',
        style: 'descriptive',
        showDisabled: false,
        commandGroups: ['cell-actions']
      }
    ])

    /*
      Cell Actions
    */

    // config.addCommand('force-cell-output', ToggleCodeCommand, { forceOutput: true, commandGroup: 'cell-actions' })

    config.addCommand('hide-cell-code', HideCellCodeCommand, { commandGroup: 'cell-actions' })
    config.addCommand('set-mini', SetLanguageCommand, { language: 'mini', commandGroup: 'cell-actions' })
    config.addCommand('set-js', SetLanguageCommand, { language: 'js', commandGroup: 'cell-actions' })
    config.addCommand('set-py', SetLanguageCommand, { language: 'py', commandGroup: 'cell-actions' })
    config.addCommand('set-r', SetLanguageCommand, { language: 'r', commandGroup: 'cell-actions' })
    config.addCommand('set-sql', SetLanguageCommand, { language: 'sql', commandGroup: 'cell-actions' })

    // Labels and icons
    config.addLabel('hide-cell-code', 'Hide code')
    config.addLabel('force-cell-output', 'Show output')
    config.addLabel('set-mini', 'Mini')
    config.addLabel('set-js', 'Javascript')
    config.addLabel('set-py', 'Python')
    config.addLabel('set-r', 'R')
    config.addLabel('set-sql', 'SQL')

    config.addIcon('ellipsis', { 'fontawesome': 'fa-ellipsis-v' })
    config.addIcon('test-failed', {'fontawesome': 'fa-times' })
    config.addIcon('test-passed', {'fontawesome': 'fa-check' })

    config.addLabel('view', 'View')
    config.addLabel('show-all-code', 'Show All Code')
    config.addLabel('hide-all-code', 'Hide All Code')

    // View Commands
    config.addCommand('hide-all-code', ToggleAllCodeCommand, {
      hideCode: true,
      commandGroup: 'view'
    })
    config.addCommand('show-all-code', ToggleAllCodeCommand, {
      hideCode: false,
      commandGroup: 'view'
    })

    config.addKeyboardShortcut('CommandOrControl+Alt+L', { command: 'show-all-code' })
    config.addKeyboardShortcut('CommandOrControl+Alt+O', { command: 'hide-all-code' })

  }
}
<|endoftext|>


<commit_before>/*
* SpriteSheetUtils
* Visit http://createjs.com/ for documentation, updates and examples.
*
* Copyright (c) 2010 gskinner.com, inc.
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/

// namespace:
this.createjs = this.createjs||{};

(function() {
// constructor:
/**
* The SpriteSheetUtils class is a collection of static methods for working
* with sprite sheets.  A sprite sheet is a series of images (usually animation frames)
* combined into a single image on a regular grid. For example, an animation consisting
* of 8 100x100 images could be combined into a 400x200 sprite sheet (4 frames across by 2 high).
* The SpriteSheetUtils class uses a static interface and should not be instantiated.
* @class SpriteSheetUtils
* @static
**/
var SpriteSheetUtils = function() {
	throw "SpriteSheetUtils cannot be instantiated";
}

	/**
	 * @property _workingCanvas
	 * @static
	 * @type HTMLCanvasElement
	 * @protected
	*/
	SpriteSheetUtils._workingCanvas = createjs.createCanvas?createjs.createCanvas():document.createElement("canvas");

	/**
	 * @property _workingContext
	 * @static
	 * @type CanvasRenderingContext2D
	 * @protected
	*/
	SpriteSheetUtils._workingContext = SpriteSheetUtils._workingCanvas.getContext("2d");

// public static methods:
	/**
	 * This is an experimental method, and is likely to be buggy. Please report issues.

	 * Extends the existing sprite sheet by flipping the original frames either horizontally, vertically, or both,
	 * and adding appropriate animation & frame data.
	 * @method flip
	 * @static
	 * @param {Image} spriteSheet The sprite sheet to use as the source.
	 * @param {Object} flipData A generic object that specifies which frames will be flipped, what to name the
	 * flipped result, and how to flip the frames (horizontally, vertically, or both). Each property name
	 * indicates the name of a new sequence to create, and should reference an array where the first index is
	 * the name of the original sequence to flip, the second index indicates whether to flip it horizontally,
	 * the third index indicates whether to flip it vertically, and the fourth indicates what the "next" value
	 * for the resulting frame data should be. For example, the following would create a new sequence named
	 * "walk_left" consisting of the frames from the original "walk_right" sequence flipped
	 * horizontally: {walk_left: ["walk_right", true, false]}
	 **/
	SpriteSheetUtils.addFlippedFrames = function(spriteSheet, horizontal, vertical, both) {
		if (!horizontal && !vertical && !both) { return; }

		var count = 0;
		if (horizontal) { SpriteSheetUtils._flip(spriteSheet,++count,true,false); }
		if (vertical) { SpriteSheetUtils._flip(spriteSheet,++count,false,true); }
		if (both) { SpriteSheetUtils._flip(spriteSheet,++count,true,true); }
	}

	/**
	 * Returns a single frame of the specified sprite sheet as a new PNG image.
	 * @method extractFrame
	 * @static
	 * @param {Image} spriteSheet The SpriteSheet instance to extract a frame from.
	 * @param {Number} frame The frame number or animation name to extract. If an animation
	 * name is specified, only the first frame of the animation will be extracted.
	 * @return {Image} a single frame of the specified sprite sheet as a new PNG image.
	*/
	SpriteSheetUtils.extractFrame = function(spriteSheet, frame) {
		if (isNaN(frame)) {
			frame = spriteSheet.getAnimation(frame).frames[0];
		}
		var data = spriteSheet.getFrame(frame);
		if (!data) { return null; }
		var r = data.rect;
		var canvas = SpriteSheetUtils._workingCanvas;
		canvas.width = r.width;
		canvas.height = r.height;
		SpriteSheetUtils._workingContext.drawImage(data.image, r.x, r.y, r.width, r.height, 0, 0, r.width, r.height);
		var img = new Image();
		img.src = canvas.toDataURL("image/png");
		return img;
	}

	/**
	 * Merges the rgb channels of one image with the alpha channel of another. This can be used to combine a compressed
	 * JPEG image containing color data with a PNG32 monochromatic image containing alpha data. With certain types of
	 * images (those with detail that lend itself to JPEG compression) this can provide significant file size savings
	 * versus a single RGBA PNG32. This method is very fast (generally on the order of 1-2 ms to run).
	 * @method mergeAlpha
	 * @static
	 * @param {Image} rbgImage The image (or canvas) containing the RGB channels to use.
	 * @param {Image} alphaImage The image (or canvas) containing the alpha channel to use.
	 * @param {Canvas} canvas Optional. If specified, this canvas will be used and returned. If not, a new canvas will be created.
	 * @return {Canvas} A canvas with the combined image data. This can be used as a source for Bitmap or SpriteSheet.
	*/
	SpriteSheetUtils.mergeAlpha = function(rgbImage, alphaImage, canvas) {
		if (!canvas) { canvas = createjs.createCanvas?createjs.createCanvas():document.createElement("canvas"); }
		canvas.width = Math.max(alphaImage.width, rgbImage.width);
		canvas.height = Math.max(alphaImage.height, rgbImage.height);
		var ctx = canvas.getContext("2d");
		ctx.save();
		ctx.drawImage(rgbImage,0,0);
		ctx.globalCompositeOperation = "destination-in";
		ctx.drawImage(alphaImage,0,0);
		ctx.restore();
		return canvas;
	}

// private static methods:
	SpriteSheetUtils._flip = function(spriteSheet, count, h, v) {
		var imgs = spriteSheet._images;
		var canvas = SpriteSheetUtils._workingCanvas;
		var ctx = SpriteSheetUtils._workingContext;
		var il = imgs.length/count;
		for (var i=0;i<il;i++) {
			var src = imgs[i];
			src.__tmp = i; // a bit hacky, but faster than doing indexOf below.
			canvas.width = 0; // make sure it clears.
			canvas.width = src.width;
			canvas.height = src.height;
			ctx.setTransform(h?-1:1, 0, 0, v?-1:1, h?src.width:0, v?src.height:0);
			ctx.drawImage(src,0,0);
			var img = new Image();
			img.src = canvas.toDataURL("image/png");
			// work around a strange bug in Safari:
			img.width = src.width;
			img.height = src.height;
			imgs.push(img);
		}

		var frames = spriteSheet._frames;
		var fl = frames.length/count;
		for (i=0;i<fl;i++) {
			src = frames[i];
			var rect = src.rect.clone();
			img = imgs[src.image.__tmp+il*count];

			var frame = {image:img,rect:rect,regX:src.regX,regY:src.regY};
			if (h) {
				rect.x = img.width-rect.x-rect.width; // update rect
				frame.regX = rect.width-src.regX; // update registration point
			}
			if (v) {
				rect.y = img.height-rect.y-rect.height;  // update rect
				frame.regY = rect.height-src.regY; // update registration point
			}
			frames.push(frame);
		}

		var sfx = "_"+(h?"h":"")+(v?"v":"");
		var names = spriteSheet._animations;
		var data = spriteSheet._data;
		var al = names.length/count;
		for (i=0;i<al;i++) {
			var name = names[i];
			src = data[name];
			var anim = {name:name+sfx,frequency:src.frequency,next:src.next,frames:[]};
			if (src.next) { anim.next += sfx; }
			frames = src.frames;
			for (var j=0,l=frames.length;j<l;j++) {
				anim.frames.push(frames[j]+fl*count);
			}
			data[anim.name] = anim;
			names.push(anim.name);
		}
	}

createjs.SpriteSheetUtils = SpriteSheetUtils;
}());<commit_msg>Fixes to SpriteSheetUtils documentation (added .addFlippedFrames, removed .flip)<commit_after>/*
* SpriteSheetUtils
* Visit http://createjs.com/ for documentation, updates and examples.
*
* Copyright (c) 2010 gskinner.com, inc.
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/

// namespace:
this.createjs = this.createjs||{};

(function() {
// constructor:
/**
* The SpriteSheetUtils class is a collection of static methods for working
* with sprite sheets.  A sprite sheet is a series of images (usually animation frames)
* combined into a single image on a regular grid. For example, an animation consisting
* of 8 100x100 images could be combined into a 400x200 sprite sheet (4 frames across by 2 high).
* The SpriteSheetUtils class uses a static interface and should not be instantiated.
* @class SpriteSheetUtils
* @static
**/
var SpriteSheetUtils = function() {
	throw "SpriteSheetUtils cannot be instantiated";
}

	/**
	 * @property _workingCanvas
	 * @static
	 * @type HTMLCanvasElement
	 * @protected
	*/
	SpriteSheetUtils._workingCanvas = createjs.createCanvas?createjs.createCanvas():document.createElement("canvas");

	/**
	 * @property _workingContext
	 * @static
	 * @type CanvasRenderingContext2D
	 * @protected
	*/
	SpriteSheetUtils._workingContext = SpriteSheetUtils._workingCanvas.getContext("2d");

// public static methods:
	/**
	 * This is an experimental method, and may be buggy. Please report issues.

	 * Extends the existing sprite sheet by flipping the original frames horizontally, vertically, or both,
	 * and adding appropriate animation & frame data. The flipped animations will have a suffix added to their names
	 * (_h, _v, _hv as appropriate).
	 *

	 * For example:

	 * SpriteSheetUtils.addFlippedFrames(mySpriteSheet, true, true);
	 * The above would add frames that are flipped horizontally AND frames that are flipped vertically.
	 *

	 * Note that you can also flip any display object by setting its scaleX or scaleY to a negative value. On some
	 * browsers (especially those without hardware accelerated canvas) this can result in slightly degraded performance,
	 * which is why addFlippedFrames is available.
	 * @method addFlippedFrames
	 * @static
	 * @param {SpriteSheet} spriteSheet
	 * @param {Boolean} horizontal If true, horizontally flipped frames will be added.
	 * @param {Boolean} vertical If true, vertically flipped frames will be added.
	 * @param {Boolean} both If true, frames that are flipped both horizontally and vertically will be added.
	 **/
	SpriteSheetUtils.addFlippedFrames = function(spriteSheet, horizontal, vertical, both) {
		if (!horizontal && !vertical && !both) { return; }

		var count = 0;
		if (horizontal) { SpriteSheetUtils._flip(spriteSheet,++count,true,false); }
		if (vertical) { SpriteSheetUtils._flip(spriteSheet,++count,false,true); }
		if (both) { SpriteSheetUtils._flip(spriteSheet,++count,true,true); }
	}

	/**
	 * Returns a single frame of the specified sprite sheet as a new PNG image.
	 * @method extractFrame
	 * @static
	 * @param {Image} spriteSheet The SpriteSheet instance to extract a frame from.
	 * @param {Number} frame The frame number or animation name to extract. If an animation
	 * name is specified, only the first frame of the animation will be extracted.
	 * @return {Image} a single frame of the specified sprite sheet as a new PNG image.
	*/
	SpriteSheetUtils.extractFrame = function(spriteSheet, frame) {
		if (isNaN(frame)) {
			frame = spriteSheet.getAnimation(frame).frames[0];
		}
		var data = spriteSheet.getFrame(frame);
		if (!data) { return null; }
		var r = data.rect;
		var canvas = SpriteSheetUtils._workingCanvas;
		canvas.width = r.width;
		canvas.height = r.height;
		SpriteSheetUtils._workingContext.drawImage(data.image, r.x, r.y, r.width, r.height, 0, 0, r.width, r.height);
		var img = new Image();
		img.src = canvas.toDataURL("image/png");
		return img;
	}

	/**
	 * Merges the rgb channels of one image with the alpha channel of another. This can be used to combine a compressed
	 * JPEG image containing color data with a PNG32 monochromatic image containing alpha data. With certain types of
	 * images (those with detail that lend itself to JPEG compression) this can provide significant file size savings
	 * versus a single RGBA PNG32. This method is very fast (generally on the order of 1-2 ms to run).
	 * @method mergeAlpha
	 * @static
	 * @param {Image} rbgImage The image (or canvas) containing the RGB channels to use.
	 * @param {Image} alphaImage The image (or canvas) containing the alpha channel to use.
	 * @param {Canvas} canvas Optional. If specified, this canvas will be used and returned. If not, a new canvas will be created.
	 * @return {Canvas} A canvas with the combined image data. This can be used as a source for Bitmap or SpriteSheet.
	*/
	SpriteSheetUtils.mergeAlpha = function(rgbImage, alphaImage, canvas) {
		if (!canvas) { canvas = createjs.createCanvas?createjs.createCanvas():document.createElement("canvas"); }
		canvas.width = Math.max(alphaImage.width, rgbImage.width);
		canvas.height = Math.max(alphaImage.height, rgbImage.height);
		var ctx = canvas.getContext("2d");
		ctx.save();
		ctx.drawImage(rgbImage,0,0);
		ctx.globalCompositeOperation = "destination-in";
		ctx.drawImage(alphaImage,0,0);
		ctx.restore();
		return canvas;
	}

// private static methods:
	SpriteSheetUtils._flip = function(spriteSheet, count, h, v) {
		var imgs = spriteSheet._images;
		var canvas = SpriteSheetUtils._workingCanvas;
		var ctx = SpriteSheetUtils._workingContext;
		var il = imgs.length/count;
		for (var i=0;i<il;i++) {
			var src = imgs[i];
			src.__tmp = i; // a bit hacky, but faster than doing indexOf below.
			canvas.width = 0; // make sure it clears.
			canvas.width = src.width;
			canvas.height = src.height;
			ctx.setTransform(h?-1:1, 0, 0, v?-1:1, h?src.width:0, v?src.height:0);
			ctx.drawImage(src,0,0);
			var img = new Image();
			img.src = canvas.toDataURL("image/png");
			// work around a strange bug in Safari:
			img.width = src.width;
			img.height = src.height;
			imgs.push(img);
		}

		var frames = spriteSheet._frames;
		var fl = frames.length/count;
		for (i=0;i<fl;i++) {
			src = frames[i];
			var rect = src.rect.clone();
			img = imgs[src.image.__tmp+il*count];

			var frame = {image:img,rect:rect,regX:src.regX,regY:src.regY};
			if (h) {
				rect.x = img.width-rect.x-rect.width; // update rect
				frame.regX = rect.width-src.regX; // update registration point
			}
			if (v) {
				rect.y = img.height-rect.y-rect.height;  // update rect
				frame.regY = rect.height-src.regY; // update registration point
			}
			frames.push(frame);
		}

		var sfx = "_"+(h?"h":"")+(v?"v":"");
		var names = spriteSheet._animations;
		var data = spriteSheet._data;
		var al = names.length/count;
		for (i=0;i<al;i++) {
			var name = names[i];
			src = data[name];
			var anim = {name:name+sfx,frequency:src.frequency,next:src.next,frames:[]};
			if (src.next) { anim.next += sfx; }
			frames = src.frames;
			for (var j=0,l=frames.length;j<l;j++) {
				anim.frames.push(frames[j]+fl*count);
			}
			data[anim.name] = anim;
			names.push(anim.name);
		}
	}

createjs.SpriteSheetUtils = SpriteSheetUtils;
}());<|endoftext|>


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

/*
 * Copyright (c) 2014, Joyent, Inc.
 */

/*
 * Provision Limits Plugin.
 *
 * See Appendix A of CloudAPI Administrator Guide for the whole details
 * on how the plugin works:
 *
 * https://mo.joyent.com/docs/cloudapi/master/admin.html
 *
 */

var assert = require('assert');
var util = require('util');
var restify = require('restify');
var vasync = require('vasync');

// --- Globals

var CODE = 'QuotaExceeded';
var MESSAGE = 'To have your limits raised please contact Support.';

/*
 * Separated function in order to make testing easier.
 *
 * Unique task for this function is to return which limits will be
 * applied for a given customer. Required information for the function includes
 * the specific limits, the global limits and the image which will be used for
 * provisioning. All this information can be obtained from the request object.
 *
 * Please, note this function will just return an array with the limits to be
 * applied for the given request, even if that array is empty.
 */
function filterLimits(req_image, cfg_limits, limits) {

    // First, filter relevant limits from configuration (defaults):
    cfg_limits = cfg_limits.filter(function (l) {
        return ((l.os && l.os === 'any') ||
                (l.image && l.image === 'any') ||
                (l.os && l.check && l.os === req_image.os &&
                    l.check === 'os') ||
                (l.image && (l.image === req_image.name ||
                    l.image === req_image.name.toLowerCase())));
    });

    // At this point we should have a single limits entry, let's
    // convert from capi_limits before we go further
    if (limits.length) {
        var parsedLimits = [];
        Object.keys(limits[0]).forEach(function (k) {
            if (k === 'limit') {
                if (typeof (limits[0][k]) === 'string') {
                    limits[0][k] = [limits[0][k]];
                }
                if (!Array.isArray(limits[0][k]) &&
                    typeof (limits[0][k]) === 'object') {
                    parsedLimits.push(limits[0][k]);
                } else {
                    limits[0][k].forEach(function (j) {
                        try {
                            parsedLimits.push(JSON.parse(j));
                        } catch (e) {}
                    });
                }
            } else if (['dn',
                'controls',
                '_parent',
                '_owner',
                'objectclass',
                'datacenter'].indexOf(k) === -1) {
                // This is an old capi_limit: check image by
                // number of machines:
                parsedLimits.push({
                    image: k,
                    check: 'image',
                    by: 'machines',
                    value: limits[0][k]
                });
            }
        });
        limits = parsedLimits;
    }

    limits = limits.filter(function (l) {
        return ((l.os && l.os === 'any') ||
                (l.image && l.image === 'any') ||
                (l.os && l.check && l.os === req_image.os &&
                    l.check === 'os') ||
                (l.image && (l.image === req_image.name.toLowerCase() ||
                            l.image === req_image.name)));
    });
    // Check if the customer has a 'catch all' limit specified and, if so
    // skip adding anything from the cfg_limits:
    var catchAll = limits.some(function (l) {
        return ((l.os && l.os === 'any') || (l.image && l.image === 'any'));
    });

    // Next, from cfg_limits, take any limit which is different
    // than the ones from UFDS:
    cfg_limits.filter(function (cfg_l) {
        if (!cfg_l.by) {
            return false;
        }
        var exists = limits.some(function (l) {
            return (l.check && cfg_l.check &&
                    l.check === cfg_l.check &&
                    l.by === cfg_l.by);
        });

        return (!exists);
    });

    // Push any limits not the same to our limits list:
    if (cfg_limits.length > 0 && !catchAll && limits.length === 0) {
        limits = limits.concat(cfg_limits);
    }
    return (limits);
}

module.exports = {
    filterLimits: filterLimits,
    preProvision: function (cfg) {
        if (!cfg || typeof (cfg) !== 'object') {
            throw new TypeError('cfg (object) is required');
        }

        // In the future we may replace this with terms like "any" or "all"
        // and make possible apply the same configuration to every datacenter.
        if (!cfg.datacenter) {
            throw new TypeError('cfg.datacenter is required');
        }

        if (!cfg.defaults || typeof (cfg.defaults) !== 'object') {
            throw new TypeError('cfg.defaults (object) is required');
        }

        return function provisioningLimits(req, res, next) {
            // Do nothing if we are not provisioning, (not strictly required,
            // since CloudAPI will handle this, but illustrative):
            if (!(/\/machines$/.test(req.url) &&
                        req.method.toUpperCase() === 'POST')) {
                return next();
            }

            // This is the customer account. See node_modules/sdc-clients UFDS
            // library (lib/ufds.js) `_extendUser` method for a detailed list
            // of available properties and methods for the `account` object.
            assert.ok(req.account);

            var log = req.log;

            if (req.account.isAdmin()) {
                log.debug('Prov. limits: account %s is an admin, allowing.',
                    req.account.login);
                return next();
            }

            if (!req.dataset) {
                log.debug('Prov. limits: no dataset on req; skipping checks.');
                return next();
            }

            // If the customer account has limits, we'll use those, otherwise,
            // we'll go with the default limits specified on config
            return req.account.listLimits(function (err, limits) {
                if (err) {
                    log.error({err: err},
                        'Prov. limits: unable to list limits.');
                    return next(new restify.InternalError(
                            'Provisioning limits: unable to list limits.'));
                }

                if (!limits) {
                    limits = [];
                }
                // First, we are only interested into limits defined for the
                // current datacenter:
                limits = limits.filter(function (l) {
                    return (l.datacenter === cfg.datacenter);
                });
                limits = filterLimits(req.dataset, cfg.defaults, limits);
                log.debug({provisioning_limits: limits},
                        'Limits to be applied.');

                // Before we attempt to fetch anything from VMAPI there are
                // still several things we can do in order to fail/allow early:
                // - If at this point we have any limit with a value of "-1",
                // this means we will not be able to provision, since this one
                // will be applied and we should return now with a failure.
                if (limits.some(function (a) {
                    return (a.value && parseInt(a.value, 10) <= -1);
                })) {
                    log.info('Prov. Limits: Limits with negative value ' +
                            'applied, dissallowing');
                    return next(new restify.NotAuthorizedError(
                                util.format('%s: %s', CODE, MESSAGE)));
                }
                // - If we have any limit with a value of zero, we'll simply
                // allow so we could safely remove those values from the list.
                limits = limits.filter(function (a) {
                    return (a.value && parseInt(a.value, 10) !== 0);
                });
                // - Make sure we have at least one limit or return now with
                // an OK.
                if (!limits.length) {
                    log.debug('Prov. limits: no limits to be applied, ' +
                            'allowing.');
                    return next();
                }
                req.limits = limits;

                // Now, we have to:
                // - load all images with same os than req.dataset. (We will
                // optimize and just return those with same name than current
                // image if there isn't any "check": "os" limit)
                // - get all the customer machines whose state is not destroyed
                // or failed.
                // - check pending limits
                var toCheck = 'image';
                var images = [];
                var allVms = [];
                // Vms with same OS:
                var osVms = [];
                // Vms with same image:
                var imgVms = [];

                function whatToCheck(_, cb) {
                    if (req.limits.some(function (x) {
                        return (x.check === 'os');
                    })) {
                        toCheck = 'os';
                    }
                    cb(null);
                }

                function fetchImages(_, cb) {
                    var opts = {
                        state: 'all'
                    };

                    if (toCheck === 'image') {
                        opts.name = req.dataset.name;
                    } else {
                        opts.os = req.dataset.os;
                    }

                    req.sdc.imgapi.listImages(opts, function (er, imgs, r) {
                        if (er) {
                            log.error({err: er},
                            'Prov. limits: unable to list Images');
                            return cb(er);
                        }

                        if (!imgs) {
                            log.error('Prov. limits: No Images found!');
                            return cb(new Error('No images found'));
                        }

                        images = imgs;
                        return cb(null);
                    });
                }

                function loadActiveMachines(_, cb) {
                    var filter = '(&(owner_uuid=' + req.account.uuid +
                            ')(&(!(state=destroyed))(!(state=failed))))';
                    req.sdc.vmapi.listVms({
                        query: filter
                    }, function (er, vms) {
                        if (er) {
                            log.error({err: er},
                            'Prov. limits: unable to list VMs');
                            return cb(er);
                        }

                        allVms = vms.map(function (vm) {
                            images.filter(function (i) {
                                if (i.uuid === vm.image_uuid ||
                                    (vm.brand === 'kvm' &&
                                    i.uuid === vm.disks[0].image_uuid)) {
                                    vm.image_name = i.name;
                                    vm.os = i.os;
                                }
                            });
                            return (vm);
                        });

                        return cb(null);
                    });
                }

                function machinesByOs(_, cb) {
                    osVms = allVms.filter(function (vm) {
                        return (vm.os && vm.os === req.dataset.os);
                    });

                    return cb(null);
                }

                function machinesByImg(_, cb) {
                    imgVms = allVms.filter(function (vm) {
                        return (vm.image_name &&
                            (vm.image_name === req.dataset.name ||
                            vm.image_name.toLowerCase() === req.dataset.name));
                    });

                    return cb(null);
                }

                return vasync.pipeline({
                    funcs: [whatToCheck, fetchImages, loadActiveMachines,
                            machinesByOs, machinesByImg]
                }, function (er, results) {
                    if (er) {
                        log.error({err: er},
                            'Prov. limits: unable to get machines/images');
                        return next(new restify.InternalError(
                                'Prov. limits: unable to get machines/images'));
                    }

                    return vasync.forEachPipeline({
                        inputs: req.limits,
                        func: function (limit, cb) {
                            log.debug({limit: limit}, 'Applying limit');
                            var value = parseInt(limit.value, 10);
                            if (value === 0) {
                                return cb(null);
                            }
                            var count;
                            var machines = allVms;

                            if (limit.check === 'os' && limit.os !== 'any') {
                                machines = osVms;
                            }

                            if (limit.check === 'image' &&
                                limit.image !== 'any') {
                                machines = imgVms;
                            }

                            switch (limit.by) {
                            case 'ram':
                                count = machines.map(function (vm) {
                                    return parseInt(vm.ram, 10);
                                }).reduce(function (a, b) {
                                    return (a + b);
                                }, 0) + parseInt(
                                    req.pkg.max_physical_memory, 10);
                                break;
                            case 'quota':
                                count = machines.map(function (vm) {
                                    return parseInt(vm.quota, 10);
                                }).reduce(function (a, b) {
                                    return (a + b);
                                }, 0) + parseInt(req.pkg.quota, 10);
                                break;
                            default: // machines
                                count = machines.length + 1;
                                break;
                            }

                            if (count > value) {
                                log.info({limit: limit},
                                        'Provisioning limit applied');
                                return cb(new restify.NotAuthorizedError(
                                    util.format('%s: %s', CODE, MESSAGE)));
                            }

                            return cb(null);
                        }
                    }, function (er2, results2) {
                        if (er2) {
                            return next(er2);
                        }

                        return next();

                    });
                });
            });
        };
    }
};
<commit_msg>JPC-1745: Allow specifying custom default limits per account tenant<commit_after>/*
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */

/*
 * Copyright (c) 2014, Joyent, Inc.
 */

/*
 * Provision Limits Plugin.
 *
 * See Appendix A of CloudAPI Administrator Guide for the whole details
 * on how the plugin works:
 *
 * https://mo.joyent.com/docs/cloudapi/master/admin.html
 *
 */

var assert = require('assert');
var util = require('util');
var restify = require('restify');
var vasync = require('vasync');

// --- Globals

var CODE = 'QuotaExceeded';
var MESSAGE = 'To have your limits raised please contact Support.';

/*
 * Separated function in order to make testing easier.
 *
 * Unique task for this function is to return which limits will be
 * applied for a given customer. Required information for the function includes
 * the specific limits, the global limits and the image which will be used for
 * provisioning. All this information can be obtained from the request object.
 *
 * Please, note this function will just return an array with the limits to be
 * applied for the given request, even if that array is empty.
 */
function filterLimits(req_image, cfg_limits, limits) {

    // First, filter relevant limits from configuration (defaults):
    cfg_limits = cfg_limits.filter(function (l) {
        return ((l.os && l.os === 'any') ||
                (l.image && l.image === 'any') ||
                (l.os && l.check && l.os === req_image.os &&
                    l.check === 'os') ||
                (l.image && (l.image === req_image.name ||
                    l.image === req_image.name.toLowerCase())));
    });

    // At this point we should have a single limits entry, let's
    // convert from capi_limits before we go further
    if (limits.length) {
        var parsedLimits = [];
        Object.keys(limits[0]).forEach(function (k) {
            if (k === 'limit') {
                if (typeof (limits[0][k]) === 'string') {
                    limits[0][k] = [limits[0][k]];
                }
                if (!Array.isArray(limits[0][k]) &&
                    typeof (limits[0][k]) === 'object') {
                    parsedLimits.push(limits[0][k]);
                } else {
                    limits[0][k].forEach(function (j) {
                        try {
                            parsedLimits.push(JSON.parse(j));
                        } catch (e) {}
                    });
                }
            } else if (['dn',
                'controls',
                '_parent',
                '_owner',
                'objectclass',
                'datacenter'].indexOf(k) === -1) {
                // This is an old capi_limit: check image by
                // number of machines:
                parsedLimits.push({
                    image: k,
                    check: 'image',
                    by: 'machines',
                    value: limits[0][k]
                });
            }
        });
        limits = parsedLimits;
    }

    limits = limits.filter(function (l) {
        return ((l.os && l.os === 'any') ||
                (l.image && l.image === 'any') ||
                (l.os && l.check && l.os === req_image.os &&
                    l.check === 'os') ||
                (l.image && (l.image === req_image.name.toLowerCase() ||
                            l.image === req_image.name)));
    });
    // Check if the customer has a 'catch all' limit specified and, if so
    // skip adding anything from the cfg_limits:
    var catchAll = limits.some(function (l) {
        return ((l.os && l.os === 'any') || (l.image && l.image === 'any'));
    });

    // Next, from cfg_limits, take any limit which is different
    // than the ones from UFDS:
    cfg_limits.filter(function (cfg_l) {
        if (!cfg_l.by) {
            return false;
        }
        var exists = limits.some(function (l) {
            return (l.check && cfg_l.check &&
                    l.check === cfg_l.check &&
                    l.by === cfg_l.by);
        });

        return (!exists);
    });

    // Push any limits not the same to our limits list:
    if (cfg_limits.length > 0 && !catchAll && limits.length === 0) {
        limits = limits.concat(cfg_limits);
    }
    return (limits);
}

module.exports = {
    filterLimits: filterLimits,
    preProvision: function (cfg) {
        if (!cfg || typeof (cfg) !== 'object') {
            throw new TypeError('cfg (object) is required');
        }

        // In the future we may replace this with terms like "any" or "all"
        // and make possible apply the same configuration to every datacenter.
        if (!cfg.datacenter) {
            throw new TypeError('cfg.datacenter is required');
        }

        if (!cfg.defaults || typeof (cfg.defaults) !== 'object') {
            throw new TypeError('cfg.defaults (object) is required');
        }

        return function provisioningLimits(req, res, next) {
            // Do nothing if we are not provisioning, (not strictly required,
            // since CloudAPI will handle this, but illustrative):
            if (!(/\/machines$/.test(req.url) &&
                        req.method.toUpperCase() === 'POST')) {
                return next();
            }

            // This is the customer account. See node_modules/sdc-clients UFDS
            // library (lib/ufds.js) `_extendUser` method for a detailed list
            // of available properties and methods for the `account` object.
            assert.ok(req.account);

            var log = req.log;

            if (req.account.isAdmin()) {
                log.debug('Prov. limits: account %s is an admin, allowing.',
                    req.account.login);
                return next();
            }

            if (!req.dataset) {
                log.debug('Prov. limits: no dataset on req; skipping checks.');
                return next();
            }

            // If the customer account has limits, we'll use those, otherwise,
            // we'll go with the default limits specified on config
            return req.account.listLimits(function (err, limits) {
                if (err) {
                    log.error({err: err},
                        'Prov. limits: unable to list limits.');
                    return next(new restify.InternalError(
                            'Provisioning limits: unable to list limits.'));
                }

                if (!limits) {
                    limits = [];
                }
                // First, we are only interested into limits defined for the
                // current datacenter:
                limits = limits.filter(function (l) {
                    return (l.datacenter === cfg.datacenter);
                });

                var defaults = (req.account.tenant &&
                                cfg[req.account.tenant]) ?
                                cfg[req.account.tenant] : cfg.defaults;
                limits = filterLimits(req.dataset, defaults, limits);
                log.debug({provisioning_limits: limits},
                        'Limits to be applied.');

                // Before we attempt to fetch anything from VMAPI there are
                // still several things we can do in order to fail/allow early:
                // - If at this point we have any limit with a value of "-1",
                // this means we will not be able to provision, since this one
                // will be applied and we should return now with a failure.
                if (limits.some(function (a) {
                    return (a.value && parseInt(a.value, 10) <= -1);
                })) {
                    log.info('Prov. Limits: Limits with negative value ' +
                            'applied, dissallowing');
                    return next(new restify.NotAuthorizedError(
                                util.format('%s: %s', CODE, MESSAGE)));
                }
                // - If we have any limit with a value of zero, we'll simply
                // allow so we could safely remove those values from the list.
                limits = limits.filter(function (a) {
                    return (a.value && parseInt(a.value, 10) !== 0);
                });
                // - Make sure we have at least one limit or return now with
                // an OK.
                if (!limits.length) {
                    log.debug('Prov. limits: no limits to be applied, ' +
                            'allowing.');
                    return next();
                }
                req.limits = limits;

                // Now, we have to:
                // - load all images with same os than req.dataset. (We will
                // optimize and just return those with same name than current
                // image if there isn't any "check": "os" limit)
                // - get all the customer machines whose state is not destroyed
                // or failed.
                // - check pending limits
                var toCheck = 'image';
                var images = [];
                var allVms = [];
                // Vms with same OS:
                var osVms = [];
                // Vms with same image:
                var imgVms = [];

                function whatToCheck(_, cb) {
                    if (req.limits.some(function (x) {
                        return (x.check === 'os');
                    })) {
                        toCheck = 'os';
                    }
                    cb(null);
                }

                function fetchImages(_, cb) {
                    var opts = {
                        state: 'all'
                    };

                    if (toCheck === 'image') {
                        opts.name = req.dataset.name;
                    } else {
                        opts.os = req.dataset.os;
                    }

                    req.sdc.imgapi.listImages(opts, function (er, imgs, r) {
                        if (er) {
                            log.error({err: er},
                            'Prov. limits: unable to list Images');
                            return cb(er);
                        }

                        if (!imgs) {
                            log.error('Prov. limits: No Images found!');
                            return cb(new Error('No images found'));
                        }

                        images = imgs;
                        return cb(null);
                    });
                }

                function loadActiveMachines(_, cb) {
                    var filter = '(&(owner_uuid=' + req.account.uuid +
                            ')(&(!(state=destroyed))(!(state=failed))))';
                    req.sdc.vmapi.listVms({
                        query: filter
                    }, function (er, vms) {
                        if (er) {
                            log.error({err: er},
                            'Prov. limits: unable to list VMs');
                            return cb(er);
                        }

                        allVms = vms.map(function (vm) {
                            images.filter(function (i) {
                                if (i.uuid === vm.image_uuid ||
                                    (vm.brand === 'kvm' &&
                                    i.uuid === vm.disks[0].image_uuid)) {
                                    vm.image_name = i.name;
                                    vm.os = i.os;
                                }
                            });
                            return (vm);
                        });

                        return cb(null);
                    });
                }

                function machinesByOs(_, cb) {
                    osVms = allVms.filter(function (vm) {
                        return (vm.os && vm.os === req.dataset.os);
                    });

                    return cb(null);
                }

                function machinesByImg(_, cb) {
                    imgVms = allVms.filter(function (vm) {
                        return (vm.image_name &&
                            (vm.image_name === req.dataset.name ||
                            vm.image_name.toLowerCase() === req.dataset.name));
                    });

                    return cb(null);
                }

                return vasync.pipeline({
                    funcs: [whatToCheck, fetchImages, loadActiveMachines,
                            machinesByOs, machinesByImg]
                }, function (er, results) {
                    if (er) {
                        log.error({err: er},
                            'Prov. limits: unable to get machines/images');
                        return next(new restify.InternalError(
                                'Prov. limits: unable to get machines/images'));
                    }

                    return vasync.forEachPipeline({
                        inputs: req.limits,
                        func: function (limit, cb) {
                            log.debug({limit: limit}, 'Applying limit');
                            var value = parseInt(limit.value, 10);
                            if (value === 0) {
                                return cb(null);
                            }
                            var count;
                            var machines = allVms;

                            if (limit.check === 'os' && limit.os !== 'any') {
                                machines = osVms;
                            }

                            if (limit.check === 'image' &&
                                limit.image !== 'any') {
                                machines = imgVms;
                            }

                            switch (limit.by) {
                            case 'ram':
                                count = machines.map(function (vm) {
                                    return parseInt(vm.ram, 10);
                                }).reduce(function (a, b) {
                                    return (a + b);
                                }, 0) + parseInt(
                                    req.pkg.max_physical_memory, 10);
                                break;
                            case 'quota':
                                count = machines.map(function (vm) {
                                    return parseInt(vm.quota, 10);
                                }).reduce(function (a, b) {
                                    return (a + b);
                                }, 0) + parseInt(req.pkg.quota, 10);
                                break;
                            default: // machines
                                count = machines.length + 1;
                                break;
                            }

                            if (count > value) {
                                log.info({limit: limit},
                                        'Provisioning limit applied');
                                return cb(new restify.NotAuthorizedError(
                                    util.format('%s: %s', CODE, MESSAGE)));
                            }

                            return cb(null);
                        }
                    }, function (er2, results2) {
                        if (er2) {
                            return next(er2);
                        }

                        return next();

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


<commit_before>//Exercices

//Exercice 1 FLATTENING
var arrays = [[1, 2, 3], [4, 5], [6]];
// Your code here.

var flatted = arrays.reduce(function(prev, next){
   return prev.concat(next);
});

console.log(flatted);
// → [1, 2, 3, 4, 5, 6]

// Exercice 2.
var ancestry = [{"name": "Carolus Haverbeke", "sex": "m", "born": 1832, "died": 1905, "father": "Carel Haverbeke", "mother": "Maria van Brussel"},
  {"name": "Emma de Milliano", "sex": "f", "born": 1876, "died": 1956, "father": "Petrus de Milliano", "mother": "Sophia van Damme"},
  {"name": "Maria de Rycke", "sex": "f", "born": 1683, "died": 1724, "father": "Frederik de Rycke", "mother": "Laurentia van Vlaenderen"},
  {"name": "Jan van Brussel", "sex": "m", "born": 1714, "died": 1748, "father": "Jacobus van Brussel", "mother": "Joanna van Rooten"},
  {"name": "Philibert Haverbeke", "sex": "m", "born": 1907, "died": 1997, "father": "Emile Haverbeke", "mother": "Emma de Milliano"},
  {"name": "Jan Frans van Brussel", "sex": "m", "born": 1761, "died": 1833, "father": "Jacobus Bernardus van Brussel", "mother":null},
  {"name": "Pauwels van Haverbeke", "sex": "m", "born": 1535, "died": 1582, "father": "N. van Haverbeke", "mother":null},
  {"name": "Clara Aernoudts", "sex": "f", "born": 1918, "died": 2012, "father": "Henry Aernoudts", "mother": "Sidonie Coene"},
  {"name": "Emile Haverbeke", "sex": "m", "born": 1877, "died": 1968, "father": "Carolus Haverbeke", "mother": "Maria Sturm"},
  {"name": "Lieven de Causmaecker", "sex": "m", "born": 1696, "died": 1724, "father": "Carel de Causmaecker", "mother": "Joanna Claes"},
  {"name": "Pieter Haverbeke", "sex": "m", "born": 1602, "died": 1642, "father": "Lieven van Haverbeke", "mother":null},
  {"name": "Livina Haverbeke", "sex": "f", "born": 1692, "died": 1743, "father": "Daniel Haverbeke", "mother": "Joanna de Pape"},
  {"name": "Pieter Bernard Haverbeke", "sex": "m", "born": 1695, "died": 1762, "father": "Willem Haverbeke", "mother": "Petronella Wauters"},
  {"name": "Lieven van Haverbeke", "sex": "m", "born": 1570, "died": 1636, "father": "Pauwels van Haverbeke", "mother": "Lievijne Jans"},
  {"name": "Joanna de Causmaecker", "sex": "f", "born": 1762, "died": 1807, "father": "Bernardus de Causmaecker", "mother":null},
  {"name": "Willem Haverbeke", "sex": "m", "born": 1668, "died": 1731, "father": "Lieven Haverbeke", "mother": "Elisabeth Hercke"},
  {"name": "Pieter Antone Haverbeke", "sex": "m", "born": 1753, "died": 1798, "father": "Jan Francies Haverbeke", "mother": "Petronella de Decker"},
  {"name": "Maria van Brussel", "sex": "f", "born": 1801, "died": 1834, "father": "Jan Frans van Brussel", "mother": "Joanna de Causmaecker"},
  {"name": "Angela Haverbeke", "sex": "f", "born": 1728, "died": 1734, "father": "Pieter Bernard Haverbeke", "mother": "Livina de Vrieze"},
  {"name": "Elisabeth Haverbeke", "sex": "f", "born": 1711, "died": 1754, "father": "Jan Haverbeke", "mother": "Maria de Rycke"},
  {"name": "Lievijne Jans", "sex": "f", "born": 1542, "died": 1582, "father":null, "mother":null},
  {"name": "Bernardus de Causmaecker", "sex": "m", "born": 1721, "died": 1789, "father": "Lieven de Causmaecker", "mother": "Livina Haverbeke"},
  {"name": "Jacoba Lammens", "sex": "f", "born": 1699, "died": 1740, "father": "Lieven Lammens", "mother": "Livina de Vrieze"},
  {"name": "Pieter de Decker", "sex": "m", "born": 1705, "died": 1780, "father": "Joos de Decker", "mother": "Petronella van de Steene"},
  {"name": "Joanna de Pape", "sex": "f", "born": 1654, "died": 1723, "father": "Vincent de Pape", "mother": "Petronella Wauters"},
  {"name": "Daniel Haverbeke", "sex": "m", "born": 1652, "died": 1723, "father": "Lieven Haverbeke", "mother": "Elisabeth Hercke"},
  {"name": "Lieven Haverbeke", "sex": "m", "born": 1631, "died": 1676, "father": "Pieter Haverbeke", "mother": "Anna van Hecke"},
  {"name": "Martina de Pape", "sex": "f", "born": 1666, "died": 1727, "father": "Vincent de Pape", "mother": "Petronella Wauters"},
  {"name": "Jan Francies Haverbeke", "sex": "m", "born": 1725, "died": 1779, "father": "Pieter Bernard Haverbeke", "mother": "Livina de Vrieze"},
  {"name": "Maria Haverbeke", "sex": "m", "born": 1905, "died": 1997, "father": "Emile Haverbeke", "mother": "Emma de Milliano"},
  {"name": "Petronella de Decker", "sex": "f", "born": 1731, "died": 1781, "father": "Pieter de Decker", "mother": "Livina Haverbeke"},
  {"name": "Livina Sierens", "sex": "f", "born": 1761, "died": 1826, "father": "Jan Sierens", "mother": "Maria van Waes"},
  {"name": "Laurentia Haverbeke", "sex": "f", "born": 1710, "died": 1786, "father": "Jan Haverbeke", "mother": "Maria de Rycke"},
  {"name": "Carel Haverbeke", "sex": "m", "born": 1796, "died": 1837, "father": "Pieter Antone Haverbeke", "mother": "Livina Sierens"},
  {"name": "Elisabeth Hercke", "sex": "f", "born": 1632, "died": 1674, "father": "Willem Hercke", "mother": "Margriet de Brabander"},
  {"name": "Jan Haverbeke", "sex": "m", "born": 1671, "died": 1731, "father": "Lieven Haverbeke", "mother": "Elisabeth Hercke"},
  {"name": "Anna van Hecke", "sex": "f", "born": 1607, "died": 1670, "father": "Paschasius van Hecke", "mother": "Martijntken Beelaert"},
  {"name": "Maria Sturm", "sex": "f", "born": 1835, "died": 1917, "father": "Charles Sturm", "mother": "Seraphina Spelier"},
  {"name": "Jacobus Bernardus van Brussel", "sex": "m", "born": 1736, "died": 1809, "father": "Jan van Brussel", "mother": "Elisabeth Haverbeke"}];

function average(array) {
  function plus(a, b) { return a + b; }
  return array.reduce(plus) / array.length;
}

var byName = [];
ancestry.forEach(function(person) {
  byName[person.name] = person;
});

var AgesWhenBorn = ancestry.map(function(child){
	if(child.mother !== null && child.mother !== undefined){
		if(byName[child.mother] !== undefined){
		var mother = byName[child.mother];
		return child.born - mother.born;
		}else{
			return 0;
		}
	}else{
		return 0;
	}
});

AgesWhenBorn = AgesWhenBorn.filter(function(age){
	return !!age;

});

console.log("Average age of mother when child is born: ",average(AgesWhenBorn));
// → 31.2

//Exercice 3 Historical life expectancy

function age(born, died){
	return died - born;
}

var byCentury = [];

ancestry.forEach(function(person){
	var index = Math.ceil(person.died/100);
	if(byCentury[index] === undefined){
		byCentury[index] = [];
	}
	byCentury[index].push(age(person.born, person.died));
});

byCentury = byCentury.map(function(century){
	return average(century);
});
console.log("\n\n_____Expectancy of life per century_____")
byCentury.forEach(function(average, index, array){
	console.log("Century",index, ":", average);
});

<commit_msg>Added the last exercice of eloquent to the chapter5.js file, eloquent theme 5 endend<commit_after>//Exercices

//Exercice 1 FLATTENING
var arrays = [[1, 2, 3], [4, 5], [6]];
// Your code here.

var flatted = arrays.reduce(function(prev, next){
   return prev.concat(next);
});

console.log(flatted);
// → [1, 2, 3, 4, 5, 6]

// Exercice 2.
var ancestry = [{"name": "Carolus Haverbeke", "sex": "m", "born": 1832, "died": 1905, "father": "Carel Haverbeke", "mother": "Maria van Brussel"},
  {"name": "Emma de Milliano", "sex": "f", "born": 1876, "died": 1956, "father": "Petrus de Milliano", "mother": "Sophia van Damme"},
  {"name": "Maria de Rycke", "sex": "f", "born": 1683, "died": 1724, "father": "Frederik de Rycke", "mother": "Laurentia van Vlaenderen"},
  {"name": "Jan van Brussel", "sex": "m", "born": 1714, "died": 1748, "father": "Jacobus van Brussel", "mother": "Joanna van Rooten"},
  {"name": "Philibert Haverbeke", "sex": "m", "born": 1907, "died": 1997, "father": "Emile Haverbeke", "mother": "Emma de Milliano"},
  {"name": "Jan Frans van Brussel", "sex": "m", "born": 1761, "died": 1833, "father": "Jacobus Bernardus van Brussel", "mother":null},
  {"name": "Pauwels van Haverbeke", "sex": "m", "born": 1535, "died": 1582, "father": "N. van Haverbeke", "mother":null},
  {"name": "Clara Aernoudts", "sex": "f", "born": 1918, "died": 2012, "father": "Henry Aernoudts", "mother": "Sidonie Coene"},
  {"name": "Emile Haverbeke", "sex": "m", "born": 1877, "died": 1968, "father": "Carolus Haverbeke", "mother": "Maria Sturm"},
  {"name": "Lieven de Causmaecker", "sex": "m", "born": 1696, "died": 1724, "father": "Carel de Causmaecker", "mother": "Joanna Claes"},
  {"name": "Pieter Haverbeke", "sex": "m", "born": 1602, "died": 1642, "father": "Lieven van Haverbeke", "mother":null},
  {"name": "Livina Haverbeke", "sex": "f", "born": 1692, "died": 1743, "father": "Daniel Haverbeke", "mother": "Joanna de Pape"},
  {"name": "Pieter Bernard Haverbeke", "sex": "m", "born": 1695, "died": 1762, "father": "Willem Haverbeke", "mother": "Petronella Wauters"},
  {"name": "Lieven van Haverbeke", "sex": "m", "born": 1570, "died": 1636, "father": "Pauwels van Haverbeke", "mother": "Lievijne Jans"},
  {"name": "Joanna de Causmaecker", "sex": "f", "born": 1762, "died": 1807, "father": "Bernardus de Causmaecker", "mother":null},
  {"name": "Willem Haverbeke", "sex": "m", "born": 1668, "died": 1731, "father": "Lieven Haverbeke", "mother": "Elisabeth Hercke"},
  {"name": "Pieter Antone Haverbeke", "sex": "m", "born": 1753, "died": 1798, "father": "Jan Francies Haverbeke", "mother": "Petronella de Decker"},
  {"name": "Maria van Brussel", "sex": "f", "born": 1801, "died": 1834, "father": "Jan Frans van Brussel", "mother": "Joanna de Causmaecker"},
  {"name": "Angela Haverbeke", "sex": "f", "born": 1728, "died": 1734, "father": "Pieter Bernard Haverbeke", "mother": "Livina de Vrieze"},
  {"name": "Elisabeth Haverbeke", "sex": "f", "born": 1711, "died": 1754, "father": "Jan Haverbeke", "mother": "Maria de Rycke"},
  {"name": "Lievijne Jans", "sex": "f", "born": 1542, "died": 1582, "father":null, "mother":null},
  {"name": "Bernardus de Causmaecker", "sex": "m", "born": 1721, "died": 1789, "father": "Lieven de Causmaecker", "mother": "Livina Haverbeke"},
  {"name": "Jacoba Lammens", "sex": "f", "born": 1699, "died": 1740, "father": "Lieven Lammens", "mother": "Livina de Vrieze"},
  {"name": "Pieter de Decker", "sex": "m", "born": 1705, "died": 1780, "father": "Joos de Decker", "mother": "Petronella van de Steene"},
  {"name": "Joanna de Pape", "sex": "f", "born": 1654, "died": 1723, "father": "Vincent de Pape", "mother": "Petronella Wauters"},
  {"name": "Daniel Haverbeke", "sex": "m", "born": 1652, "died": 1723, "father": "Lieven Haverbeke", "mother": "Elisabeth Hercke"},
  {"name": "Lieven Haverbeke", "sex": "m", "born": 1631, "died": 1676, "father": "Pieter Haverbeke", "mother": "Anna van Hecke"},
  {"name": "Martina de Pape", "sex": "f", "born": 1666, "died": 1727, "father": "Vincent de Pape", "mother": "Petronella Wauters"},
  {"name": "Jan Francies Haverbeke", "sex": "m", "born": 1725, "died": 1779, "father": "Pieter Bernard Haverbeke", "mother": "Livina de Vrieze"},
  {"name": "Maria Haverbeke", "sex": "m", "born": 1905, "died": 1997, "father": "Emile Haverbeke", "mother": "Emma de Milliano"},
  {"name": "Petronella de Decker", "sex": "f", "born": 1731, "died": 1781, "father": "Pieter de Decker", "mother": "Livina Haverbeke"},
  {"name": "Livina Sierens", "sex": "f", "born": 1761, "died": 1826, "father": "Jan Sierens", "mother": "Maria van Waes"},
  {"name": "Laurentia Haverbeke", "sex": "f", "born": 1710, "died": 1786, "father": "Jan Haverbeke", "mother": "Maria de Rycke"},
  {"name": "Carel Haverbeke", "sex": "m", "born": 1796, "died": 1837, "father": "Pieter Antone Haverbeke", "mother": "Livina Sierens"},
  {"name": "Elisabeth Hercke", "sex": "f", "born": 1632, "died": 1674, "father": "Willem Hercke", "mother": "Margriet de Brabander"},
  {"name": "Jan Haverbeke", "sex": "m", "born": 1671, "died": 1731, "father": "Lieven Haverbeke", "mother": "Elisabeth Hercke"},
  {"name": "Anna van Hecke", "sex": "f", "born": 1607, "died": 1670, "father": "Paschasius van Hecke", "mother": "Martijntken Beelaert"},
  {"name": "Maria Sturm", "sex": "f", "born": 1835, "died": 1917, "father": "Charles Sturm", "mother": "Seraphina Spelier"},
  {"name": "Jacobus Bernardus van Brussel", "sex": "m", "born": 1736, "died": 1809, "father": "Jan van Brussel", "mother": "Elisabeth Haverbeke"}];

function average(array) {
  function plus(a, b) { return a + b; }
  return array.reduce(plus) / array.length;
}

var byName = [];
ancestry.forEach(function(person) {
  byName[person.name] = person;
});

var AgesWhenBorn = ancestry.map(function(child){
	if(child.mother !== null && child.mother !== undefined){
		if(byName[child.mother] !== undefined){
		var mother = byName[child.mother];
		return child.born - mother.born;
		}else{
			return 0;
		}
	}else{
		return 0;
	}
});

AgesWhenBorn = AgesWhenBorn.filter(function(age){
	return !!age;

});

console.log("Average age of mother when child is born: ",average(AgesWhenBorn));
// → 31.2

//Exercice 3 Historical life expectancy

function age(born, died){
	return died - born;
}

var byCentury = [];

ancestry.forEach(function(person){
	var index = Math.ceil(person.died/100);
	if(byCentury[index] === undefined){
		byCentury[index] = [];
	}
	byCentury[index].push(age(person.born, person.died));
});

byCentury = byCentury.map(function(century){
	return average(century);
});

console.log("\n\n_____Expectancy of life per century_____")
byCentury.forEach(function(average, index, array){
	console.log("Century",index, ":", average);
});

// Your code here.
var every = function every(array, func, index){

	var newArray = array.map(
		function(element){
			return func(element);
		});
  	var reducedArray = newArray.reduce(function(prev, current){
	    if(!prev){
	    	return false;
	    }else{
	    	return current;
	    }
  	});
  	return reducedArray;
};

var some = function some(array, func){
		var newArray = array.map(
		function(element){
			return func(element);
		});
		if(newArray.indexOf(true) !== -1){
			return true;
		}else{
			return false;
		}
}

console.log("Every: ", every([NaN, NaN, NaN], isNaN));
// → true
console.log("Every: ", every([NaN, NaN, 4], isNaN));
// → false

console.log("Some: ", some([NaN, 3, 4], isNaN));
// → true
console.log("Some: ", some([2, 3, 4], isNaN));
// → false

<|endoftext|>


<commit_before>function median(numbers) {
  numbers.sort(function(a, b) {return a-b});
  var middleElement = Math.floor(numbers.length / 2);

  median = numbers[middleElement];

  if (numbers.length % 2 === 0) {
    var median = (parseInt(numbers[middleElement-1]) + parseInt(numbers[middleElement])) / 2;
  }

  return median;
};
console.log(median([-1,700,2,48,49]))
//Mike User Story: I took out console.log in the initial  as it seemed like an artifact to check results and I rewrote the code to default to setting the median to the middle entry and then changing it if the number of entries was even instead of using an else statement for fewer lines (though arugably less readable)

<commit_msg>add mike london<commit_after>
//Refactored Solution Mike + Michael

function sum(array) {
  var totalOfArray = 0;
  for (var counter = 0; counter < array.length; counter++) {
    totalOfArray += array[counter];
  } // end for
  return totalOfArray;
}

function mean (array) {
  var averageValue = 0;
  for (counter = 0; counter < array.length; counter++) {
    averageValue += array[counter];
  } // end for
  return averageValue / array.length;
}

//User Stories
/* - Mike London
As a user I would like to take the sum of a containers number values. The Function sum takes in a container of numbers and returns the total value of sum of that containers values. We are assuming that this is only for Numbers.
As a user I would like to find the average value of a containers number values. The Function mean takes in a container of numbers and returns the average value of the containers numbers.
*/

function median(numbers) {
  numbers.sort(function(a, b) {return a-b});
  var middleElement = Math.floor(numbers.length / 2);

  median = numbers[middleElement];

  if (numbers.length % 2 === 0) {
    var median = (parseInt(numbers[middleElement-1]) + parseInt(numbers[middleElement])) / 2;
  }

  return median;
};
console.log(median([-1,700,2,48,49]))
//Mike Yao User Story: I took out console.log in the initial  as it seemed like an artifact to check results and I rewrote the code to default to setting the median to the middle entry and then changing it if the number of entries was even instead of using an else statement for fewer lines (though arugably less readable)

<|endoftext|>


<commit_before>var LIBEO;

(function($, window, document, undefined) {

    // On document ready
    $(document).ready(function() {

        // Check if the 'WAQ' object is defined
        if (typeof WAQ === 'object') {

        	LIBEO = WAQ;

        	WAQ.Constants = {
        		name            : 'WAQ',
        		onClickClass    : 'isClicked',
        		onHoverClass    : 'isHover',
        		onZoomClass     : 'l-zoomed',
        		fontPrefixClass : 'l-font',
        		isOpenClass     : 'isOpen',
        		isActiveClass   : 'isActive',
        		extLinksClass   : 'external'
        	};

            // Instantiate and store objects
            if( ! $('html').hasClass('lt-ie9') ){
                WAQ.SnapMenu         = WAQ.Common.createObjects($('#nav-main'), SnapMenu, {
                    adjustTopOffset : 45,
                    customNavigation : true,
                    breakpointsSelector : '.l-page-wrapper > section',
                });
                WAQ.SnapMenuSchedule = WAQ.Common.createObjects($('.days-buttons'), SnapMenu, {
                    adjustTopOffset : -133,
                    adjustBottomOffset : -133,
                    delimiterElement : $('.js-slider'),
                });
            }
            WAQ.Schedule = WAQ.Common.createObjects($('.schedule'), Schedule);
            WAQ.Slideshow = WAQ.Common.createObjects($('.blog-wrapper'), Slideshow);
            WAQ.MobileMenu = WAQ.Common.createObjects($('#nav-main'), MobileMenu);
            WAQ.CustomSchedule = WAQ.Common.createObjects($('.schedule-wrapper'), CustomSchedule);
            WAQ.Countdown = WAQ.Common.createObjects($('.event-stats'), Countdown);
            WAQ.GoogleMap = new CustomGmap('#gmap');

            if($('#gmap').length > 0){
                google.maps.event.addListenerOnce(WAQ.GoogleMap.map, 'idle', function() {
                    GoogleMapResize();
                });
            }

            // Common functions
            WAQ.Common.goToContent($('.l-a11y'));
            WAQ.Common.betterFocus(['a', 'button', 'input', 'h2']);
            WAQ.Common.checkForKeyboardInput();
            WAQ.Common.checkFontSize();
            WAQ.Common.externalLinks();
        }

        // Scroll to active hash on page load
        var hash = window.location.hash;
        if ( hash != '' && hash != "#mercredi" && hash != "#jeudi" && hash != "#vendredi" ) {

            // Source : http://stackoverflow.com/questions/3659072/jquery-disable-anchor-jump-when-loading-a-page
            window.scrollTo(0, $( hash ).offset().top - 64);
            setTimeout(function() {
                window.scrollTo(0, $( hash ).offset().top - 64);
            }, 1);

            // Update active menu icon
            $( '#nav-main li a[href="' + hash + '"]' ).parents( 'li' ).addClass( 'active' );
        }

        // Add js class to body
        $( 'body' ).addClass( 'js' );

        // Load more blog articles
        $( '.blog-wrapper' ).append('<button class="load-more">Afficher plus d\'articles</button>');
        $( '.blog-wrapper article' ).eq(2).nextAll().hide();
        $( document ).on( 'click', '.blog-wrapper .load-more', function() {
            WAQ.Common.sequentialFadeIn( '.blog-wrapper article:hidden:lt(3)', 100, 'block', function(){
                if( $('.blog-wrapper article:hidden').length == 0 ){
                    $('.blog-wrapper .load-more').hide();
                }
            });
        });

        // Retour en haut du site
        $('.snapmenu-logo').on('click', function(e){
            var self = this,
                offset = 0;

            $('html, body').animate({
                scrollTop: offset
            }, function(){
                window.location.hash = '';
                $( '.home' ).find('h1').eq(0).attr('tabindex', '-1').focus();
            });

            e.preventDefault();
            return false;
        });

        // Hover sur les liens sociaux des comités
        $('.about .social-medias a').hover(
            function(){
                var url    = $(this).find('img').attr('src'),
                    newUrl = url.replace('-mono', '-mono-hover');
                $(this).find('img').attr('src', newUrl);
            },
            function(){
                var url    = $(this).find('img').attr('src'),
                    newUrl = url.replace('-mono-hover', '-mono');
                $(this).find('img').attr('src', newUrl);
            }
        );

        // Ajustement de la hauteur de la page
        $('.l-page-wrapper').css('min-height', $(window).height() - 30);

        // Hash sur le lien de retour de la page single
        var backlink = $('.event-back-link a'),
            hash = window.location.hash,
            href = backlink.attr('href'),
            currentHash = href.lastIndexOf('#'),
            url = (currentHash == -1) ? href :href.substring(0, currentHash);

        backlink.attr('href', url + hash);

    });

})(jQuery, window, document);
<commit_msg>Append day hash to bottom back link too<commit_after>var LIBEO;

(function($, window, document, undefined) {

    // On document ready
    $(document).ready(function() {

        // Check if the 'WAQ' object is defined
        if (typeof WAQ === 'object') {

        	LIBEO = WAQ;

        	WAQ.Constants = {
        		name            : 'WAQ',
        		onClickClass    : 'isClicked',
        		onHoverClass    : 'isHover',
        		onZoomClass     : 'l-zoomed',
        		fontPrefixClass : 'l-font',
        		isOpenClass     : 'isOpen',
        		isActiveClass   : 'isActive',
        		extLinksClass   : 'external'
        	};

            // Instantiate and store objects
            if( ! $('html').hasClass('lt-ie9') ){
                WAQ.SnapMenu         = WAQ.Common.createObjects($('#nav-main'), SnapMenu, {
                    adjustTopOffset : 45,
                    customNavigation : true,
                    breakpointsSelector : '.l-page-wrapper > section',
                });
                WAQ.SnapMenuSchedule = WAQ.Common.createObjects($('.days-buttons'), SnapMenu, {
                    adjustTopOffset : -133,
                    adjustBottomOffset : -133,
                    delimiterElement : $('.js-slider'),
                });
            }
            WAQ.Schedule = WAQ.Common.createObjects($('.schedule'), Schedule);
            WAQ.Slideshow = WAQ.Common.createObjects($('.blog-wrapper'), Slideshow);
            WAQ.MobileMenu = WAQ.Common.createObjects($('#nav-main'), MobileMenu);
            WAQ.CustomSchedule = WAQ.Common.createObjects($('.schedule-wrapper'), CustomSchedule);
            WAQ.Countdown = WAQ.Common.createObjects($('.event-stats'), Countdown);
            WAQ.GoogleMap = new CustomGmap('#gmap');

            if($('#gmap').length > 0){
                google.maps.event.addListenerOnce(WAQ.GoogleMap.map, 'idle', function() {
                    GoogleMapResize();
                });
            }

            // Common functions
            WAQ.Common.goToContent($('.l-a11y'));
            WAQ.Common.betterFocus(['a', 'button', 'input', 'h2']);
            WAQ.Common.checkForKeyboardInput();
            WAQ.Common.checkFontSize();
            WAQ.Common.externalLinks();
        }

        // Scroll to active hash on page load
        var hash = window.location.hash;
        if ( hash != '' && hash != "#mercredi" && hash != "#jeudi" && hash != "#vendredi" ) {

            // Source : http://stackoverflow.com/questions/3659072/jquery-disable-anchor-jump-when-loading-a-page
            window.scrollTo(0, $( hash ).offset().top - 64);
            setTimeout(function() {
                window.scrollTo(0, $( hash ).offset().top - 64);
            }, 1);

            // Update active menu icon
            $( '#nav-main li a[href="' + hash + '"]' ).parents( 'li' ).addClass( 'active' );
        }

        // Add js class to body
        $( 'body' ).addClass( 'js' );

        // Load more blog articles
        $( '.blog-wrapper' ).append('<button class="load-more">Afficher plus d\'articles</button>');
        $( '.blog-wrapper article' ).eq(2).nextAll().hide();
        $( document ).on( 'click', '.blog-wrapper .load-more', function() {
            WAQ.Common.sequentialFadeIn( '.blog-wrapper article:hidden:lt(3)', 100, 'block', function(){
                if( $('.blog-wrapper article:hidden').length == 0 ){
                    $('.blog-wrapper .load-more').hide();
                }
            });
        });

        // Retour en haut du site
        $('.snapmenu-logo').on('click', function(e){
            var self = this,
                offset = 0;

            $('html, body').animate({
                scrollTop: offset
            }, function(){
                window.location.hash = '';
                $( '.home' ).find('h1').eq(0).attr('tabindex', '-1').focus();
            });

            e.preventDefault();
            return false;
        });

        // Hover sur les liens sociaux des comités
        $('.about .social-medias a').hover(
            function(){
                var url    = $(this).find('img').attr('src'),
                    newUrl = url.replace('-mono', '-mono-hover');
                $(this).find('img').attr('src', newUrl);
            },
            function(){
                var url    = $(this).find('img').attr('src'),
                    newUrl = url.replace('-mono-hover', '-mono');
                $(this).find('img').attr('src', newUrl);
            }
        );

        // Ajustement de la hauteur de la page
        $('.l-page-wrapper').css('min-height', $(window).height() - 30);

        // Hash sur le lien de retour de la page single
        var backlink = $('.event-back-link a, .back-link a'),
            hash = window.location.hash,
            href = backlink.attr('href'),
            currentHash = href.lastIndexOf('#'),
            url = (currentHash == -1) ? href :href.substring(0, currentHash);

        backlink.attr('href', url + hash);

    });

})(jQuery, window, document);
<|endoftext|>