<commit_before>/*
 * grunt-shampoo
 * https://github.com/soapcreative/grunt-shampoo
 *
 * Copyright (c) 2014 Soap Creative
 * Licensed under the MIT license.
 */

'use strict';

var request = require("request"),
    fs = require("fs"),
    knox = require("knox"),
    rc = require('rc'),
    DecompressZip = require('decompress-zip'),
    fs = require('fs'),
    path = require('path'),
    util = require('util'),
    url = require('url'),
    _mkdirp = require('mkdirp'),
    _ = require('lodash'),

    shampooApi = require('./lib/shampoo-api'),
    shampooUtils = require('./lib/shampoo-utils'),
    createHandlerFilter = require('./lib/handler-filters'),
    httpCodes = require('./lib/http-codes'),
    tryHttpDownload = require('./lib/try-http-download');

var ZIP_FOLDER_NAME = "content-backups",
    ZIP_FILE_NAME_PREFIX = "content-dump-";

var DEFAULT_MAX_CONNECTIONS = 8;

module.exports = function( grunt ) {

  grunt.registerMultiTask( "shampoo", "Retrieve content from the Shampoo CMS API on shampoo.io.", function() {

    var thisTask = this,
        handlerFilter = createHandlerFilter(grunt),
        knoxClient = null;

    function mkdirp(dirPath, options, callback) {
      if (typeof options === 'function') {
        callback = options;
        options = null;
      }
      _mkdirp(dirPath, options,
        handlerFilter.logErrors("Couldn't create " + dirPath, callback)
      );
    }

    function isMediaAssetUrl(assetUrl) {
      // a URL is a media asset URL if its scheme/protocol is http or https,
      // and its hostname is a subdomain of amazonaws.com.
      return Boolean(
        assetUrl &&
        /^https?:$/i .test(assetUrl.protocol) &&
        /\.amazonaws\.com$/i .test(assetUrl.hostname)
      );
    }

    function getMediaAssetPath(assetUrlString) {
      var assetUrl;
      try {
        assetUrl = url.parse(assetUrlString);
        if (isMediaAssetUrl(assetUrl)) {
          var pathname = assetUrl.pathname;
          if (pathname.charAt(0) === "/") {
            pathname = pathname.slice(1);
          }
          // url.parse automatically url-escapes characters, but we need to
          // reverse that for passing to the knox client, as well as for saving
          // locally
          return decodeURI(pathname);
        }
      } catch (error) { } // url.parse failed, so just fall through to return null
      return null;
    }

    function getMediaAssets( obj, mediaCwd ) {
      var remotePaths = { },
          newJson;

      newJson = shampooUtils.transformJson(obj,
        function (value, key, parentObject, keyPath) {
          if (typeof value === "string") {
            var assetPath = getMediaAssetPath(value);

            if (assetPath != null) {
              var localPath = path.join(mediaCwd, assetPath);
              remotePaths[assetPath] = true;

              grunt.log.debug(
                "Rewriting %s\n" +
                "  old: %j\n" +
                "  new: %j\n",
                keyPath.join("."), value, localPath
              );

              return localPath;
            }
          }
          return value;
        }
      );

      return {
        newJson:     newJson,
        remotePaths: Object.keys(remotePaths)
      };
    }

    function processJsonFiles(localPaths, options, callback) {
      var waiting = 0;

      function fileComplete() {
        if (--waiting <= 0) {
          callback();
        }
      }

      localPaths.forEach(function (localPath) {
        waiting++;
        fs.readFile(localPath, handlerFilter.expectJsonContents(localPath,
          function (error, jsonContent) {
            if (error) {
              fileComplete();
            } else {
              processJson(jsonContent, localPath, options, fileComplete);
            }
          }
        ));
      });
    }

    function processZipFile(zipPath, options, callback) {
      var unzipper = new DecompressZip(zipPath);

      unzipper
        .once("extract", function (log) {
          grunt.log.debug("%s extract log:\n%j", zipPath, log);
          var extractedFiles = log.map(function (extractResult) {
              return extractResult.deflated || extractResult.stored;
            }).filter(function (extractedPath) {
              return Boolean(extractedPath);
            }).map(function (extractedPath) {
              return path.join(options.zipOut, extractedPath);
            });

          if (extractedFiles.length > 0) {
            processJsonFiles(extractedFiles, options, callback);
          } else {
            callback();
          }
        })
        .once("error", function(error) {
          grunt.log.error("Error unzipping file %j: %s", zipPath, error);
          callback();
        });

      unzipper.extract({ path: options.zipOut });
    }

    function generateZipFileName() {
      return ZIP_FILE_NAME_PREFIX + Date.now() + ".zip";
    }

    function requestZip(zipUrl, options, callback) {
      var zipPath = path.join(
        options.zipOut, ZIP_FOLDER_NAME, generateZipFileName());

      mkdirp(path.dirname(zipPath), null, function (mkdirError) {
        if (mkdirError) {
          callback();
          return;
        }

        grunt.verbose.writeln("Downloading zip");
        shampooUtils.downloadToFile(zipUrl, zipPath, handlerFilter.expectHttpOk(zipUrl,
          function (error) {
            if (error) {
              callback();
            } else {
              processZipFile(zipPath, options, callback);
            }
          }
        ));
      });
    }

    function saveMedia(options, mediaAssets, callback) {

      grunt.log.subhead( "Retrieving files..." );

      grunt.log.debug("Media queue is:");
      mediaAssets.forEach(function (p) {
        grunt.log.debug("  %j", p);
      });

      var loadCounter = 0;
      var next = function() {
        loadCounter--;
        fillQueue();
      };

      var fillQueue = function() {
        if (mediaAssets.length === 0 && loadCounter === 0) {
          callback();
        } else {
          while (mediaAssets.length > 0 && loadCounter < options.maxConnections) {
            loadCounter++;
            verifyDownload( mediaAssets.shift(), options.mediaOut, next );
          }
        }
      };

      fillQueue();
    }

    function verifyDownload( remotePath, mediaOut, callback ) {
      var localPath = path.join(mediaOut, remotePath),
          tryOptions = {
            logError:   grunt.log.error.bind(grunt.log),
            logVerbose: grunt.verbose.writeln.bind(grunt.verbose),
            logDebug:   grunt.log.debug.bind(grunt.log)
          };

      grunt.log.debug("Verifying %j -> %j", remotePath, localPath);

      shampooUtils.hashFile(localPath, "md5", function (error, hasher) {
        if (error) {
          grunt.log.debug("Etag calculation of local file failed: %s", error);
        } else {
          tryOptions.etag = hasher.digest("hex");
        }

        mkdirp(path.dirname(localPath), function (error) {
          if (error) {
            callback();
          } else {
            tryHttpDownload(
              function (headers, tryCallback) {
                grunt.log.debug("S3 GET %j", remotePath);
                knoxClient.getFile(remotePath, headers, tryCallback);
              },
              localPath,
              tryOptions,
              function (error, response) {
                grunt.log.write("%s ", localPath);
                if (error) {
                  grunt.log.error("%j", error);
                } else if (response.statusCode === httpCodes.NOT_MODIFIED) {
                  grunt.log.ok("up to date");
                } else {
                  grunt.log.ok("downloaded");
                }
                callback();
              }
            );
          }
        });
      });
    }

    function writeJsonFile(out, object) {
      grunt.log.write( out + " ");
      grunt.log.ok( "saved" );
      grunt.file.write(out, JSON.stringify(object, null, '\t'));
    }

    function processJson(jsonContent, outJsonFile, options, callback) {
      var result = getMediaAssets(jsonContent, options.mediaCwd);

      writeJsonFile(outJsonFile, result.newJson);
      if (options.downloadMedia) {
        saveMedia(options, result.remotePaths, callback);
      } else {
        callback();
      }
    }

    function requestJson(jsonUrl, options, callback) {
      request(jsonUrl, handlerFilter.expectJsonResponse(jsonUrl,
        function (error, response, jsonContent) {
          if (error) {
            callback();
          } else {
            processJson(jsonContent, options.out, options, callback);
          }
        }
      ));
    }

    function requestFiles(options, callback) {
      grunt.log.subhead( "Retrieving content..." );

      var apiUrl = shampooApi.createApiUrl(options);
      grunt.verbose.writeln("Url is %j", apiUrl);

      if (shampooApi.isZipQuery(options.query)) {
        grunt.verbose.writeln("Zip job");
        requestZip(apiUrl, options, callback);
      } else {
        grunt.verbose.writeln("JSON job");
        requestJson(apiUrl, options, callback);
      }
    }

    function makeClient(options) {
      return knox.createClient( _.pick(options, [
        'region', 'endpoint', 'port', 'key', 'secret', 'access', 'bucket',
        'secure', 'headers', 'style'
      ]));
    }

    function getOptions() {
      // Mix in default options, .shampoorc file
      var options = rc("shampoo", thisTask.options({
        api: 1,
        query: "dump/json/single-file",
        out: "data/content.json",
        mediaOut: null,
        mediaCwd: null,
        downloadMedia: null,
        maxConnections: DEFAULT_MAX_CONNECTIONS
      }));

      var messages = [ ];
      var missing = { };
      var required = [ "key", "secret", "domain", "query", "out" ];
      var isOk = true;

      if (shampooApi.isZipQuery(options.query)) {
        required.push("zipOut");
      }

      required.forEach(function (optionName) {
        if (!options[optionName]) {
          missing[optionName] = true;
        }
      });

      var missingArray = Object.keys(missing);
      if (missingArray.length > 0) {
        messages.push("The following required options are not set: " +
          missingArray.join(", "));
        isOk = false;
      }

      if (missing.key || missing.secret) {
        messages.push("Values for 'key' and 'secret' are found in your Shampoo account under Settings.");
      }

      if (missing.zipOut) {
        messages.push(util.format(
          "The query %j returns a zip file. This requires the 'zipOut' option to be set.",
          options.query
        ));
      }

      // downloadMedia is now an explicit option, but i've tried to add it in
      // a backwards compatible way. this, however, means its default value
      // can change:
      // - if mediaOut is specified, it defaults to true
      // - if mediaOut is null or undefined, it defaults to false
      //
      // this mimics pre-v0.0.14 behavior that would use the presence of the
      // mediaOut option to decide whether to download media files.
      //
      // this is done so gruntfiles with unspecified mediaOut values don't
      // start suddenly downloading everything to the current directory after
      // upgrading, but it will be best to explicitly set the downloadMedia
      // option
      if (options.downloadMedia == null) {
        options.downloadMedia = options.mediaOut != null;
      }

      if (options.mediaOut == null) {
        options.mediaOut = ".";
      }

      if (options.mediaCwd == null) {
        options.mediaCwd = options.mediaOut;
      }

      var parsedMaxConnections = parseInt(options.maxConnections, 10);
      if (isNaN(options.maxConnections)) {
        messages.push(util.format(
          "Invalid value for maxConnections: %j. Assuming default of %d.",
          options.maxConnections, DEFAULT_MAX_CONNECTIONS
        ));
        parsedMaxConnections = DEFAULT_MAX_CONNECTIONS;
      } else if (parsedMaxConnections < 1) {
        messages.push(
          "Value for maxConnections must be 1 or greater. Assuming 1.");
        parsedMaxConnections = 1;
      }

      options.maxConnections = parsedMaxConnections;

      return {
        options: options,
        messages: messages,
        ok: isOk
      };
    }

    function main() {
      var optionResult = getOptions();
      var messagesString = optionResult.messages.join("\n");
      var callback = thisTask.async();

      if (optionResult.ok) {
        var options = optionResult.options;
        grunt.log.writeln(messagesString);
        if (options.mediaOut) {
          knoxClient = makeClient(options.aws || { });
        }
        requestFiles(optionResult.options, callback);
      } else {
        grunt.log.error(messagesString);
        callback(false);
      }

    }

    return main();
  });
};
<commit_msg>allow use of a grunt command line option, --shampoo-no-download, to suppress downloading of media assets.<commit_after>/*
 * grunt-shampoo
 * https://github.com/soapcreative/grunt-shampoo
 *
 * Copyright (c) 2014 Soap Creative
 * Licensed under the MIT license.
 */

'use strict';

var request = require("request"),
    fs = require("fs"),
    knox = require("knox"),
    rc = require('rc'),
    DecompressZip = require('decompress-zip'),
    fs = require('fs'),
    path = require('path'),
    util = require('util'),
    url = require('url'),
    _mkdirp = require('mkdirp'),
    _ = require('lodash'),

    shampooApi = require('./lib/shampoo-api'),
    shampooUtils = require('./lib/shampoo-utils'),
    createHandlerFilter = require('./lib/handler-filters'),
    httpCodes = require('./lib/http-codes'),
    tryHttpDownload = require('./lib/try-http-download');

var ZIP_FOLDER_NAME = "content-backups",
    ZIP_FILE_NAME_PREFIX = "content-dump-";

var DEFAULT_MAX_CONNECTIONS = 8;

module.exports = function( grunt ) {

  grunt.registerMultiTask( "shampoo", "Retrieve content from the Shampoo CMS API on shampoo.io.", function() {

    var thisTask = this,
        handlerFilter = createHandlerFilter(grunt),
        knoxClient = null;

    function mkdirp(dirPath, options, callback) {
      if (typeof options === 'function') {
        callback = options;
        options = null;
      }
      _mkdirp(dirPath, options,
        handlerFilter.logErrors("Couldn't create " + dirPath, callback)
      );
    }

    function isMediaAssetUrl(assetUrl) {
      // a URL is a media asset URL if its scheme/protocol is http or https,
      // and its hostname is a subdomain of amazonaws.com.
      return Boolean(
        assetUrl &&
        /^https?:$/i .test(assetUrl.protocol) &&
        /\.amazonaws\.com$/i .test(assetUrl.hostname)
      );
    }

    function getMediaAssetPath(assetUrlString) {
      var assetUrl;
      try {
        assetUrl = url.parse(assetUrlString);
        if (isMediaAssetUrl(assetUrl)) {
          var pathname = assetUrl.pathname;
          if (pathname.charAt(0) === "/") {
            pathname = pathname.slice(1);
          }
          // url.parse automatically url-escapes characters, but we need to
          // reverse that for passing to the knox client, as well as for saving
          // locally
          return decodeURI(pathname);
        }
      } catch (error) { } // url.parse failed, so just fall through to return null
      return null;
    }

    function getMediaAssets( obj, mediaCwd ) {
      var remotePaths = { },
          newJson;

      newJson = shampooUtils.transformJson(obj,
        function (value, key, parentObject, keyPath) {
          if (typeof value === "string") {
            var assetPath = getMediaAssetPath(value);

            if (assetPath != null) {
              var localPath = path.join(mediaCwd, assetPath);
              remotePaths[assetPath] = true;

              grunt.log.debug(
                "Rewriting %s\n" +
                "  old: %j\n" +
                "  new: %j\n",
                keyPath.join("."), value, localPath
              );

              return localPath;
            }
          }
          return value;
        }
      );

      return {
        newJson:     newJson,
        remotePaths: Object.keys(remotePaths)
      };
    }

    function processJsonFiles(localPaths, options, callback) {
      var waiting = 0;

      function fileComplete() {
        if (--waiting <= 0) {
          callback();
        }
      }

      localPaths.forEach(function (localPath) {
        waiting++;
        fs.readFile(localPath, handlerFilter.expectJsonContents(localPath,
          function (error, jsonContent) {
            if (error) {
              fileComplete();
            } else {
              processJson(jsonContent, localPath, options, fileComplete);
            }
          }
        ));
      });
    }

    function processZipFile(zipPath, options, callback) {
      var unzipper = new DecompressZip(zipPath);

      unzipper
        .once("extract", function (log) {
          grunt.log.debug("%s extract log:\n%j", zipPath, log);
          var extractedFiles = log.map(function (extractResult) {
              return extractResult.deflated || extractResult.stored;
            }).filter(function (extractedPath) {
              return Boolean(extractedPath);
            }).map(function (extractedPath) {
              return path.join(options.zipOut, extractedPath);
            });

          if (extractedFiles.length > 0) {
            processJsonFiles(extractedFiles, options, callback);
          } else {
            callback();
          }
        })
        .once("error", function(error) {
          grunt.log.error("Error unzipping file %j: %s", zipPath, error);
          callback();
        });

      unzipper.extract({ path: options.zipOut });
    }

    function generateZipFileName() {
      return ZIP_FILE_NAME_PREFIX + Date.now() + ".zip";
    }

    function requestZip(zipUrl, options, callback) {
      var zipPath = path.join(
        options.zipOut, ZIP_FOLDER_NAME, generateZipFileName());

      mkdirp(path.dirname(zipPath), null, function (mkdirError) {
        if (mkdirError) {
          callback();
          return;
        }

        grunt.verbose.writeln("Downloading zip");
        shampooUtils.downloadToFile(zipUrl, zipPath, handlerFilter.expectHttpOk(zipUrl,
          function (error) {
            if (error) {
              callback();
            } else {
              processZipFile(zipPath, options, callback);
            }
          }
        ));
      });
    }

    function saveMedia(options, mediaAssets, callback) {

      grunt.log.subhead( "Retrieving files..." );

      grunt.log.debug("Media queue is:");
      mediaAssets.forEach(function (p) {
        grunt.log.debug("  %j", p);
      });

      var loadCounter = 0;
      var next = function() {
        loadCounter--;
        fillQueue();
      };

      var fillQueue = function() {
        if (mediaAssets.length === 0 && loadCounter === 0) {
          callback();
        } else {
          while (mediaAssets.length > 0 && loadCounter < options.maxConnections) {
            loadCounter++;
            verifyDownload( mediaAssets.shift(), options.mediaOut, next );
          }
        }
      };

      fillQueue();
    }

    function verifyDownload( remotePath, mediaOut, callback ) {
      var localPath = path.join(mediaOut, remotePath),
          tryOptions = {
            logError:   grunt.log.error.bind(grunt.log),
            logVerbose: grunt.verbose.writeln.bind(grunt.verbose),
            logDebug:   grunt.log.debug.bind(grunt.log)
          };

      grunt.log.debug("Verifying %j -> %j", remotePath, localPath);

      shampooUtils.hashFile(localPath, "md5", function (error, hasher) {
        if (error) {
          grunt.log.debug("Etag calculation of local file failed: %s", error);
        } else {
          tryOptions.etag = hasher.digest("hex");
        }

        mkdirp(path.dirname(localPath), function (error) {
          if (error) {
            callback();
          } else {
            tryHttpDownload(
              function (headers, tryCallback) {
                grunt.log.debug("S3 GET %j", remotePath);
                knoxClient.getFile(remotePath, headers, tryCallback);
              },
              localPath,
              tryOptions,
              function (error, response) {
                grunt.log.write("%s ", localPath);
                if (error) {
                  grunt.log.error("%j", error);
                } else if (response.statusCode === httpCodes.NOT_MODIFIED) {
                  grunt.log.ok("up to date");
                } else {
                  grunt.log.ok("downloaded");
                }
                callback();
              }
            );
          }
        });
      });
    }

    function writeJsonFile(out, object) {
      grunt.log.write( out + " ");
      grunt.log.ok( "saved" );
      grunt.file.write(out, JSON.stringify(object, null, '\t'));
    }

    function processJson(jsonContent, outJsonFile, options, callback) {
      var result = getMediaAssets(jsonContent, options.mediaCwd);

      writeJsonFile(outJsonFile, result.newJson);
      if (options.downloadMedia) {
        saveMedia(options, result.remotePaths, callback);
      } else {
        callback();
      }
    }

    function requestJson(jsonUrl, options, callback) {
      request(jsonUrl, handlerFilter.expectJsonResponse(jsonUrl,
        function (error, response, jsonContent) {
          if (error) {
            callback();
          } else {
            processJson(jsonContent, options.out, options, callback);
          }
        }
      ));
    }

    function requestFiles(options, callback) {
      grunt.log.subhead( "Retrieving content..." );

      var apiUrl = shampooApi.createApiUrl(options);
      grunt.verbose.writeln("Url is %j", apiUrl);

      if (shampooApi.isZipQuery(options.query)) {
        grunt.verbose.writeln("Zip job");
        requestZip(apiUrl, options, callback);
      } else {
        grunt.verbose.writeln("JSON job");
        requestJson(apiUrl, options, callback);
      }
    }

    function makeClient(options) {
      return knox.createClient( _.pick(options, [
        'region', 'endpoint', 'port', 'key', 'secret', 'access', 'bucket',
        'secure', 'headers', 'style'
      ]));
    }

    function getOptions() {
      // Mix in default options, .shampoorc file
      var options = rc("shampoo", thisTask.options({
        api: 1,
        query: "dump/json/single-file",
        out: "data/content.json",
        mediaOut: null,
        mediaCwd: null,
        downloadMedia: null,
        maxConnections: DEFAULT_MAX_CONNECTIONS
      }));

      var messages = [ ];
      var missing = { };
      var required = [ "key", "secret", "domain", "query", "out" ];
      var isOk = true;

      if (shampooApi.isZipQuery(options.query)) {
        required.push("zipOut");
      }

      required.forEach(function (optionName) {
        if (!options[optionName]) {
          missing[optionName] = true;
        }
      });

      var missingArray = Object.keys(missing);
      if (missingArray.length > 0) {
        messages.push("The following required options are not set: " +
          missingArray.join(", "));
        isOk = false;
      }

      if (missing.key || missing.secret) {
        messages.push("Values for 'key' and 'secret' are found in your Shampoo account under Settings.");
      }

      if (missing.zipOut) {
        messages.push(util.format(
          "The query %j returns a zip file. This requires the 'zipOut' option to be set.",
          options.query
        ));
      }

      // downloadMedia is now an explicit option, but i've tried to add it in
      // a backwards compatible way. this, however, means its default value
      // can change:
      // - if mediaOut is specified, it defaults to true
      // - if mediaOut is null or undefined, it defaults to false
      //
      // this mimics pre-v0.0.14 behavior that would use the presence of the
      // mediaOut option to decide whether to download media files.
      //
      // this is done so gruntfiles with unspecified mediaOut values don't
      // start suddenly downloading everything to the current directory after
      // upgrading, but it will be best to explicitly set the downloadMedia
      // option
      if (options.downloadMedia == null) {
        options.downloadMedia = options.mediaOut != null;
      }

      // allow temporary override via command line
      // specify --shampoo-no-download
      if (grunt.option("shampoo-no-download")) {
        options.downloadMedia = false;
      }

      if (options.mediaOut == null) {
        options.mediaOut = ".";
      }

      if (options.mediaCwd == null) {
        options.mediaCwd = options.mediaOut;
      }

      var parsedMaxConnections = parseInt(options.maxConnections, 10);
      if (isNaN(options.maxConnections)) {
        messages.push(util.format(
          "Invalid value for maxConnections: %j. Assuming default of %d.",
          options.maxConnections, DEFAULT_MAX_CONNECTIONS
        ));
        parsedMaxConnections = DEFAULT_MAX_CONNECTIONS;
      } else if (parsedMaxConnections < 1) {
        messages.push(
          "Value for maxConnections must be 1 or greater. Assuming 1.");
        parsedMaxConnections = 1;
      }

      options.maxConnections = parsedMaxConnections;

      return {
        options: options,
        messages: messages,
        ok: isOk
      };
    }

    function main() {
      var optionResult = getOptions();
      var messagesString = optionResult.messages.join("\n");
      var callback = thisTask.async();

      if (optionResult.ok) {
        var options = optionResult.options;
        grunt.log.writeln(messagesString);
        if (options.mediaOut) {
          knoxClient = makeClient(options.aws || { });
        }
        requestFiles(optionResult.options, callback);
      } else {
        grunt.log.error(messagesString);
        callback(false);
      }

    }

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


<commit_before>class Person {

  constructor (name) {
      this.name = name;
  }

  hi () {
      return this.name;
  }

  // Computed property
  get firstInitial() {
    return this.name.slice(0, 1);
  }

}

export class Man extends Person {

  constructor (name) {
      super(name);
  }

  hi () {
      return 'I am a man and my name is ' + super();
  }

}
<commit_msg>fix super call<commit_after>class Person {

  constructor (name) {
      this.name = name;
  }

  hi () {
      return this.name;
  }

  // Computed property
  get firstInitial() {
    return this.name.slice(0, 1);
  }

}

export class Man extends Person {

  constructor (name) {
      super(name);
  }

  hi () {
      return 'I am a man and my name is ' + super.hi();
  }

}
<|endoftext|>


<commit_before>import angular from 'angular';
import uiRouter from 'angular-ui-router';
import Common from './common/common';
import Components from './components/components';
import AppComponent from './app.component';

import 'bootstrap';
import 'bootstrap/less/bootstrap.less';

angular.module('app', [
  uiRouter,
  Common.name,
  Components.name
])

.directive('app', AppComponent);
<commit_msg>form-input-match: unneeded input arg is removed<commit_after>import angular from 'angular';
import uiRouter from 'angular-ui-router';
import Common from './common/common';
import Components from './components/components';
import AppComponent from './app.component';

import 'bootstrap';
import 'bootstrap/less/bootstrap.less';

angular.module('app', [
    uiRouter,
    Common.name,
    Components.name
  ])

  .directive('app', AppComponent);
<|endoftext|>


<commit_before>/* Copyright (C) 2013 Coders at Work */
'use strict';

window.__angularjs_loader_testing = true;

<commit_msg>Adding noinit to the test.<commit_after>/* Copyright (C) 2013 Coders at Work */
'use strict';

window.__angularjs_loader_testing = true;
window.__angularjs_loader_noinit = true;
<|endoftext|>


<commit_before>/*
 * (c) Copyright Ascensio System SIA 2010-2019
 *
 * This program is a free software product. You can redistribute it and/or
 * modify it under the terms of the GNU Affero General Public License (AGPL)
 * version 3 as published by the Free Software Foundation. In accordance with
 * Section 7(a) of the GNU AGPL its Section 15 shall be amended to the effect
 * that Ascensio System SIA expressly excludes the warranty of non-infringement
 * of any third-party rights.
 *
 * This program is distributed WITHOUT ANY WARRANTY; without even the implied
 * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR  PURPOSE. For
 * details, see the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
 *
 * You can contact Ascensio System SIA at 20A-12 Ernesta Birznieka-Upisha
 * street, Riga, Latvia, EU, LV-1050.
 *
 * The  interactive user interfaces in modified source and object code versions
 * of the Program must display Appropriate Legal Notices, as required under
 * Section 5 of the GNU AGPL version 3.
 *
 * Pursuant to Section 7(b) of the License you must retain the original Product
 * logo when distributing the program. Pursuant to Section 7(e) we decline to
 * grant you any rights under trademark law for use of our trademarks.
 *
 * All the Product's GUI elements, including illustrations and icon sets, as
 * well as technical writing content are licensed under the terms of the
 * Creative Commons Attribution-ShareAlike 4.0 International. See the License
 * terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
 *
 */

//This file containes definition of object which used in api.js
//It need to prevent minimize the name of object's method.

var Asc;
var AscDFH;
var AscCH;
var AscFormat;
var AscFonts;
var AscCommon;
var AscCommonWord;
var AscCommonExcel;
var AscCommonSlide;

function jq(){}
function DE(){}
function SE(){}
function SSE(){}
function PE(){}
function Ext(){}
function FB(){}
function twttr(){}
function _gaq(){}

function constructor(){}

function touchstart(){}
function touchmove(){}
function touchend(){}
function mousedown(){}
function mousemove(){}
function mouseup(){}
function pointerdown(){}
function pointermove(){}
function pointerup(){}
function MSPointerDown(){}
function MSPointerMove(){}
function MSPointerUp(){}

var editor;
<commit_msg>Developing (2 font engines)<commit_after>/*
 * (c) Copyright Ascensio System SIA 2010-2019
 *
 * This program is a free software product. You can redistribute it and/or
 * modify it under the terms of the GNU Affero General Public License (AGPL)
 * version 3 as published by the Free Software Foundation. In accordance with
 * Section 7(a) of the GNU AGPL its Section 15 shall be amended to the effect
 * that Ascensio System SIA expressly excludes the warranty of non-infringement
 * of any third-party rights.
 *
 * This program is distributed WITHOUT ANY WARRANTY; without even the implied
 * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR  PURPOSE. For
 * details, see the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
 *
 * You can contact Ascensio System SIA at 20A-12 Ernesta Birznieka-Upisha
 * street, Riga, Latvia, EU, LV-1050.
 *
 * The  interactive user interfaces in modified source and object code versions
 * of the Program must display Appropriate Legal Notices, as required under
 * Section 5 of the GNU AGPL version 3.
 *
 * Pursuant to Section 7(b) of the License you must retain the original Product
 * logo when distributing the program. Pursuant to Section 7(e) we decline to
 * grant you any rights under trademark law for use of our trademarks.
 *
 * All the Product's GUI elements, including illustrations and icon sets, as
 * well as technical writing content are licensed under the terms of the
 * Creative Commons Attribution-ShareAlike 4.0 International. See the License
 * terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
 *
 */

//This file containes definition of object which used in api.js
//It need to prevent minimize the name of object's method.

var Asc;
var AscDFH;
var AscCH;
var AscFormat;
var AscFonts;
var AscCommon;
var AscCommonWord;
var AscCommonExcel;
var AscCommonSlide;

function jq(){}
function DE(){}
function SE(){}
function SSE(){}
function PE(){}
function Ext(){}
function FB(){}
function twttr(){}
function _gaq(){}

function constructor(){}

function touchstart(){}
function touchmove(){}
function touchend(){}
function mousedown(){}
function mousemove(){}
function mouseup(){}
function pointerdown(){}
function pointermove(){}
function pointerup(){}
function MSPointerDown(){}
function MSPointerMove(){}
function MSPointerUp(){}

var editor;

var WebAssembly;
WebAssembly.instantiate;
WebAssembly.instantiateStreaming;
WebAssembly.Table;
WebAssembly.Memory;
<|endoftext|>


<commit_before>(function($) {
  /*
    ======== A Handy Little QUnit Reference ========
    http://api.qunitjs.com/

    Test methods:
      module(name, {[setup][ ,teardown]})
      test(name, callback)
      expect(numberOfAssertions)
      stop(increment)
      start(decrement)
    Test assertions:
      ok(value, [message])
      equal(actual, expected, [message])
      notEqual(actual, expected, [message])
      deepEqual(actual, expected, [message])
      notDeepEqual(actual, expected, [message])
      strictEqual(actual, expected, [message])
      notStrictEqual(actual, expected, [message])
      throws(block, [expected], [message])
  */

 module('core', {
  // This will run before each test in this module.
    setup: function() {
      this.elems = $('#qunit-fixture');
    }, teardown: function() {
      $('.scroll-nav').remove();
    }
 });

 test('is chainable', function() {
    expect(1);
    // Not a bad test to run on collection methods.
    strictEqual(this.elems.scrollNav(), this.elems, 'should be chainable');
  });

  module('sections', {
    // This will run before each test in this module.
    setup: function() {
      this.elems = $('#qunit-fixture');
      this.sections = this.elems.scrollNav().children();
    }, teardown: function() {
      $('.scroll-nav').remove();
    }
  });

  test('correct total number of sections', function() {
    expect(3);

    strictEqual(this.sections.length, 2, 'should be two');
    notStrictEqual(this.sections.length, 1, 'should not be one');
    notStrictEqual(this.sections.length, 0, 'should not be zero');
  });

  test('correct number of children in first section', function() {
    expect(3);

    strictEqual(this.sections.eq(0).children().length, 6, 'should be six');
    notStrictEqual(this.sections.eq(0).children().length, 5, 'should not be five');
    notStrictEqual(this.sections.eq(0).children().length, 7, 'should not be seven');
  });

  test('correct number of children in second section', function() {
    expect(3);

    strictEqual(this.sections.eq(1).children().length, 6, 'should be six');
    notStrictEqual(this.sections.eq(1).children().length, 5, 'should not be five');
    notStrictEqual(this.sections.eq(1).children().length, 7, 'should not be seven');
  });

  test('first section is correct tag', function() {
    expect(4);
    var sectionTag = this.sections.eq(0).prop('tagName');

    strictEqual(sectionTag, 'SECTION', 'should be a section tag');
    notStrictEqual(sectionTag, 'DIV', 'should not be a div tag');
    notStrictEqual(sectionTag, 'H2', 'should not be an h2 tag');
    notStrictEqual(sectionTag, 'P', 'should not be a p tag');
  });

  test('second section is correct tag', function() {
    expect(4);
    var sectionTag = this.sections.eq(1).prop('tagName');

    strictEqual(sectionTag, 'SECTION', 'should be a section tag');
    notStrictEqual(sectionTag, 'DIV', 'should not be a div tag');
    notStrictEqual(sectionTag, 'H2', 'should not be an h2 tag');
    notStrictEqual(sectionTag, 'P', 'should not be a p tag');
  });

  test('first section has correct id', function() {
    expect(4);
    var sectionID = this.sections.eq(0).attr('id');

    strictEqual(sectionID, 'scrollNav-1', 'should be scrollNav-1');
    notStrictEqual(sectionID, 'scrollNav-0', 'should not be scrollNav-0');
    notStrictEqual(sectionID, 'scrollNav-2', 'should not be scrollNav-2');
    notStrictEqual(sectionID, 'scrollNav-1-1', 'should not be scrollNav-1-1');
  });

  test('second section has correct id', function() {
    expect(4);
    var sectionID = this.sections.eq(1).attr('id');

    strictEqual(sectionID, 'scrollNav-2', 'should be scrollNav-2');
    notStrictEqual(sectionID, 'scrollNav-1', 'should not be scrollNav-1');
    notStrictEqual(sectionID, 'scrollNav-3', 'should not be scrollNav-3');
    notStrictEqual(sectionID, 'scrollNav-2-1', 'should not be scrollNav-2-1');
  });

  module('subSections', {
    // This will run before each test in this module.
    setup: function() {
      this.elems = $('#qunit-fixture');
      this.scrollNav = this.elems.scrollNav({
        sections: 'h2',
        subSections: 'h3',
        showHeadline: true
      });
      this.sections = this.scrollNav.children();
    }, teardown: function() {
      $('.scroll-nav').remove();
    }
  });

  test('correct total number of sub-sections in first section', function() {
    expect(3);
    var $subSections = this.sections.eq(0).find('.scroll-nav__sub-section');

    strictEqual($subSections.length, 2, 'should be two');
    notStrictEqual($subSections.length, 1, 'should not be one');
    notStrictEqual($subSections.length, 0, 'should not be zero');
  });

  module('nav', {
    // This will run before each test in this module.
    setup: function() {
      this.elems = $('#qunit-fixture');
      this.scrollNav = this.elems.scrollNav();
      this.sections = this.scrollNav.children();
      this.nav = this.scrollNav.siblings('.scroll-nav');
    }, teardown: function() {
      $('.scroll-nav').remove();
    }
  });

  test('first nav lik has correct text', function() {
    expect(3);
    var headingText = this.sections.eq(0).children().eq(0).text();
    var linkText = this.nav.find('li').eq(0).text();

    strictEqual(linkText, headingText, 'should be Test Heading');
    notStrictEqual(linkText, 'lame test markup', 'should not be lame test');
    notStrictEqual(linkText, 'Another Test Heading', 'should not be Another Test Heading');
  });

  test('second nav lik has correct text', function() {
    expect(3);
    var headingText = this.sections.eq(1).children().eq(0).text();
    var linkText = this.nav.find('li').eq(1).text();

    strictEqual(linkText, headingText, 'should be Test Heading');
    notStrictEqual(linkText, 'lame test markup', 'should not be lame test');
    notStrictEqual(linkText, 'Test Heading', 'should not be Test Heading');
  });

  test('first nav lik has correct url', function() {
    expect(3);
    var sectionID = this.sections.eq(0).attr('id');
    var linkURL = this.nav.find('li').eq(0).find('a').attr('href');

    strictEqual(linkURL, '#' + sectionID, 'should be #scrollNav-1');
    notStrictEqual(linkURL, 'lame test markup', 'should not be #scrollNav-0');
    notStrictEqual(linkURL, 'Another Test Heading', 'should not be #scrollNav-2');
  });

  test('second nav lik has correct url', function() {
    expect(3);
    var sectionID = this.sections.eq(1).attr('id');
    var linkURL = this.nav.find('li').eq(1).find('a').attr('href');

    strictEqual(linkURL, '#' + sectionID, 'should be #scrollNav-1');
    notStrictEqual(linkURL, 'lame test markup', 'should not be #scrollNav-1');
    notStrictEqual(linkURL, 'Another Test Heading', 'should not be #scrollNav-3');
  });

}(jQuery));
<commit_msg>Added test to check that nav is inserted<commit_after>(function($) {
  /*
    ======== A Handy Little QUnit Reference ========
    http://api.qunitjs.com/

    Test methods:
      module(name, {[setup][ ,teardown]})
      test(name, callback)
      expect(numberOfAssertions)
      stop(increment)
      start(decrement)
    Test assertions:
      ok(value, [message])
      equal(actual, expected, [message])
      notEqual(actual, expected, [message])
      deepEqual(actual, expected, [message])
      notDeepEqual(actual, expected, [message])
      strictEqual(actual, expected, [message])
      notStrictEqual(actual, expected, [message])
      throws(block, [expected], [message])
  */

 module('core', {
  // This will run before each test in this module.
    setup: function() {
      this.elems = $('#qunit-fixture');
    }, teardown: function() {
      $('.scroll-nav').remove();
    }
 });

 test('is chainable', function() {
    expect(1);
    // Not a bad test to run on collection methods.
    strictEqual(this.elems.scrollNav(), this.elems, 'should be chainable');
  });

  module('sections', {
    // This will run before each test in this module.
    setup: function() {
      this.elems = $('#qunit-fixture');
      this.sections = this.elems.scrollNav().children();
    }, teardown: function() {
      $('.scroll-nav').remove();
    }
  });

  test('correct total number of sections', function() {
    expect(3);

    strictEqual(this.sections.length, 2, 'should be two');
    notStrictEqual(this.sections.length, 1, 'should not be one');
    notStrictEqual(this.sections.length, 0, 'should not be zero');
  });

  test('correct number of children in first section', function() {
    expect(3);

    strictEqual(this.sections.eq(0).children().length, 6, 'should be six');
    notStrictEqual(this.sections.eq(0).children().length, 5, 'should not be five');
    notStrictEqual(this.sections.eq(0).children().length, 7, 'should not be seven');
  });

  test('correct number of children in second section', function() {
    expect(3);

    strictEqual(this.sections.eq(1).children().length, 6, 'should be six');
    notStrictEqual(this.sections.eq(1).children().length, 5, 'should not be five');
    notStrictEqual(this.sections.eq(1).children().length, 7, 'should not be seven');
  });

  test('first section is correct tag', function() {
    expect(4);
    var sectionTag = this.sections.eq(0).prop('tagName');

    strictEqual(sectionTag, 'SECTION', 'should be a section tag');
    notStrictEqual(sectionTag, 'DIV', 'should not be a div tag');
    notStrictEqual(sectionTag, 'H2', 'should not be an h2 tag');
    notStrictEqual(sectionTag, 'P', 'should not be a p tag');
  });

  test('second section is correct tag', function() {
    expect(4);
    var sectionTag = this.sections.eq(1).prop('tagName');

    strictEqual(sectionTag, 'SECTION', 'should be a section tag');
    notStrictEqual(sectionTag, 'DIV', 'should not be a div tag');
    notStrictEqual(sectionTag, 'H2', 'should not be an h2 tag');
    notStrictEqual(sectionTag, 'P', 'should not be a p tag');
  });

  test('first section has correct id', function() {
    expect(4);
    var sectionID = this.sections.eq(0).attr('id');

    strictEqual(sectionID, 'scrollNav-1', 'should be scrollNav-1');
    notStrictEqual(sectionID, 'scrollNav-0', 'should not be scrollNav-0');
    notStrictEqual(sectionID, 'scrollNav-2', 'should not be scrollNav-2');
    notStrictEqual(sectionID, 'scrollNav-1-1', 'should not be scrollNav-1-1');
  });

  test('second section has correct id', function() {
    expect(4);
    var sectionID = this.sections.eq(1).attr('id');

    strictEqual(sectionID, 'scrollNav-2', 'should be scrollNav-2');
    notStrictEqual(sectionID, 'scrollNav-1', 'should not be scrollNav-1');
    notStrictEqual(sectionID, 'scrollNav-3', 'should not be scrollNav-3');
    notStrictEqual(sectionID, 'scrollNav-2-1', 'should not be scrollNav-2-1');
  });

  module('subSections', {
    // This will run before each test in this module.
    setup: function() {
      this.elems = $('#qunit-fixture');
      this.scrollNav = this.elems.scrollNav({
        sections: 'h2',
        subSections: 'h3',
        showHeadline: true
      });
      this.sections = this.scrollNav.children();
    }, teardown: function() {
      $('.scroll-nav').remove();
    }
  });

  test('correct total number of sub-sections in first section', function() {
    expect(3);
    var $subSections = this.sections.eq(0).find('.scroll-nav__sub-section');

    strictEqual($subSections.length, 2, 'should be two');
    notStrictEqual($subSections.length, 1, 'should not be one');
    notStrictEqual($subSections.length, 0, 'should not be zero');
  });

  module('nav', {
    // This will run before each test in this module.
    setup: function() {
      this.elems = $('#qunit-fixture');
      this.scrollNav = this.elems.scrollNav();
      this.sections = this.scrollNav.children();
      this.nav = this.scrollNav.siblings('.scroll-nav');
    }, teardown: function() {
      $('.scroll-nav').remove();
    }
  });

  test('nav is inserted', function() {
    expect(1);

    equal(this.nav.length, true, 'nav should exist');
  });

  test('first nav lik has correct text', function() {
    expect(3);
    var headingText = this.sections.eq(0).children().eq(0).text();
    var linkText = this.nav.find('li').eq(0).text();

    strictEqual(linkText, headingText, 'should be Test Heading');
    notStrictEqual(linkText, 'lame test markup', 'should not be lame test');
    notStrictEqual(linkText, 'Another Test Heading', 'should not be Another Test Heading');
  });

  test('second nav lik has correct text', function() {
    expect(3);
    var headingText = this.sections.eq(1).children().eq(0).text();
    var linkText = this.nav.find('li').eq(1).text();

    strictEqual(linkText, headingText, 'should be Test Heading');
    notStrictEqual(linkText, 'lame test markup', 'should not be lame test');
    notStrictEqual(linkText, 'Test Heading', 'should not be Test Heading');
  });

  test('first nav lik has correct url', function() {
    expect(3);
    var sectionID = this.sections.eq(0).attr('id');
    var linkURL = this.nav.find('li').eq(0).find('a').attr('href');

    strictEqual(linkURL, '#' + sectionID, 'should be #scrollNav-1');
    notStrictEqual(linkURL, 'lame test markup', 'should not be #scrollNav-0');
    notStrictEqual(linkURL, 'Another Test Heading', 'should not be #scrollNav-2');
  });

  test('second nav lik has correct url', function() {
    expect(3);
    var sectionID = this.sections.eq(1).attr('id');
    var linkURL = this.nav.find('li').eq(1).find('a').attr('href');

    strictEqual(linkURL, '#' + sectionID, 'should be #scrollNav-1');
    notStrictEqual(linkURL, 'lame test markup', 'should not be #scrollNav-1');
    notStrictEqual(linkURL, 'Another Test Heading', 'should not be #scrollNav-3');
  });

}(jQuery));
<|endoftext|>


<commit_before>/*
 * vanilla
 * https://github.com/dschnare/vanilla
 *
 * Copyright (c) 2013 Darren Schnare
 * Licensed under the MIT license.
 */

'use strict';

var path = require('path');
var fs = require('fs');
var Hogan = require('beefcake.js');

module.exports = function(grunt) {
  var parseHtmlFiles, parseHtmlFile, parseMeta,
    parseIncludes, parseScriptsAndStylesheets,
    crawlDependencies, concatDependencies, prefixPathExt,
    getBlocks, parseLayout, getBlockExtensions, mixin, parsePartials;

  grunt.registerMultiTask('vanilla', 'Simple HTML templating plugin that will also crawl JS and CSS dependencies.', function() {
    var options = this.options({
      baseDest: 'web',
      mode: 'debug',
      jsDest: 'js',
      jsMinify: function (content, callback) {
        require('yuicompressor').compress(content, {
          charset: 'utf8',
          type: 'js'
        }, callback);
      },
      cssDest: 'css',
      cssMinify: function (content, callback) {
        require('yuicompressor').compress(content, {
          charset: 'utf8',
          type: 'css'
        }, callback);
      }
    });

    options.baseDest = path.resolve(options.baseDest);
    options.jsDest = path.resolve(options.baseDest, options.jsDest || '');
    options.cssDset = path.resolve(options.baseDest, options.cssDset || '');
    options.jsMode = options.jsMode || options.mode;
    options.cssMode = options.cssMode || options.mode;

    // Iterate over all specified file groups.
    this.files.forEach(function(f) {
      var files = f.src.filter(function(filePath) {
        // Warn on and remove invalid source files (if nonull was set).
        if (!grunt.file.exists(filePath)) {
          grunt.log.warn('Source file "' + filePath + '" not found.');
          return false;
        } else {
          return true;
        }
      });

      parseHtmlFiles(files.filter(function (filePath) {
        return path.extname(filePath) === '.html';
      }), options).pages.forEach(function (page) {
        var dest, content;

        dest = path.resolve(options.baseDest, f.dest);
        page.meta.url = '/' + path.relative(options.baseDest, dest).replace(/\\/g, '/');
        content = Hogan.compile(page.content).render(page.meta, page.partials || {});

        // Destination is a file.
        if (path.extname(dest)) {
          grunt.file.write(dest, content);
        // Destination is a directory.
        } else {
          grunt.file.write(path.join(dest, page.filePath), content);
        }
      });
    });
  });

  mixin = function (a) {
    var k, args, b;

    args = Array.prototype.slice.call(arguments, 1);
    while (args.length) {
      b = args.pop();
      for (k in b) {
        a[k] = b[k];
      }
    }

    return a;
  };

  parseHtmlFiles = function (files, options) {
    var pages = [];

    files.forEach(function (filePath) {
      var content, result, page, meta;

      content = grunt.file.read(filePath, {encoding:'utf8'});
      result = parseHtmlFile(content, filePath, options);
      content = result.content;
      meta = mixin({}, result.meta || {}, { pages: pages });

      Object.keys(meta).forEach(function (key) {
        if (typeof meta[key] === 'function') meta[key] = meta[key].bind(meta);
      });

      pages.push({
        filePath: filePath,
        content: content,
        meta: meta,
        partials: result.partials
      });
    });

    return {pages:pages};
  };

  parseHtmlFile = function (content, filePath, options, blockExtensions, meta, partials) {
    var result, blocks;

    result = parseMeta(content);
    content = result.content;
    meta = mixin(result.meta, meta || {});

    result = parseIncludes(content, filePath, options);
    content = result.content;

    result = parseScriptsAndStylesheets(content, filePath, options);
    content = result.content;

    blocks = getBlocks(content);

    if (Array.isArray(blockExtensions)) {
      blockExtensions.forEach(function (extension, i) {
        var block;

        if (!extension) {
          return;
        }

        block = blocks[extension.blockName];

        if (block) {
          switch (extension.type) {
            case 'append':
              block.content = block.content + extension.content;
              break;
            case 'prepend':
              block.content = extension.content + block.content;
              break;
            default:
              block.content = extension.content;
          }

          blockExtensions[i] = undefined;
        }
      });
    }

    Object.keys(blocks).reverse().forEach(function (blockName) {
      var block = blocks[blockName];
      content = content.substring(0, block.begin) + block.content + content.substring(block.end);
    });

    result = parsePartials(content);
    content = result.content;
    partials = mixin(result.partials, partials || {});

    result = parseLayout(content, filePath, options, blockExtensions, meta, partials);
    content = result.content;

    return {content:content, meta:meta, partials:partials};
  };

  parseMeta = function (content) {
    // Parse directives of the form:
    // <v:meta>JavaScript literal</v:meta>
    // <v:meta-somename>Multiline text</v:meta-somename>
    var meta, reg, match, o, k;

    meta = {};

    /*jshint evil:true */
    reg = /<v:meta>([^<]+)<\/v:meta>/g;
    while (match = reg.exec(content)) {
      try {
        o = new Function('return ' + match[1].trim()).call();
      } catch (o) {
        continue;
      }

      for (k in o) {
        meta[k] = o[k];
      }
    }
    content = content.replace(/(?:\r\n|\n)?\s*<v:meta>[^<]*<\/v:meta>\s*/g, '');
    /*jshint evil:false */

    reg = /<v:meta name=("|')([^\1]+?)\1\s*>([\s\S]*?)<\/v:meta>/g;
    while (match = reg.exec(content)) {
      meta[match[2]] = match[3].trim();
    }
    content = content.replace(/(?:\r\n|\n)?\s*<v:meta name=("|')[^\1]+?\1\s*>[\s\S]*?<\/v:meta>\s*/g, '');

    return {meta:meta, content:content};
  };

  parseIncludes = function (content, includingFilepath, options) {
    // Recursively parse each included HTML file. Look for
    // directives of the form:
    // <v:include file="" />
    var reg, match, dirname, includedFilepath, includes, result, includeMeta;

    includes = [];
    dirname = path.resolve(path.dirname(includingFilepath));

    reg = /<v:include (?:file|src)=("|')([^\1]+?)\1\s*(?:\/>|>([^<]*?)<\/v:include>|>)/g;
    while (match = reg.exec(content)) {
      includedFilepath = path.resolve(dirname, match[2]);

      if (match[3]) {
        includeMeta = (new Function('return ' + match[3].trim()).call());
      }

      if (path.extname(includedFilepath) === '.html') {
        result = parseHtmlFile(grunt.file.read(includedFilepath), includedFilepath, options, null, includeMeta);
        includes.push({
          content: Hogan.render(result.content, result.meta, result.partials),
          begin: match.index,
          end: reg.lastIndex
        });
      } else {
        includes.push({
          content: Hogan.render(grunt.file.read(includedFilepath), includeMeta || {}),
          begin: match.index,
          end: reg.lastIndex
        });
      }
    }

    includes.reverse().forEach(function (incl) {
      content = content.substring(0, incl.begin) + incl.content + content.substring(incl.end);
    });

    return {content:content};
  };

  parseScriptsAndStylesheets = function (content, includedFilepath, options) {
    // Concatenate and possibly minify scripts and stylesheets. Look for
    // directives of the form:
    // <v:script file="" />
    // <v:stylesheet file="" />
    var reg, match, dirname, resources;

    resources = [];
    dirname = path.resolve(path.dirname(includedFilepath));

    reg = /<v:(stylesheet|script) (?:file|src)=("|')([^\2]+?)\2\s*\/?>/g;
    while (match = reg.exec(content)) {
      resources.push({
        type: match[1],
        filePath: path.resolve(dirname, match[3]),
        relFilePath: match[3],
        begin: match.index,
        end: reg.lastIndex,
        dest: match[6] ? match[6] : ''
      });
    }

    resources.reverse().forEach(function (resource) {
      var deps;

      switch (resource.type) {
        // Interpret: /* @@import "file.css" */
        case 'stylesheet':
          deps = crawlDependencies(/\/\*\s*@@import ("|')([^\1]+?)\1\s*\*\//g, resource.filePath, resource.relFilePath, '.css');
          content = content.substring(0, resource.begin) +  concatDependencies(deps, options, '<link rel="stylesheet" href="{url}" />') + content.substring(resource.end);
          break;
        // Interpret: // #import "file.js"
        case 'script':
          deps = crawlDependencies(/\/\/\s*#import ("|')([^\1]+?)\1/g, resource.filePath, resource.relFilePath, '.js');
          content = content.substring(0, resource.begin) +  concatDependencies(deps, options, '<script type="text/javascript" src="{url}"></script>') + content.substring(resource.end);
          break;
      }
    });

    return {content:content};
  };
  crawlDependencies = function (pattern, filePath, relFilePath, ext, visited) {
    var deps, content, match, importedFilename, baseDir;

    deps = [];

    if (!Array.isArray(visited)) {
      visited = [];
    }

    filePath = (function (filePath) {
      var fileExt = path.extname(filePath);

      if (!fileExt && fs.statSync(filePath).isDirectory()) {
        filePath = path.join(filePath, 'index' + ext);
        relFilePath = path.join(relFilePath, 'index' + ext);
      } else if (fileExt !== ext) {
        filePath = path.join(path.dirname(filePath), path.basename(filePath, importedFileExt) + ext);
        relFilePath = path.join(path.dirname(relFilePath), path.basename(relFilePath, importedFileExt) + ext);
      }

      return filePath;
    }(filePath));

    if (visited.indexOf(filePath) >= 0) return deps;

    visited.push(filePath);

    ////// Perform the crawl //////

    baseDir = path.dirname(filePath);
    content = grunt.file.read(filePath);
    pattern = pattern.toString().split('/');
    pattern.pop();
    pattern.shift();
    pattern = new RegExp(pattern.join('/'), 'g');

    while (match = pattern.exec(content)) {
      importedFilename = path.resolve(baseDir, match[2]);
      deps = crawlDependencies(pattern, importedFilename, path.join(path.dirname(relFilePath), match[2]), ext, visited).concat(deps);
    }

    deps.push({
      filePath: filePath,
      relFilePath: relFilePath,
      content: content
    });

    return deps;
  };
  concatDependencies = function (deps, options, tpl) {
    var outputPath, urls, rootDep;

    if (!deps.length) {
      return '';
    }

    urls = [];
    rootDep = deps[deps.length - 1];

    switch (options.jsMode) {
      case 'concat':
        outputPath = prefixPathExt(path.join(options.jsDest, rootDep.relFilePath), '.max');
        grunt.file.write(outputPath, deps.map(function (dep) {
          return dep.content;
        }).join('\n\n'));
        urls.push('/' + path.relative(options.baseDest, outputPath).replace(/\\/g, '/'));
        break;
      case 'compress':
        outputPath = prefixPathExt(path.join(options.jsDest, rootDep.relFilePath), '.min');
        grunt.file.write(outputPath, options.jsMinify(deps.map(function (dep) {
          return dep.content;
        }).join('\n\n')));
        urls.push('/' + path.relative(options.baseDest, outputPath).replace(/\\/g, '/'));
        break;
      default:
        deps.forEach(function (dep) {
          var copyPath = path.join(options.jsDest, dep.relFilePath);
          grunt.file.copy(dep.filePath, copyPath, {encoding:'utf8'});
          urls.push('/' + path.relative(options.baseDest, copyPath).replace(/\\/g, '/'));
        });
    }

    return urls.map(function (url) {
      return tpl.replace('{url}', url);
    }).join('\n');
  };
  prefixPathExt = function (thePath, extPrefix) {
    var ext = path.extname(thePath);
    extPrefix = extPrefix || '';
    return path.join(path.dirname(thePath), path.basename(thePath, ext) + extPrefix + ext);
  };

  getBlocks = function (content) {
    var blocks, reg, match;

    blocks = {};

    reg = /<v:block name=("|')([^\1]+?)\1\s*(?:\/>|>([\s\S]*?)<\/v:block>|>)/g;
    while (match = reg.exec(content)) {
      blocks[match[2]] = {
        begin: match.index,
        end: reg.lastIndex,
        content: (match[3] || '').trim()
      };
    }

    return blocks;
  };

  parsePartials = function (content) {
    var partials, reg, match;

    partials = {};

    reg = /<v:partial name=("|')([^\1]+?)\1\s*(?:\/>|>([\s\S]*?)<\/v:partial>|>)/g;
    while (match = reg.exec(content)) {
      partials[match[2]] = (match[3] || '').trim();
    }

    content = content.replace(/<v:partial name=("|')[^\1]+?\1\s*(?:\/>|>[\s\S]*?<\/v:partial>|>)/g, '');

    return {content:content, partials:partials};
  };

  parseLayout = function (content, filePath, options, blockExtensions, meta, partials) {
    // <v:extend file="" />
    var reg, match, k, extensions, layoutFilePath;

    if (!Array.isArray(blockExtensions)) {
      blockExtensions = [];
    }

    // Only process the first extends directive if one exists.
    reg = /<v:(?:extends|layout) (?:file|src)=("|')([^\1]+?)\1\s*\/?>/;
    if (match = reg.exec(content)) {
      layoutFilePath = path.resolve(path.resolve(path.dirname(filePath)), match[2]);
      extensions = blockExtensions.concat(getBlockExtensions(content));
      content = parseHtmlFile(grunt.file.read(layoutFilePath), layoutFilePath, options, extensions, meta, partials).content;
    }

    return {content:content};
  };

  getBlockExtensions = function (content) {
    // <v:append name="" />
    // <v:prepend name="" />
    // <v:replace name="" />
    var extensions, reg, match;

    extensions = [];

    reg = /<v:(append|prepend|replace) name=("|')([^\2]+?)\2\s*(?:\/>|>([\s\S]*?)<\/v:\1>|>)/g;
    while (match = reg.exec(content)) {
      extensions.push({
        blockName: match[3],
        type:match[1],
        content:(match[4] || '').trim()
      });
    }

    return extensions;
  };
};<commit_msg>Add caching support to included resources<commit_after>/*
 * vanilla
 * https://github.com/dschnare/vanilla
 *
 * Copyright (c) 2013 Darren Schnare
 * Licensed under the MIT license.
 */

'use strict';

var path = require('path');
var fs = require('fs');
var Hogan = require('beefcake.js');

module.exports = function(grunt) {
  var parseHtmlFiles, parseHtmlFile, parseMeta,
    parseIncludes, parseScriptsAndStylesheets,
    crawlDependencies, concatDependencies, prefixPathExt,
    getBlocks, parseLayout, getBlockExtensions, mixin, parsePartials;

  grunt.registerMultiTask('vanilla', 'Simple HTML templating plugin that will also crawl JS and CSS dependencies.', function() {
    var options, cache;

    options = this.options({
      baseDest: 'web',
      mode: 'debug',
      jsDest: 'js',
      jsMinify: function (content, callback) {
        require('yuicompressor').compress(content, {
          charset: 'utf8',
          type: 'js'
        }, callback);
      },
      cssDest: 'css',
      cssMinify: function (content, callback) {
        require('yuicompressor').compress(content, {
          charset: 'utf8',
          type: 'css'
        }, callback);
      }
    });

    options.cache = {
      hash: {},
      exists: function (key) {
        return !!this.hash[key];
      },
      get: function (key) {
        return this.hash[key];
      },
      set: function (key, value) {
        this.hash[key] = value;
      },
      clear: function () {
        this.hash = {};
      }
    };

    options.baseDest = path.resolve(options.baseDest);
    options.jsDest = path.resolve(options.baseDest, options.jsDest || '');
    options.cssDset = path.resolve(options.baseDest, options.cssDset || '');
    options.jsMode = options.jsMode || options.mode;
    options.cssMode = options.cssMode || options.mode;

    // Iterate over all specified file groups.
    this.files.forEach(function(f) {
      var files = f.src.filter(function(filePath) {
        // Warn on and remove invalid source files (if nonull was set).
        if (!grunt.file.exists(filePath)) {
          grunt.log.warn('Source file "' + filePath + '" not found.');
          return false;
        } else {
          return true;
        }
      });

      parseHtmlFiles(files.filter(function (filePath) {
        return path.extname(filePath) === '.html';
      }), options).pages.forEach(function (page) {
        var dest, content;

        dest = path.resolve(options.baseDest, f.dest);
        page.meta.url = '/' + path.relative(options.baseDest, dest).replace(/\\/g, '/');
        content = Hogan.compile(page.content).render(page.meta, page.partials || {});

        // Destination is a file.
        if (path.extname(dest)) {
          grunt.file.write(dest, content);
        // Destination is a directory.
        } else {
          grunt.file.write(path.join(dest, page.filePath), content);
        }
      });
    });
  });

  mixin = function (a) {
    var k, args, b;

    args = Array.prototype.slice.call(arguments, 1);
    while (args.length) {
      b = args.pop();
      for (k in b) {
        a[k] = b[k];
      }
    }

    return a;
  };

  parseHtmlFiles = function (files, options) {
    var pages = [];

    files.forEach(function (filePath) {
      var content, result, page, meta;

      content = grunt.file.read(filePath, {encoding:'utf8'});
      result = parseHtmlFile(content, filePath, options, null, options.meta, options.partials);
      content = result.content;
      meta = mixin({}, result.meta || {}, { pages: pages });

      Object.keys(meta).forEach(function (key) {
        if (typeof meta[key] === 'function') meta[key] = meta[key].bind(meta);
      });

      pages.push({
        filePath: filePath,
        content: content,
        meta: meta,
        partials: result.partials
      });
    });

    return {pages:pages};
  };

  parseHtmlFile = function (content, filePath, options, blockExtensions, meta, partials) {
    var result, blocks;

    result = parseMeta(content);
    content = result.content;
    meta = mixin(result.meta, meta || {});

    result = parseIncludes(content, filePath, options);
    content = result.content;

    result = parseScriptsAndStylesheets(content, filePath, options);
    content = result.content;

    blocks = getBlocks(content);

    if (Array.isArray(blockExtensions)) {
      blockExtensions.forEach(function (extension, i) {
        var block;

        if (!extension) {
          return;
        }

        block = blocks[extension.blockName];

        if (block) {
          switch (extension.type) {
            case 'append':
              block.content = block.content + extension.content;
              break;
            case 'prepend':
              block.content = extension.content + block.content;
              break;
            default:
              block.content = extension.content;
          }

          blockExtensions[i] = undefined;
        }
      });
    }

    Object.keys(blocks).reverse().forEach(function (blockName) {
      var block = blocks[blockName];
      content = content.substring(0, block.begin) + block.content + content.substring(block.end);
    });

    result = parsePartials(content);
    content = result.content;
    partials = mixin(result.partials, partials || {});

    result = parseLayout(content, filePath, options, blockExtensions, meta, partials);
    content = result.content;

    return {content:content, meta:meta, partials:partials};
  };

  parseMeta = function (content) {
    // Parse directives of the form:
    // <v:meta>JavaScript literal</v:meta>
    // <v:meta-somename>Multiline text</v:meta-somename>
    var meta, reg, match, o, k;

    meta = {};

    /*jshint evil:true */
    reg = /<v:meta>([^<]+)<\/v:meta>/g;
    while (match = reg.exec(content)) {
      try {
        o = new Function('return ' + match[1].trim()).call();
      } catch (o) {
        continue;
      }

      for (k in o) {
        meta[k] = o[k];
      }
    }
    content = content.replace(/(?:\r\n|\n)?\s*<v:meta>[^<]*<\/v:meta>\s*/g, '');
    /*jshint evil:false */

    reg = /<v:meta name=("|')([^\1]+?)\1\s*>([\s\S]*?)<\/v:meta>/g;
    while (match = reg.exec(content)) {
      meta[match[2]] = match[3].trim();
    }
    content = content.replace(/(?:\r\n|\n)?\s*<v:meta name=("|')[^\1]+?\1\s*>[\s\S]*?<\/v:meta>\s*/g, '');

    return {meta:meta, content:content};
  };

  parseIncludes = function (content, includingFilepath, options) {
    // Recursively parse each included HTML file. Look for
    // directives of the form:
    // <v:include file="" />
    var reg, match, dirname, includedFilepath, includes, result, includeMeta, interpolatedContent;

    includes = [];
    dirname = path.resolve(path.dirname(includingFilepath));

    reg = /<v:include (?:file|src)=("|')([^\1]+?)\1\s*(?:\/>|>([^<]*?)<\/v:include>|>)/g;
    while (match = reg.exec(content)) {
      includedFilepath = path.resolve(dirname, match[2]);

      if (match[3]) {
        includeMeta = (new Function('return ' + match[3].trim()).call());
      }

      // Only read from cache if there were no meta variables specified
      if (!match[3] && options.cache.exists(includedFilepath)) {
        interpolatedContent = options.cache.get(includedFilepath);
      } else if (path.extname(includedFilepath) === '.html') {
        result = parseHtmlFile(grunt.file.read(includedFilepath), includedFilepath, options, null, includeMeta);
        interpolatedContent = Hogan.render(result.content, result.meta, result.partials);
      } else {
        interpolatedContent = Hogan.render(grunt.file.read(includedFilepath), includeMeta || {});
      }

      // Only cache the interpolated result if there was no meta variables specified.
      if (!match[3]) {
        options.cache.set(includedFilepath, interpolatedContent);
      }

      includes.push({
        content: interpolatedContent,
        begin: match.index,
        end: reg.lastIndex
      });
    }

    includes.reverse().forEach(function (incl) {
      content = content.substring(0, incl.begin) + incl.content + content.substring(incl.end);
    });

    return {content:content};
  };

  parseScriptsAndStylesheets = function (content, includedFilepath, options) {
    // Concatenate and possibly minify scripts and stylesheets. Look for
    // directives of the form:
    // <v:script file="" />
    // <v:stylesheet file="" />
    var reg, match, dirname, resources, concatenatedDeps;

    resources = [];
    dirname = path.resolve(path.dirname(includedFilepath));

    reg = /<v:(stylesheet|script) (?:file|src)=("|')([^\2]+?)\2\s*\/?>/g;
    while (match = reg.exec(content)) {
      resources.push({
        type: match[1],
        filePath: path.resolve(dirname, match[3]),
        relFilePath: match[3],
        begin: match.index,
        end: reg.lastIndex,
        dest: match[6] ? match[6] : ''
      });
    }

    resources.reverse().forEach(function (resource) {
      var deps;

      switch (resource.type) {
        // Interpret: /* @@import "file.css" */
        case 'stylesheet':
          if (options.cache.exists(resource.filePath)) {
            concatenatedDeps = options.cache.get(resource.filePath);
          } else {
            deps = crawlDependencies(/\/\*\s*@@import ("|')([^\1]+?)\1\s*\*\//g, resource.filePath, resource.relFilePath, '.css');
            concatenatedDeps = concatDependencies(deps, options, '<link rel="stylesheet" href="{url}" />');
            options.cache.set(resource.filePath, concatenatedDeps);
          }
          content = content.substring(0, resource.begin) + concatenatedDeps + content.substring(resource.end);
          break;
        // Interpret: // #import "file.js"
        case 'script':
          if (options.cache.exists(resource.filePath)) {
            concatenatedDeps = options.cache.get(resource.filePath);
          } else {
            deps = crawlDependencies(/\/\/\s*#import ("|')([^\1]+?)\1/g, resource.filePath, resource.relFilePath, '.js');
            concatenatedDeps = concatDependencies(deps, options, '<script type="text/javascript" src="{url}"></script>');
            options.cache.set(resource.filePath, concatenatedDeps);
          }
          content = content.substring(0, resource.begin) + concatenatedDeps + content.substring(resource.end);
          break;
      }
    });

    return {content:content};
  };
  crawlDependencies = function (pattern, filePath, relFilePath, ext, visited) {
    var deps, content, match, importedFilename, baseDir;

    deps = [];

    if (!Array.isArray(visited)) {
      visited = [];
    }

    filePath = (function (filePath) {
      var fileExt = path.extname(filePath);

      if (!fileExt && fs.statSync(filePath).isDirectory()) {
        filePath = path.join(filePath, 'index' + ext);
        relFilePath = path.join(relFilePath, 'index' + ext);
      } else if (fileExt !== ext) {
        filePath = path.join(path.dirname(filePath), path.basename(filePath, importedFileExt) + ext);
        relFilePath = path.join(path.dirname(relFilePath), path.basename(relFilePath, importedFileExt) + ext);
      }

      return filePath;
    }(filePath));

    if (visited.indexOf(filePath) >= 0) return deps;

    visited.push(filePath);

    ////// Perform the crawl //////

    baseDir = path.dirname(filePath);
    content = grunt.file.read(filePath);
    pattern = pattern.toString().split('/');
    pattern.pop();
    pattern.shift();
    pattern = new RegExp(pattern.join('/'), 'g');

    while (match = pattern.exec(content)) {
      importedFilename = path.resolve(baseDir, match[2]);
      deps = crawlDependencies(pattern, importedFilename, path.join(path.dirname(relFilePath), match[2]), ext, visited).concat(deps);
    }

    deps.push({
      filePath: filePath,
      relFilePath: relFilePath,
      content: content
    });

    return deps;
  };
  concatDependencies = function (deps, options, tpl) {
    var outputPath, urls, rootDep;

    if (!deps.length) {
      return '';
    }

    urls = [];
    rootDep = deps[deps.length - 1];

    switch (options.jsMode) {
      case 'concat':
        outputPath = prefixPathExt(path.join(options.jsDest, rootDep.relFilePath), '.max');
        grunt.file.write(outputPath, deps.map(function (dep) {
          return dep.content;
        }).join('\n\n'));
        urls.push('/' + path.relative(options.baseDest, outputPath).replace(/\\/g, '/'));
        break;
      case 'compress':
        outputPath = prefixPathExt(path.join(options.jsDest, rootDep.relFilePath), '.min');
        grunt.file.write(outputPath, options.jsMinify(deps.map(function (dep) {
          return dep.content;
        }).join('\n\n')));
        urls.push('/' + path.relative(options.baseDest, outputPath).replace(/\\/g, '/'));
        break;
      default:
        deps.forEach(function (dep) {
          var copyPath = path.join(options.jsDest, dep.relFilePath);
          grunt.file.copy(dep.filePath, copyPath, {encoding:'utf8'});
          urls.push('/' + path.relative(options.baseDest, copyPath).replace(/\\/g, '/'));
        });
    }

    return urls.map(function (url) {
      return tpl.replace('{url}', url);
    }).join('\n');
  };
  prefixPathExt = function (thePath, extPrefix) {
    var ext = path.extname(thePath);
    extPrefix = extPrefix || '';
    return path.join(path.dirname(thePath), path.basename(thePath, ext) + extPrefix + ext);
  };

  getBlocks = function (content) {
    var blocks, reg, match;

    blocks = {};

    reg = /<v:block name=("|')([^\1]+?)\1\s*(?:\/>|>([\s\S]*?)<\/v:block>|>)/g;
    while (match = reg.exec(content)) {
      blocks[match[2]] = {
        begin: match.index,
        end: reg.lastIndex,
        content: (match[3] || '').trim()
      };
    }

    return blocks;
  };

  parsePartials = function (content) {
    var partials, reg, match;

    partials = {};

    reg = /<v:partial name=("|')([^\1]+?)\1\s*(?:\/>|>([\s\S]*?)<\/v:partial>|>)/g;
    while (match = reg.exec(content)) {
      partials[match[2]] = (match[3] || '').trim();
    }

    content = content.replace(/<v:partial name=("|')[^\1]+?\1\s*(?:\/>|>[\s\S]*?<\/v:partial>|>)/g, '');

    return {content:content, partials:partials};
  };

  parseLayout = function (content, filePath, options, blockExtensions, meta, partials) {
    // <v:extend file="" />
    var reg, match, k, extensions, layoutFilePath;

    if (!Array.isArray(blockExtensions)) {
      blockExtensions = [];
    }

    // Only process the first extends directive if one exists.
    reg = /<v:(?:extends|layout) (?:file|src)=("|')([^\1]+?)\1\s*\/?>/;
    if (match = reg.exec(content)) {
      layoutFilePath = path.resolve(path.resolve(path.dirname(filePath)), match[2]);
      extensions = blockExtensions.concat(getBlockExtensions(content));
      content = parseHtmlFile(grunt.file.read(layoutFilePath), layoutFilePath, options, extensions, meta, partials).content;
    }

    return {content:content};
  };

  getBlockExtensions = function (content) {
    // <v:append name="" />
    // <v:prepend name="" />
    // <v:replace name="" />
    var extensions, reg, match;

    extensions = [];

    reg = /<v:(append|prepend|replace) name=("|')([^\2]+?)\2\s*(?:\/>|>([\s\S]*?)<\/v:\1>|>)/g;
    while (match = reg.exec(content)) {
      extensions.push({
        blockName: match[3],
        type:match[1],
        content:(match[4] || '').trim()
      });
    }

    return extensions;
  };
};<|endoftext|>


<commit_before>var util = require('util'),
    assert = require('assert'),
    vows = require('vows'),
    functions = require('../');

var N = 1024; // this is a bit silly

var OurError = function () {};
util.inherits(OurError, Error);

vows.describe('functions').addBatch({
  '`returns` function result': {
    topic: function () {
      return functions.returns(42);
      //
      // This is cause by a bug/feature in vows. When vows is given a function
      // as a topic, it executes it. Since our functions return functions, this
      // behavior results in unexpected results and has to be prevented.
      //
    },
    'should be a function': function (topic) {
      assert.isFunction(topic);
    },
    'should always return given value': function (topic) {
      for (var i = 0; i < N; i++) {
        assert.equal(topic(), 42);
      }
    }
  },
  '`throws` function result': {
    topic: function () {
      return functions.throws(new OurError('Expected error'));
    },
    'should be a function': function (topic) {
      assert.isFunction(topic);
    },
    'should always throw given error': function (topic) {
      for (var i = 0; i < N; i++) {
        assert.throws(topic, OurError);
      }
    }
  },
  '`throwsIfError` function result': {
    'without `exception` parameter': {
      topic: function () {
        return functions.throwsIfError();
      },
      'should be a function': function (topic) {
        assert.isFunction(topic);
      },
      "should always throw first parameter if it's truthy": function (topic) {
        for (var i = 0; i < N; i++) {
          assert.throws(topic.bind(topic, new OurError('Expected error')),
                        OurError);
        }
      },
      'should never throw if first parameter is falsy': function (topic) {
        for (var i = 0; i < N; i++) {
          assert.doesNotThrow(topic.bind(topic, false));
        }
      }
    },
    'with `exception` parameter': {
      topic: function () {
        return functions.throwsIfError(new OurError('Expected error'));
      },
      'should be a function': function (topic) {
        assert.isFunction(topic);
      },
      'should always throw provided error if first parameter is truthy': function (topic) {
        for (var i = 0; i < N; i++) {
          assert.throws(topic.bind(topic, true), OurError);
        }
      },
      'should never throw if first parameter is falsy': function (topic) {
        for (var i = 0; i < N; i++) {
          assert.doesNotThrow(topic.bind(topic, false), Error);
        }
      }
    }
  },
  '`noop` function': {
    topic: function () {
      return functions.noop;
    },
    'should be a function': function (topic) {
      assert.isFunction(topic);
    },
    'when called': {
      'should never return anything': function (result) {
        for (var i = 0; i < N; i++) {
          assert.isUndefined(result());
        }
      }
    }
  },
  '`eq` function': {
    topic: function () {
      return functions.eq;
    },
    'should be a function': function (topic) {
      assert.isFunction(topic);
    },
    'when called': {
      'with equal values': {
        topic: functions.eq(42, 42),
        'should return true': function (result) {
          assert.isTrue(result);
        }
      },
      'with equalish values': {
        topic: functions.eq('42', 42),
        'should return true': function (result) {
          assert.isTrue(result);
        }
      }
    }
  }
}).export(module);

<commit_msg>[test] Test `strictEq`<commit_after>var util = require('util'),
    assert = require('assert'),
    vows = require('vows'),
    functions = require('../');

var N = 1024; // this is a bit silly

var OurError = function () {};
util.inherits(OurError, Error);

vows.describe('functions').addBatch({
  '`returns` function result': {
    topic: function () {
      return functions.returns(42);
      //
      // This is cause by a bug/feature in vows. When vows is given a function
      // as a topic, it executes it. Since our functions return functions, this
      // behavior results in unexpected results and has to be prevented.
      //
    },
    'should be a function': function (topic) {
      assert.isFunction(topic);
    },
    'should always return given value': function (topic) {
      for (var i = 0; i < N; i++) {
        assert.equal(topic(), 42);
      }
    }
  },
  '`throws` function result': {
    topic: function () {
      return functions.throws(new OurError('Expected error'));
    },
    'should be a function': function (topic) {
      assert.isFunction(topic);
    },
    'should always throw given error': function (topic) {
      for (var i = 0; i < N; i++) {
        assert.throws(topic, OurError);
      }
    }
  },
  '`throwsIfError` function result': {
    'without `exception` parameter': {
      topic: function () {
        return functions.throwsIfError();
      },
      'should be a function': function (topic) {
        assert.isFunction(topic);
      },
      "should always throw first parameter if it's truthy": function (topic) {
        for (var i = 0; i < N; i++) {
          assert.throws(topic.bind(topic, new OurError('Expected error')),
                        OurError);
        }
      },
      'should never throw if first parameter is falsy': function (topic) {
        for (var i = 0; i < N; i++) {
          assert.doesNotThrow(topic.bind(topic, false));
        }
      }
    },
    'with `exception` parameter': {
      topic: function () {
        return functions.throwsIfError(new OurError('Expected error'));
      },
      'should be a function': function (topic) {
        assert.isFunction(topic);
      },
      'should always throw provided error if first parameter is truthy': function (topic) {
        for (var i = 0; i < N; i++) {
          assert.throws(topic.bind(topic, true), OurError);
        }
      },
      'should never throw if first parameter is falsy': function (topic) {
        for (var i = 0; i < N; i++) {
          assert.doesNotThrow(topic.bind(topic, false), Error);
        }
      }
    }
  },
  '`noop` function': {
    topic: function () {
      return functions.noop;
    },
    'should be a function': function (topic) {
      assert.isFunction(topic);
    },
    'when called': {
      'should never return anything': function (result) {
        for (var i = 0; i < N; i++) {
          assert.isUndefined(result());
        }
      }
    }
  },
  '`eq` function': {
    topic: function () {
      return functions.eq;
    },
    'should be a function': function (topic) {
      assert.isFunction(topic);
    },
    'when called': {
      'with equal values': {
        topic: functions.eq(42, 42),
        'should return true': function (result) {
          assert.isTrue(result);
        }
      },
      'with equalish values': {
        topic: functions.eq('42', 42),
        'should return true': function (result) {
          assert.isTrue(result);
        }
      }
    }
  },
  '`strictEq` function': {
    topic: function () {
      return functions.strictEq;
    },
    'should be a function': function (topic) {
      assert.isFunction(topic);
    },
    'when called': {
      'with equal values': {
        topic: functions.strictEq(42, 42),
        'should return true': function (result) {
          assert.isTrue(result);
        }
      },
      'with equalish values': {
        topic: functions.strictEq('42', 42),
        'should return false': function (result) {
          assert.isFalse(result);
        }
      }
    }
  }
}).export(module);

<|endoftext|>


<commit_before>// require dependencies
var React = require('react');
var ReactDOM = require('react-dom');

var d3 = require('d3');

var $ = require('jquery');
var _ = require('lodash');

var bootstrap = require('bootstrap');

var DATA_PATH = 'app/data/budget-first-test.tsv';

var color = d3.scale.ordinal()
    .range(["#98abc5", "#8a89a6"]);

function loadBudgetData() {
  var deferred = $.Deferred();

  d3.tsv(DATA_PATH, function(error, cityData) {
    if (error) throw error;

    // keep only the key total
    color.domain(d3.keys(cityData[0]).filter(function(key) {
      return key == "TOTAL";
    }));

    // aggregate by department/ORGANIZATION
    var cityData = d3.nest()
    .key(function(d) { return d.ORGANIZATION;})
    .rollup(function(d) {
     return d3.sum(d, function(g) {return g.TOTAL; });
   })
    .entries(cityData);
    console.log(JSON.stringify(cityData, null, 2));

    // calculate the city's full annual budget
    var cityBudget = 0;
    cityData.forEach(function(d) {
      cityBudget += parseFloat(d.values);
    });

    // calculate relative values
    cityData.forEach(function(d) {
      d.budgets = color.domain().map(function(name) {
        return {name: 'total', amount: +d.values};
      });
      d.budgets.push({name: 'remainder', amount: cityBudget - d.values});
      d.percentage = d.values / cityBudget;
      console.log(JSON.stringify(d, null, 2));
    });

    // resolve the promise and pass the data
    deferred.resolve(cityData, cityBudget);
  });

  return deferred.promise();
}

var App;
loadBudgetData().done(function(cityData, cityBudget) {

  var radius = 74;

  var Chart = React.createClass({
    render: function() {
      return (
        <svg className="pie" width={this.props.width} height={this.props.height}>{this.props.children}</svg>
      );
    }
  });

  var Sector = React.createClass({
    render: function() {
      var arc = d3.svg.arc()
        .outerRadius(radius)
        .innerRadius(0);

      return (
        <g className="arc">
          <path d={arc(this.props.data)}></path>
        </g>
      );
    }
  });

  var DataSeries = React.createClass({
    render: function() {
      var pie = d3.layout.pie();
      var amounts = _.map(this.props.data.budgets, function(budget) {
        return budget.amount;
      });
      var bars = _.map(pie(amounts), function(point, i) {
        return (
          <Sector data={point} key={i}/>
        )
      });

      return (
        <g transform="translate(74, 74)">{bars}</g>
        // <g transform="translate(" + radius + ", " + radius + ")">{bars}</g>
      );
    }
  });

  var PieChart = React.createClass({
    getDefaultProps: function() {
      return {
        width: radius * 2,
        height: radius * 2
      };
    },
    render: function() {
      return (
        <Chart width={this.props.width} height={this.props.height}>
          <DataSeries data={this.props.data} width={this.props.width} height={this.props.height} />
        </Chart>
      );
    }
  });

  var DepartmentView = React.createClass({
    getInitialState: function() {
        return {
            cityData: cityData
        };
    },
    eachDepartement: function(departmentData, i) {
      return (
        <PieChart key={i} index={i} data={departmentData}></PieChart>
      );
    },
    render: function() {
      return (

          {this.state.cityData.map(this.eachDepartement)}

      );
    }
  });

  ReactDOM.render(<DepartmentView />, document.getElementById('react-container'));

});
<commit_msg>fix radius in DataSeries<commit_after>// require dependencies
var React = require('react');
var ReactDOM = require('react-dom');

var d3 = require('d3');

var $ = require('jquery');
var _ = require('lodash');

var bootstrap = require('bootstrap');

var DATA_PATH = 'app/data/budget-first-test.tsv';

var color = d3.scale.ordinal()
    .range(["#98abc5", "#8a89a6"]);

function loadBudgetData() {
  var deferred = $.Deferred();

  d3.tsv(DATA_PATH, function(error, cityData) {
    if (error) throw error;

    // keep only the key total
    color.domain(d3.keys(cityData[0]).filter(function(key) {
      return key == "TOTAL";
    }));

    // aggregate by department/ORGANIZATION
    var cityData = d3.nest()
    .key(function(d) { return d.ORGANIZATION;})
    .rollup(function(d) {
     return d3.sum(d, function(g) {return g.TOTAL; });
   })
    .entries(cityData);
    console.log(JSON.stringify(cityData, null, 2));

    // calculate the city's full annual budget
    var cityBudget = 0;
    cityData.forEach(function(d) {
      cityBudget += parseFloat(d.values);
    });

    // calculate relative values
    cityData.forEach(function(d) {
      d.budgets = color.domain().map(function(name) {
        return {name: 'total', amount: +d.values};
      });
      d.budgets.push({name: 'remainder', amount: cityBudget - d.values});
      d.percentage = d.values / cityBudget;
      console.log(JSON.stringify(d, null, 2));
    });

    // resolve the promise and pass the data
    deferred.resolve(cityData, cityBudget);
  });

  return deferred.promise();
}

var App;
loadBudgetData().done(function(cityData, cityBudget) {

  var radius = 74;

  var Chart = React.createClass({
    render: function() {
      return (
        <svg className="pie" width={this.props.width} height={this.props.height}>{this.props.children}</svg>
      );
    }
  });

  var Sector = React.createClass({
    render: function() {
      var arc = d3.svg.arc()
        .outerRadius(radius)
        .innerRadius(0);

      return (
        <g className="arc">
          <path d={arc(this.props.data)}></path>
        </g>
      );
    }
  });

  var DataSeries = React.createClass({
    render: function() {
      var pie = d3.layout.pie();
      var amounts = _.map(this.props.data.budgets, function(budget) {
        return budget.amount;
      });
      var bars = _.map(pie(amounts), function(point, i) {
        return (
          <Sector data={point} key={i}/>
        )
      });

      var transform = 'translate(' + radius + ', ' + radius + ')';
      return (
        <g transform={transform}>{bars}</g>
      );
    }
  });

  var PieChart = React.createClass({
    getDefaultProps: function() {
      return {
        width: radius * 2,
        height: radius * 2
      };
    },
    render: function() {
      return (
        <Chart width={this.props.width} height={this.props.height}>
          <DataSeries data={this.props.data} width={this.props.width} height={this.props.height} />
        </Chart>
      );
    }
  });

  var DepartmentView = React.createClass({
    getInitialState: function() {
        return {
            cityData: cityData
        };
    },
    eachDepartement: function(departmentData, i) {
      return (
        <PieChart key={i} index={i} data={departmentData}></PieChart>
      );
    },
    render: function() {
      return (

          {this.state.cityData.map(this.eachDepartement)}

      );
    }
  });

  ReactDOM.render(<DepartmentView />, document.getElementById('react-container'));

});
<|endoftext|>


<commit_before>import React from 'react'
import { Button } from 'reactstrap'
import fileSystemApi from '../../filesystem-api/filesystem-api'
import styles from './DropArea.css'

const DropArea = (props) => {
  // somehow electron needs both event handlers to be able to prevent default...
  const handleDragOver = e => {
    e.preventDefault()
  }

  const handleDrop = e => {
    e.preventDefault()
    const dataDir = e.dataTransfer.files[0].path
    props.actions.changeDataDir(dataDir)
    props.actions.fetchAllProducts()
    props.actions.fetchAllFAOs()
    props.actions.fetchAllNutrients()
  }

  const handleChooseDir = () => {
    fileSystemApi.chooseDataDir()
      .then(dataDir => {
        props.actions.setDataDir(dataDir)
        props.actions.fetchAllProducts()
        props.actions.fetchAllFAOs()
        props.actions.fetchAllNutrients()
      })
  }

  return (

 handleDragOver(e)}
      onDrop={(e) => handleDrop(e)}>

Nothing here yet...

Choose data folder or drop it here!

      <Button
        onClick={() => handleChooseDir()}
        outline
        color='success' >
        Choose data dir
      </Button>

  )
}

export default DropArea
<commit_msg>Fix drop functionality of DropArea<commit_after>import React, { PropTypes } from 'react'
import { Button } from 'reactstrap'
import fileSystemApi from '../../filesystem-api/filesystem-api'
import styles from './DropArea.css'

const DropArea = props => {
  const {
    setDataDir,
    fetchAllProducts,
    fetchAllFAOs,
    fetchAllNutrients
  } = props.actions

  // somehow electron needs both event handlers, handleDragOver and handleDrop
  // to be able to prevent default...
  const handleDragOver = event => {
    event.preventDefault()
  }

  const handleDrop = event => {
    event.preventDefault()
    const dataDir = event.dataTransfer.files[0].path
    setDataDir(dataDir)
    fetchAllProducts()
    fetchAllFAOs()
    fetchAllNutrients()
  }

  const handleChooseDir = () => {
    fileSystemApi.chooseDataDir()
      .then(dataDir => {
        setDataDir(dataDir)
        fetchAllProducts()
        fetchAllFAOs()
        fetchAllNutrients()
      })
  }

  return (

Nothing here yet...

Choose data folder or drop it here!

      <Button
        onClick={handleChooseDir}
        outline
        color='success' >
        Choose data dir
      </Button>

  )
}

DropArea.propTypes = {
  actions: PropTypes.object.isRequired
}

export default DropArea
<|endoftext|>