<commit_before>/**
 * @flow
 */

let JsonFile = require('@exponent/json-file');

import 'instapromise';

import targz from 'tar.gz';
import download from 'download';
import existsAsync from 'exists-async';
import fs from 'fs';
import mkdirp from 'mkdirp';
import path from 'path';
import spawnAsync from '@exponent/spawn-async';
import joi from 'joi';

import * as Analytics from './Analytics';
import Api from './Api';
import ErrorCode from './ErrorCode';
import Logger from './Logger';
import NotificationCode from './NotificationCode';
import * as User from './User';
import * as UrlUtils from './UrlUtils';
import UserSettings from './UserSettings';
import XDLError from './XDLError';
import * as ProjectSettings from './ProjectSettings';

export function packageJsonForRoot(root: string) {
  return new JsonFile(path.join(root, 'package.json'));
}

export function expJsonForRoot(root: string) {
  return new JsonFile(path.join(root, 'exp.json'), {json5: true});
}

export async function expConfigForRootAsync(root: string) {
  let pkg, exp;
  try {
    pkg = await packageJsonForRoot(root).readAsync();
    exp = await expJsonForRoot(root).readAsync();
  } catch (e) {
    // exp or pkg missing
  }

  if (!exp && pkg) {
    exp = pkg.exp;
  }

  return exp;
}

export async function determineEntryPointAsync(root: string) {
  let exp = await expConfigForRootAsync(root);
  let pkgJson = packageJsonForRoot(root);
  let pkg = await pkgJson.readAsync();
  let { main } = pkg;

  // NOTE(brentvatne): why do we have entryPoint and main?
  let entryPoint = main || 'index.js';
  if (exp && exp.entryPoint) {
    entryPoint = exp.entryPoint;
  }
  return entryPoint;
}

function _starterAppCacheDirectory() {
  let dotExponentHomeDirectory = UserSettings.dotExponentHomeDirectory();
  let dir = path.join(dotExponentHomeDirectory, 'starter-app-cache');
  mkdirp.sync(dir);
  return dir;
}

async function _downloadStarterAppAsync(name) {
  let versions = await Api.versionsAsync();
  let starterAppVersion = versions.starterApps[name].version;
  let filename = `${name}-${starterAppVersion}.tar.gz`;
  let starterAppPath = path.join(_starterAppCacheDirectory(), filename);

  if (await existsAsync(starterAppPath)) {
    return starterAppPath;
  }

  let url = `https://s3.amazonaws.com/exp-starter-apps/${filename}`;
  await new download().get(url).dest(_starterAppCacheDirectory()).promise.run();
  return starterAppPath;
}

async function _extract(archive, dir) {
  try {
    await spawnAsync('tar', ['-xvf', archive, '-C', dir], {
      stdio: 'inherit',
      cwd: __dirname,
    });
  } catch (e) {
    await targz().extract(archive, dir);
  }
}

export async function createNewExpAsync(selectedDir: string, extraPackageJsonFields: any, opts: any) {
  // Validate
  let schema = joi.object().keys({
    name: joi.string().required(),
  });

  try {
    await joi.promise.validate(opts, schema);
  } catch (e) {
    throw new XDLError(ErrorCode.INVALID_OPTIONS, e.toString());
  }

  let name = opts.name;
  let root = path.join(selectedDir, name);

  Analytics.logEvent('New Project', {
    selectedDir,
    name,
  });

  let fileExists = true;
  try {
    // If file doesn't exist it will throw an error.
    // Don't want to continue unless there is nothing there.
    fs.statSync(root);
  } catch (e) {
    fileExists = false;
  }

  if (fileExists) {
    throw new XDLError(ErrorCode.DIRECTORY_ALREADY_EXISTS, `That directory already exists. Please choose a different parent directory or project name. (${root})`);
  }

  // Download files
  await mkdirp.promise(root);

  Logger.notifications.info({code: NotificationCode.PROGRESS}, 'Downloading project files...');
  let starterAppPath = await _downloadStarterAppAsync('default');

  // Extract files
  Logger.notifications.info({code: NotificationCode.PROGRESS}, 'Extracting project files...');
  await _extract(starterAppPath, root);

  // Update files
  Logger.notifications.info({code: NotificationCode.PROGRESS}, 'Customizing project...');

  let author = await UserSettings.getAsync('email', null);
  let packageJsonFile = new JsonFile(path.join(root, 'package.json'));
  let packageJson = await packageJsonFile.readAsync();
  packageJson = Object.assign(packageJson, extraPackageJsonFields);

  let data = Object.assign(packageJson, {
    name,
    version: '0.0.0',
    description: "Hello Exponent!",
    author,
  });

  await packageJsonFile.writeAsync(data);

  // Custom code for replacing __NAME__ in main.js
  let mainJs = await fs.readFile.promise(path.join(root, 'main.js'), 'utf8');
  let customMainJs = mainJs.replace(/__NAME__/g, data.name);
  await fs.writeFile.promise(path.join(root, 'main.js'), customMainJs, 'utf8');

  // Update exp.json
  let expJson = await fs.readFile.promise(path.join(root, 'exp.json'), 'utf8');
  let customExpJson = expJson.replace(/\"My New Project\"/, `"${data.name}"`).replace(/\"my-new-project\"/, `"${data.name}"`);
  await fs.writeFile.promise(path.join(root, 'exp.json'), customExpJson, 'utf8');

  return root;
}

export async function saveRecentExpRootAsync(root: string) {
  root = path.resolve(root);

  // Write the recent Exps JSON file
  let recentExpsJsonFile = UserSettings.recentExpsJsonFile();
  let recentExps = await recentExpsJsonFile.readAsync({cantReadFileDefault: []});
  // Filter out copies of this so we don't get dupes in this list
  recentExps = recentExps.filter(function(x) {
    return x !== root;
  });
  recentExps.unshift(root);
  return await recentExpsJsonFile.writeAsync(recentExps.slice(0, 100));
}

function getHomeDir(): string {
  return process.env[(process.platform === 'win32') ? 'USERPROFILE' : 'HOME'] || '';
}

function makePathReadable(pth) {
  let homedir = getHomeDir();
  if (pth.substr(0, homedir.length) === homedir) {
    return `~${pth.substr(homedir.length)}`;
  } else {
    return pth;
  }
}

export async function expInfoAsync(root: string) {
  let pkgJson = packageJsonForRoot(root);

  let name, description, icon;
  try {
    let exp = await expJsonForRoot(root).readAsync();
    name = exp.name;
    description = exp.description;
    icon = exp.iconUrl;
  } catch (err) {
    let pkg = await pkgJson.readAsync();
    name = pkg.name;
    description = pkg.description;
    icon = pkg.exp && pkg.exp.iconUrl;
  }

  return {
    readableRoot: makePathReadable(root),
    root,
    name,
    description,
    icon,
  };
}

export async function expInfoSafeAsync(root: string) {
  try {
    return await expInfoAsync(root);
  } catch (e) {
    return null;
  }
}

type PublishInfo = {
  args: {
    username: string,
    localPackageName: string,
    packageVersion: string,
    remoteUsername: string,
    remotePackageName: string,
    remoteFullPackageName: string,
    ngrokUrl: string,
    sdkVersion: string,
  },
  body: any,
};

// TODO: remove / change, no longer publishInfo, this is just used for signing
export async function getPublishInfoAsync(root: string): Promise<PublishInfo> {
  let username = await User.getUsernameAsync();
  if (!username) {
    throw new Error(`Can't get username!`);
  }
  let pkg: any;
  let exp: any;

  try {
    pkg = await packageJsonForRoot(root).readAsync();
    exp = await expJsonForRoot(root).readAsync();
  } catch (e) {
    // exp or pkg missing
  }

  let name;
  let version;
  // Support legacy package.json with exp
  if (!exp && pkg && pkg.exp) {
    exp = pkg.exp;
    name = pkg.name;
    version = pkg.version;
  } else if (exp && pkg) {
    name = exp.slug;
    version = pkg.version || exp.version;
  }

  if (!exp || !exp.sdkVersion) {
    throw new Error(`sdkVersion is missing from exp.json`);
  }

  if (!name) {
    throw new Error(`slug field is missing from exp.json`);
  }

  if (!version) {
    throw new Error(`Can't get version of package.`);
  }

  let remotePackageName = name;
  let remoteUsername = username;
  let remoteFullPackageName = `@${remoteUsername}/${remotePackageName}`;
  let localPackageName = name;
  let packageVersion = version;
  let sdkVersion = exp.sdkVersion;

  let entryPoint = await determineEntryPointAsync(root);
  let ngrokUrl = await UrlUtils.constructPublishUrlAsync(root, entryPoint);
  return {
    args: {
      username,
      localPackageName,
      packageVersion,
      remoteUsername,
      remotePackageName,
      remoteFullPackageName,
      ngrokUrl,
      sdkVersion,
    },
    body: pkg,
  };
}

export async function recentValidExpsAsync() {
  let recentExpsJsonFile = UserSettings.recentExpsJsonFile();
  let recentExps = await recentExpsJsonFile.readAsync({cantReadFileDefault: []});

  let results = await Promise.all(recentExps.map(expInfoSafeAsync));
  let filteredResults = results.filter(result => result);
  return filteredResults.slice(0, 5);
}

export async function sendAsync(recipient: string, url_: string) {
  let result = await Api.callMethodAsync('send', [recipient, url_]);
  return result;
}

// TODO: figure out where these functions should live
export async function getProjectRandomnessAsync(projectRoot: string) {
  let ps = await ProjectSettings.readAsync(projectRoot);
  let randomness = ps.urlRandomness;
  if (randomness) {
    return randomness;
  } else {
    return resetProjectRandomnessAsync(projectRoot);
  }
}

export async function resetProjectRandomnessAsync(projectRoot: string) {
  let randomness = UrlUtils.someRandomness();
  ProjectSettings.setAsync(projectRoot, {'urlRandomness': randomness});
  return randomness;
}
<commit_msg>Allows dashes in project names<commit_after>/**
 * @flow
 */

let JsonFile = require('@exponent/json-file');

import 'instapromise';

import targz from 'tar.gz';
import download from 'download';
import existsAsync from 'exists-async';
import fs from 'fs';
import mkdirp from 'mkdirp';
import path from 'path';
import spawnAsync from '@exponent/spawn-async';
import joi from 'joi';

import * as Analytics from './Analytics';
import Api from './Api';
import ErrorCode from './ErrorCode';
import Logger from './Logger';
import NotificationCode from './NotificationCode';
import * as User from './User';
import * as UrlUtils from './UrlUtils';
import UserSettings from './UserSettings';
import XDLError from './XDLError';
import * as ProjectSettings from './ProjectSettings';

export function packageJsonForRoot(root: string) {
  return new JsonFile(path.join(root, 'package.json'));
}

export function expJsonForRoot(root: string) {
  return new JsonFile(path.join(root, 'exp.json'), {json5: true});
}

export async function expConfigForRootAsync(root: string) {
  let pkg, exp;
  try {
    pkg = await packageJsonForRoot(root).readAsync();
    exp = await expJsonForRoot(root).readAsync();
  } catch (e) {
    // exp or pkg missing
  }

  if (!exp && pkg) {
    exp = pkg.exp;
  }

  return exp;
}

export async function determineEntryPointAsync(root: string) {
  let exp = await expConfigForRootAsync(root);
  let pkgJson = packageJsonForRoot(root);
  let pkg = await pkgJson.readAsync();
  let { main } = pkg;

  // NOTE(brentvatne): why do we have entryPoint and main?
  let entryPoint = main || 'index.js';
  if (exp && exp.entryPoint) {
    entryPoint = exp.entryPoint;
  }
  return entryPoint;
}

function _starterAppCacheDirectory() {
  let dotExponentHomeDirectory = UserSettings.dotExponentHomeDirectory();
  let dir = path.join(dotExponentHomeDirectory, 'starter-app-cache');
  mkdirp.sync(dir);
  return dir;
}

async function _downloadStarterAppAsync(name) {
  let versions = await Api.versionsAsync();
  let starterAppVersion = versions.starterApps[name].version;
  let filename = `${name}-${starterAppVersion}.tar.gz`;
  let starterAppPath = path.join(_starterAppCacheDirectory(), filename);

  if (await existsAsync(starterAppPath)) {
    return starterAppPath;
  }

  let url = `https://s3.amazonaws.com/exp-starter-apps/${filename}`;
  await new download().get(url).dest(_starterAppCacheDirectory()).promise.run();
  return starterAppPath;
}

async function _extract(archive, dir) {
  try {
    await spawnAsync('tar', ['-xvf', archive, '-C', dir], {
      stdio: 'inherit',
      cwd: __dirname,
    });
  } catch (e) {
    await targz().extract(archive, dir);
  }
}

export async function createNewExpAsync(selectedDir: string, extraPackageJsonFields: any, opts: any) {
  // Validate
  let schema = joi.object().keys({
    name: joi.string().required(),
  });

  // Should we validate that name is a valid name here?

  try {
    await joi.promise.validate(opts, schema);
  } catch (e) {
    throw new XDLError(ErrorCode.INVALID_OPTIONS, e.toString());
  }

  let name = opts.name;
  let root = path.join(selectedDir, name);

  Analytics.logEvent('New Project', {
    selectedDir,
    name,
  });

  let fileExists = true;
  try {
    // If file doesn't exist it will throw an error.
    // Don't want to continue unless there is nothing there.
    fs.statSync(root);
  } catch (e) {
    fileExists = false;
  }

  if (fileExists) {
    throw new XDLError(ErrorCode.DIRECTORY_ALREADY_EXISTS, `That directory already exists. Please choose a different parent directory or project name. (${root})`);
  }

  // Download files
  await mkdirp.promise(root);

  Logger.notifications.info({code: NotificationCode.PROGRESS}, 'Downloading project files...');
  let starterAppPath = await _downloadStarterAppAsync('default');

  // Extract files
  Logger.notifications.info({code: NotificationCode.PROGRESS}, 'Extracting project files...');
  await _extract(starterAppPath, root);

  // Update files
  Logger.notifications.info({code: NotificationCode.PROGRESS}, 'Customizing project...');

  let author = await UserSettings.getAsync('email', null);
  let packageJsonFile = new JsonFile(path.join(root, 'package.json'));
  let packageJson = await packageJsonFile.readAsync();
  packageJson = Object.assign(packageJson, extraPackageJsonFields);

  let data = Object.assign(packageJson, {
    name,
    version: '0.0.0',
    description: "Hello Exponent!",
    author,
  });

  await packageJsonFile.writeAsync(data);

  // Custom code for replacing __NAME__ in main.js
  let mainJs = await fs.readFile.promise(path.join(root, 'main.js'), 'utf8');
  let customMainJs = mainJs.replace(/__NAME__/g, data.name);
  await fs.writeFile.promise(path.join(root, 'main.js'), customMainJs, 'utf8');

  // Update exp.json
  let expJson = await fs.readFile.promise(path.join(root, 'exp.json'), 'utf8');
  let customExpJson = expJson.replace(/\"My New Project\"/, `"${data.name}"`).replace(/\"my-new-project\"/, `"${data.name}"`);
  await fs.writeFile.promise(path.join(root, 'exp.json'), customExpJson, 'utf8');

  return root;
}

export async function saveRecentExpRootAsync(root: string) {
  root = path.resolve(root);

  // Write the recent Exps JSON file
  let recentExpsJsonFile = UserSettings.recentExpsJsonFile();
  let recentExps = await recentExpsJsonFile.readAsync({cantReadFileDefault: []});
  // Filter out copies of this so we don't get dupes in this list
  recentExps = recentExps.filter(function(x) {
    return x !== root;
  });
  recentExps.unshift(root);
  return await recentExpsJsonFile.writeAsync(recentExps.slice(0, 100));
}

function getHomeDir(): string {
  return process.env[(process.platform === 'win32') ? 'USERPROFILE' : 'HOME'] || '';
}

function makePathReadable(pth) {
  let homedir = getHomeDir();
  if (pth.substr(0, homedir.length) === homedir) {
    return `~${pth.substr(homedir.length)}`;
  } else {
    return pth;
  }
}

export async function expInfoAsync(root: string) {
  let pkgJson = packageJsonForRoot(root);

  let name, description, icon;
  try {
    let exp = await expJsonForRoot(root).readAsync();
    name = exp.name;
    description = exp.description;
    icon = exp.iconUrl;
  } catch (err) {
    let pkg = await pkgJson.readAsync();
    name = pkg.name;
    description = pkg.description;
    icon = pkg.exp && pkg.exp.iconUrl;
  }

  return {
    readableRoot: makePathReadable(root),
    root,
    name,
    description,
    icon,
  };
}

export async function expInfoSafeAsync(root: string) {
  try {
    return await expInfoAsync(root);
  } catch (e) {
    return null;
  }
}

type PublishInfo = {
  args: {
    username: string,
    localPackageName: string,
    packageVersion: string,
    remoteUsername: string,
    remotePackageName: string,
    remoteFullPackageName: string,
    ngrokUrl: string,
    sdkVersion: string,
  },
  body: any,
};

// TODO: remove / change, no longer publishInfo, this is just used for signing
export async function getPublishInfoAsync(root: string): Promise<PublishInfo> {
  let username = await User.getUsernameAsync();
  if (!username) {
    throw new Error(`Can't get username!`);
  }
  let pkg: any;
  let exp: any;

  try {
    pkg = await packageJsonForRoot(root).readAsync();
    exp = await expJsonForRoot(root).readAsync();
  } catch (e) {
    // exp or pkg missing
  }

  let name;
  let version;
  // Support legacy package.json with exp
  if (!exp && pkg && pkg.exp) {
    exp = pkg.exp;
    name = pkg.name;
    version = pkg.version;
  } else if (exp && pkg) {
    name = exp.slug;
    version = pkg.version || exp.version;
  }

  if (!exp || !exp.sdkVersion) {
    throw new Error(`sdkVersion is missing from exp.json`);
  }

  if (!name) {
    throw new Error(`slug field is missing from exp.json`);
  }

  if (!version) {
    throw new Error(`Can't get version of package.`);
  }

  let remotePackageName = name;
  let remoteUsername = username;
  let remoteFullPackageName = `@${remoteUsername}/${remotePackageName}`;
  let localPackageName = name;
  let packageVersion = version;
  let sdkVersion = exp.sdkVersion;

  let entryPoint = await determineEntryPointAsync(root);
  let ngrokUrl = await UrlUtils.constructPublishUrlAsync(root, entryPoint);
  return {
    args: {
      username,
      localPackageName,
      packageVersion,
      remoteUsername,
      remotePackageName,
      remoteFullPackageName,
      ngrokUrl,
      sdkVersion,
    },
    body: pkg,
  };
}

export async function recentValidExpsAsync() {
  let recentExpsJsonFile = UserSettings.recentExpsJsonFile();
  let recentExps = await recentExpsJsonFile.readAsync({cantReadFileDefault: []});

  let results = await Promise.all(recentExps.map(expInfoSafeAsync));
  let filteredResults = results.filter(result => result);
  return filteredResults.slice(0, 5);
}

export async function sendAsync(recipient: string, url_: string) {
  let result = await Api.callMethodAsync('send', [recipient, url_]);
  return result;
}

// TODO: figure out where these functions should live
export async function getProjectRandomnessAsync(projectRoot: string) {
  let ps = await ProjectSettings.readAsync(projectRoot);
  let randomness = ps.urlRandomness;
  if (randomness) {
    return randomness;
  } else {
    return resetProjectRandomnessAsync(projectRoot);
  }
}

export async function resetProjectRandomnessAsync(projectRoot: string) {
  let randomness = UrlUtils.someRandomness();
  ProjectSettings.setAsync(projectRoot, {'urlRandomness': randomness});
  return randomness;
}
<|endoftext|>


<commit_before>module.exports = function(grunt) {

    if (process.env.ANDROID_HOME === "" || process.env.ANDROID_HOME === undefined)
    {
        grunt.fail.fatal("Set ANDROID_HOME to point to the correct Android SDK location\n");
    }

    if (process.env.GIT_COMMIT === "" || process.env.GIT_COMMIT === undefined)
    {
        grunt.log.error("The GIT_COMMIT is not set. This NativeScript Android Runtime will not be tagged with the git commit it is build from\n");
    }

    //if (process.env.JAVA_HOME === "" || process.env.GIT_COMMIT === undefined)
    //{
    //    grunt.fail.fatal("The JAVA_HOME is not set. Set the JAVA_HOME to JDK7 or JDK8");
    //}

    //if (process.env.JAVA_HOME.indexof("jdk1.7") === -1 && process.env.JAVA_HOME.indexof("jdk1.8") === -1)
    //{
    //    grunt.fail.fatal("The JAVA_HOME is set to unsupported jdk version. Set the JAVA_HOME to JDK7 or JDK8 directory");
    //}

    var pathModule = require("path");

    var outDir = "./dist";
    var rootDir = ".";

    if (process.platform === "win32")
    {
        var destinationPackageJsonFileName = rootDir + "/src/package.json"
        var sourcePackageJsonFileName = rootDir + "/package.json";
        grunt.log.error("This is windows machine. Coping " + sourcePackageJsonFileName + " to " + destinationPackageJsonFileName + " \n");
        //delete package.json dummy file in src dir
        grunt.file.delete(destinationPackageJsonFileName);
        //copy package.json over to the src dir
        grunt.file.copy(sourcePackageJsonFileName, destinationPackageJsonFileName);
    }

    var args = {
        metadataGen: grunt.option("metadataGen") || "../android-metadata-generator/dist/tns-android-metadata-generator-0.0.1.tgz",
        libsDir: grunt.option("libsDir") || "./src/libs",
        devmode: (grunt.option("devmode") == true) || false,
        metadataGenSrc: grunt.option("metadataGenSrc") || "../android-metadata-generator"
    };

    //var generatorDir = rootDir + "/Bindings/Generator";
    //var jarCreationDir = rootDir + "/Bindings/JarCreation";

    var testPackageFile = function(filePath)
    {
        var packageFileTester = /package\.json$/ig;
        return packageFileTester.test(filePath);
    };

    var updatePackageVersion = function(content, srcPath)
    {
        if (!testPackageFile(srcPath))
        {
            return content;
        }

        var contentAsObject = JSON.parse(content);
        contentAsObject.version = localCfg.packageVersion;
        return JSON.stringify(contentAsObject, null, "\t");
    };

    function getPackageVersion(packageFilePath)
    {
        packageContent = grunt.file.readJSON(packageFilePath);
        var buildVersion = process.env.PACKAGE_VERSION;
        if (!buildVersion)
        {
            return packageContent.version;
        }
        return packageContent.version + "-" + buildVersion;
    }

    var localCfg = {
        jniDir: rootDir + "/src/jni",
        runtimeVersionHFile: rootDir + "/src/jni/Version.h",
        packageJsonFilePath: rootDir + "/package.json",
        commitVersion: process.env.GIT_COMMIT || "",
        metadataGenPath: args.metadataGen,
        androidSrcJar: pathModule.join(process.env.ANDROID_HOME, "platforms/android-17/android.jar"),
        localAndroidJar: pathModule.join(rootDir, "src", "libs", "android.jar"),
        runtimeDir: pathModule.join(rootDir, "src"),
        libsDir: pathModule.join(rootDir, "src", "libs"),
        runtimeBinariesDir: pathModule.join(rootDir, "src", "dist"),
        metadataRuntimeJarSrc: pathModule.join(rootDir, "src", "dist", "libs", "nativescript.jar"),
        metadataRuntimeJar: pathModule.join(rootDir, "src", "libs", "nativescript.jar")
    };

    localCfg.packageVersion = getPackageVersion(localCfg.packageJsonFilePath);

    grunt.initConfig({
        pkg: grunt.file.readJSON(rootDir + "/package.json"),
        clean: {
            build: {
                src: [outDir]
            },
            androidLib: {
                src: localCfg.localAndroidJar
            },
            metadataGenRuntimeJar: {
                src: localCfg.metadataRuntimeJar
            }
        },
        mkdir: {
            build: {
                options: {
                    create: [outDir]
                }
            }
        },
        copy: {
            pkgDef: {
                src: [
                    rootDir + "/package.json"
                ],
                dest: outDir + "/",
                options: {
                    process: updatePackageVersion
                }
            },
            templateProject: {
                expand: true,
                dot: true,
                cwd: pathModule.join(rootDir, "build/project-template"),
                src: [
                    "**/*.*"
                ],
                dest: pathModule.join(outDir, "framework") + "/"
            },
            internalFolder: {
                expand: true,
                cwd: pathModule.join(rootDir, "src/assets"),
                src: ["internal/**/*.*"],
                dest: pathModule.join(outDir, "framework/assets") + "/"
            },
            collectRuntime: {
                expand: true,
                cwd: localCfg.runtimeBinariesDir,
                src: [
                    "**/*.*",
                    "**/*"
                ],
                dest: outDir + "/framework/"
            },
            collectLibs: {
                expand: true,
                cwd: localCfg.libsDir,
                src: ["**/*.*"],
                dest: pathModule.join(outDir, "framework", "libs") + "/"
            },
            runtimeJarToLibs: {
                src: [localCfg.metadataRuntimeJarSrc],
                dest: localCfg.metadataRuntimeJar
            },
            updateVersionFile: {
                expand: true,
                flatten: true,
                src: localCfg.runtimeVersionHFile,
                dest: localCfg.jniDir,
                options: {
                    process: function(content, srcPath)
                    {
                        var updatedContent = content;
                        updatedContent = updatedContent.replace(/RUNTIME_VERSION_PLACEHOLDER/ig, localCfg.packageVersion);
                        updatedContent = updatedContent.replace(/RUNTIME_COMMIT_SHA_PLACEHOLDER/ig, localCfg.commitVersion);

                        return updatedContent;
                    }
                }
            },
            updateManifestFile: {
                expand: true,
                flatten: true,
                src: "./src/manifest.mf",
                dest: "./src/",
                options: {
                    process: function(content, srcPath) {
                        var updatedContent = content;
                        updatedContent = updatedContent.replace(/RUNTIME_VERSION_PLACEHOLDER/ig, localCfg.packageVersion);
                        updatedContent = updatedContent.replace(/RUNTIME_COMMIT_SHA_PLACEHOLDER/ig, localCfg.commitVersion);

                        return updatedContent;
                    }
                }
            },
            androidLib: {
                src: localCfg.androidSrcJar,
                dest: localCfg.localAndroidJar
            }
        },
        replace: {
            ndk_configuration: {
                options: {
                    patterns: [
                        {
                            //match: "^APP_OPTIM\\s*?:=\\s*?debug$",
                            match: /APP_OPTIM\s*?\:=\s*?debug/ig,
                            replacement: "APP_OPTIM := release"
                        }
                    ]
                },
                files: [
                    {src: rootDir + "/src/jni/Application.mk", dest: rootDir + "/src/jni/Application.mk"}
                ]
            }
        },
        exec: {
            npmPack: {
                cmd: "npm pack",
                cwd: outDir + "/"
            },
            generateRuntime: {
                cmd: "npm install && grunt --verbose",
                cwd: "./src"
            },
            buildMetadataGenerator: {
                cmd: "npm install && grunt --verbose",
                cwd: args.metadataGenSrc
            },
            installMetadataGenerator: {
                cmd: "npm install " + localCfg.metadataGenPath
            },
            runMetadataGenerator: {
                cmd: pathModule.normalize("\"node_modules/.bin/generate-metadatajs\"") + " " + localCfg.libsDir + " ./dist/framework/assets/metadata"
            },
			runTests: {
				cmd: "npm install && grunt --verbose",
                cwd: "./test-app"
			},
			antCleanBindingGenerator: {
				cmd: "ant clean",
				cwd: "./binding-generator/Generator/"
			},
			antCleanRunTime: {
				cmd: "ant clean",
				cwd: "./src/"
			},
        }
    });

    grunt.loadNpmTasks("grunt-contrib-clean");
    grunt.loadNpmTasks("grunt-contrib-copy");
    grunt.loadNpmTasks("grunt-mkdir");
    grunt.loadNpmTasks("grunt-exec");
    grunt.loadNpmTasks("grunt-replace");

    grunt.registerTask("generateRuntime", [
				"exec:generateRuntime"
            ]);

    grunt.registerTask("generateMetadata", [
                "exec:installMetadataGenerator",
                "copy:androidLib",
                "copy:runtimeJarToLibs",
                "exec:runMetadataGenerator",
                "clean:androidLib",
                "clean:metadataGenRuntimeJar"
            ]);

    grunt.registerTask("buildMetadataGenerator", (function(){
        if (args.devmode) {
            localCfg.metadataGenPath = pathModule.join(args.metadataGenSrc, "dist", "tns-android-metadata-generator-*.tgz");
            grunt.config(["exec", "installMetadataGenerator"], { cmd: "npm install " + localCfg.metadataGenPath } );

            return [
                "exec:buildMetadataGenerator"
            ];
        } else {
            return [];
        }
    })());

	grunt.registerTask("test", [
				"exec:antCleanBindingGenerator",
				"exec:antCleanRunTime",
				"exec:runTests"
            ]);

    grunt.registerTask("default", [
                            "clean:build",
                            "mkdir:build",
                            "copy:templateProject",
                            "copy:internalFolder",
                            "copy:pkgDef",
                            "generateRuntime",
                            "buildMetadataGenerator",
                            "generateMetadata",
                            "copy:collectRuntime",
                            "copy:collectLibs",
                            //"copy:collectBindings",
                            "exec:npmPack"
            ]);

}
<commit_msg>add check for ndk-build and android commands<commit_after>module.exports = function(grunt) {

    if (process.env.ANDROID_HOME === "" || process.env.ANDROID_HOME === undefined)
    {
        grunt.fail.fatal("Set ANDROID_HOME to point to the correct Android SDK location\n");
    }

    if (process.env.GIT_COMMIT === "" || process.env.GIT_COMMIT === undefined)
    {
        grunt.log.error("The GIT_COMMIT is not set. This NativeScript Android Runtime will not be tagged with the git commit it is build from\n");
    }

    grunt.util.spawn({ cmd: "ndk-build --version" },
    	function doneFunction(error, result, code)
    	{
    		if (error)
        {
            grunt.fail.fatal("ndk-build command not found. Set the PATH variable to include the path to Android NDK directory. \n");
        }
    	}
    );

    grunt.util.spawn({ cmd: "android -h" },
    	function doneFunction(error, result, code)
    	{
    		if (error)
        {
            grunt.fail.fatal("android command not found. Set the PATH variable to include the path to Android SDK directory. \n");
        }
    	}
    );

    var pathModule = require("path");

    var outDir = "./dist";
    var rootDir = ".";

    if (process.platform === "win32")
    {
        var destinationPackageJsonFileName = rootDir + "/src/package.json"
        var sourcePackageJsonFileName = rootDir + "/package.json";
        grunt.log.error("This is windows machine. Coping " + sourcePackageJsonFileName + " to " + destinationPackageJsonFileName + " \n");
        //delete package.json dummy file in src dir
        grunt.file.delete(destinationPackageJsonFileName);
        //copy package.json over to the src dir
        grunt.file.copy(sourcePackageJsonFileName, destinationPackageJsonFileName);
    }

    var args = {
        metadataGen: grunt.option("metadataGen") || "../android-metadata-generator/dist/tns-android-metadata-generator-0.0.1.tgz",
        libsDir: grunt.option("libsDir") || "./src/libs",
        devmode: (grunt.option("devmode") == true) || false,
        metadataGenSrc: grunt.option("metadataGenSrc") || "../android-metadata-generator"
    };

    //var generatorDir = rootDir + "/Bindings/Generator";
    //var jarCreationDir = rootDir + "/Bindings/JarCreation";

    var testPackageFile = function(filePath)
    {
        var packageFileTester = /package\.json$/ig;
        return packageFileTester.test(filePath);
    };

    var updatePackageVersion = function(content, srcPath)
    {
        if (!testPackageFile(srcPath))
        {
            return content;
        }

        var contentAsObject = JSON.parse(content);
        contentAsObject.version = localCfg.packageVersion;
        return JSON.stringify(contentAsObject, null, "\t");
    };

    function getPackageVersion(packageFilePath)
    {
        packageContent = grunt.file.readJSON(packageFilePath);
        var buildVersion = process.env.PACKAGE_VERSION;
        if (!buildVersion)
        {
            return packageContent.version;
        }
        return packageContent.version + "-" + buildVersion;
    }

    var localCfg = {
        jniDir: rootDir + "/src/jni",
        runtimeVersionHFile: rootDir + "/src/jni/Version.h",
        packageJsonFilePath: rootDir + "/package.json",
        commitVersion: process.env.GIT_COMMIT || "",
        metadataGenPath: args.metadataGen,
        androidSrcJar: pathModule.join(process.env.ANDROID_HOME, "platforms/android-17/android.jar"),
        localAndroidJar: pathModule.join(rootDir, "src", "libs", "android.jar"),
        runtimeDir: pathModule.join(rootDir, "src"),
        libsDir: pathModule.join(rootDir, "src", "libs"),
        runtimeBinariesDir: pathModule.join(rootDir, "src", "dist"),
        metadataRuntimeJarSrc: pathModule.join(rootDir, "src", "dist", "libs", "nativescript.jar"),
        metadataRuntimeJar: pathModule.join(rootDir, "src", "libs", "nativescript.jar")
    };

    localCfg.packageVersion = getPackageVersion(localCfg.packageJsonFilePath);

    grunt.initConfig({
        pkg: grunt.file.readJSON(rootDir + "/package.json"),
        clean: {
            build: {
                src: [outDir]
            },
            androidLib: {
                src: localCfg.localAndroidJar
            },
            metadataGenRuntimeJar: {
                src: localCfg.metadataRuntimeJar
            }
        },
        mkdir: {
            build: {
                options: {
                    create: [outDir]
                }
            }
        },
        copy: {
            pkgDef: {
                src: [
                    rootDir + "/package.json"
                ],
                dest: outDir + "/",
                options: {
                    process: updatePackageVersion
                }
            },
            templateProject: {
                expand: true,
                dot: true,
                cwd: pathModule.join(rootDir, "build/project-template"),
                src: [
                    "**/*.*"
                ],
                dest: pathModule.join(outDir, "framework") + "/"
            },
            internalFolder: {
                expand: true,
                cwd: pathModule.join(rootDir, "src/assets"),
                src: ["internal/**/*.*"],
                dest: pathModule.join(outDir, "framework/assets") + "/"
            },
            collectRuntime: {
                expand: true,
                cwd: localCfg.runtimeBinariesDir,
                src: [
                    "**/*.*",
                    "**/*"
                ],
                dest: outDir + "/framework/"
            },
            collectLibs: {
                expand: true,
                cwd: localCfg.libsDir,
                src: ["**/*.*"],
                dest: pathModule.join(outDir, "framework", "libs") + "/"
            },
            runtimeJarToLibs: {
                src: [localCfg.metadataRuntimeJarSrc],
                dest: localCfg.metadataRuntimeJar
            },
            updateVersionFile: {
                expand: true,
                flatten: true,
                src: localCfg.runtimeVersionHFile,
                dest: localCfg.jniDir,
                options: {
                    process: function(content, srcPath)
                    {
                        var updatedContent = content;
                        updatedContent = updatedContent.replace(/RUNTIME_VERSION_PLACEHOLDER/ig, localCfg.packageVersion);
                        updatedContent = updatedContent.replace(/RUNTIME_COMMIT_SHA_PLACEHOLDER/ig, localCfg.commitVersion);

                        return updatedContent;
                    }
                }
            },
            updateManifestFile: {
                expand: true,
                flatten: true,
                src: "./src/manifest.mf",
                dest: "./src/",
                options: {
                    process: function(content, srcPath) {
                        var updatedContent = content;
                        updatedContent = updatedContent.replace(/RUNTIME_VERSION_PLACEHOLDER/ig, localCfg.packageVersion);
                        updatedContent = updatedContent.replace(/RUNTIME_COMMIT_SHA_PLACEHOLDER/ig, localCfg.commitVersion);

                        return updatedContent;
                    }
                }
            },
            androidLib: {
                src: localCfg.androidSrcJar,
                dest: localCfg.localAndroidJar
            }
        },
        replace: {
            ndk_configuration: {
                options: {
                    patterns: [
                        {
                            //match: "^APP_OPTIM\\s*?:=\\s*?debug$",
                            match: /APP_OPTIM\s*?\:=\s*?debug/ig,
                            replacement: "APP_OPTIM := release"
                        }
                    ]
                },
                files: [
                    {src: rootDir + "/src/jni/Application.mk", dest: rootDir + "/src/jni/Application.mk"}
                ]
            }
        },
        exec: {
            npmPack: {
                cmd: "npm pack",
                cwd: outDir + "/"
            },
            generateRuntime: {
                cmd: "npm install && grunt --verbose",
                cwd: "./src"
            },
            buildMetadataGenerator: {
                cmd: "npm install && grunt --verbose",
                cwd: args.metadataGenSrc
            },
            installMetadataGenerator: {
                cmd: "npm install " + localCfg.metadataGenPath
            },
            runMetadataGenerator: {
                cmd: pathModule.normalize("\"node_modules/.bin/generate-metadatajs\"") + " " + localCfg.libsDir + " ./dist/framework/assets/metadata"
            },
			runTests: {
				cmd: "npm install && grunt --verbose",
                cwd: "./test-app"
			},
			antCleanBindingGenerator: {
				cmd: "ant clean",
				cwd: "./binding-generator/Generator/"
			},
			antCleanRunTime: {
				cmd: "ant clean",
				cwd: "./src/"
			},
        }
    });

    grunt.loadNpmTasks("grunt-contrib-clean");
    grunt.loadNpmTasks("grunt-contrib-copy");
    grunt.loadNpmTasks("grunt-mkdir");
    grunt.loadNpmTasks("grunt-exec");
    grunt.loadNpmTasks("grunt-replace");

    grunt.registerTask("generateRuntime", [
				"exec:generateRuntime"
            ]);

    grunt.registerTask("generateMetadata", [
                "exec:installMetadataGenerator",
                "copy:androidLib",
                "copy:runtimeJarToLibs",
                "exec:runMetadataGenerator",
                "clean:androidLib",
                "clean:metadataGenRuntimeJar"
            ]);

    grunt.registerTask("buildMetadataGenerator", (function(){
        if (args.devmode) {
            localCfg.metadataGenPath = pathModule.join(args.metadataGenSrc, "dist", "tns-android-metadata-generator-*.tgz");
            grunt.config(["exec", "installMetadataGenerator"], { cmd: "npm install " + localCfg.metadataGenPath } );

            return [
                "exec:buildMetadataGenerator"
            ];
        } else {
            return [];
        }
    })());

	grunt.registerTask("test", [
				"exec:antCleanBindingGenerator",
				"exec:antCleanRunTime",
				"exec:runTests"
            ]);

    grunt.registerTask("default", [
                            "clean:build",
                            "mkdir:build",
                            "copy:templateProject",
                            "copy:internalFolder",
                            "copy:pkgDef",
                            "generateRuntime",
                            "buildMetadataGenerator",
                            "generateMetadata",
                            "copy:collectRuntime",
                            "copy:collectLibs",
                            //"copy:collectBindings",
                            "exec:npmPack"
            ]);

}
<|endoftext|>


<commit_before>//var testacular = require('testacular');
//var typescript = require('typescript');

/*global module:false*/
module.exports = function (grunt) {
    "use strict";

    // Project configuration.
    grunt.initConfig({
        pkg: grunt.file.readJSON('package.json'),
        banner: '/* THIS IS A BANNER */ \r\n',

        clean: {
            src: ['build']
        },

        typescript: {
            src: {
                src: ['src/**/*.ts'],
                dest: 'build/src',
                options: {
                    module: 'commonjs', //or commonjs
                    target: 'es5', //or es3
                    base_path: 'src',
                    sourcemap: false,
                    declaration: false
                }
            },
            test: {
                src: ['test/**/*.ts'],
                dest: 'build/test',
                options: {
                    module: 'commonjs', //or commonjs
                    target: 'es5', //or es3
                    base_path: 'test',
                    sourcemap: false,
                    declaration: false
                }
            },
        },

        concat: {
            options: {
                banner: '<%= banner %>',
                stripBanners: true
            },
            core: {
                src: [  'build/src/common.js',
                        'build/src/route.js',
                        'build/src/state.js',
                        'build/src/template.js',
                        'build/src/view.js',
                        'build/src/directives/jemView.js'],
                dest: 'build/<%= pkg.name %>.js'
            },
            legacy: {
                src: [ 'build/src/legacy/templateDecorator.js' ],
                dest: 'build/<%= pkg.name %>.legacy.js'
            }
        },

        uglify: {
            options: {
                banner: '<%= banner %>'
            },
            core: {
                src: '<%= concat.core.dest %>',
                dest: 'build/<%= pkg.name %>.min.js'
            },
            legacy: {
                src: '<%= concat.legacy.dest %>',
                dest: 'build/<%= pkg.name %>.legacy.min.js'
            },
        },

        karma: {
            unit: {
                configFile: 'test-grunt-config.js',
                runnerPort: 9999,
                singleRun: true,
                browsers: ['PhantomJS']
            }
        }
    });

    // These plugins provide necessary tasks.
    grunt.loadNpmTasks('grunt-contrib-clean');
    grunt.loadNpmTasks('grunt-contrib-concat');
    grunt.loadNpmTasks('grunt-contrib-uglify');
    grunt.loadNpmTasks('grunt-contrib-jshint');
    grunt.loadNpmTasks('grunt-contrib-watch');
    grunt.loadNpmTasks('grunt-typescript');
    grunt.loadNpmTasks('grunt-karma');

    // Default task.
    grunt.registerTask('default', ['clean', 'typescript', 'concat', 'uglify', 'karma']);
};
    //grunt.registerTask('build', 'concat min');
    //grunt.registerTask('dist', 'build jsdoc');
    //grunt.registerTask('default', 'build lint test');

    // Project configuration.
    //grunt.initConfig({
    //    builddir: 'build',
    //    pkg: '<json:package.json>',
    //    meta: {
    //        banner: '/**\n' + ' * <%= pkg.description %>\n' +
    //          ' * @version v<%= pkg.version %> - ' + '<%= grunt.template.today("yyyy-mm-dd") %>\n' +
    //          ' * @link <%= pkg.homepage %>\n' +
    //          ' * @license MIT License, http://www.opensource.org/licenses/MIT\n' + ' */',
    //        prefix: '(function (window, angular, undefined) {',
    //        suffix: '})(window, window.angular);'
    //    },
    //    concat: {
    //        build: {
    //            src: [
    //              '<banner:meta.banner>',
    //              '<banner:meta.prefix>',
    //              'src/common.js',
    //              'src/templateFactory.js',
    //              'src/urlMatcherFactory.js',
    //              'src/urlRouter.js',
    //              'src/state.js',
    //              'src/viewDirective.js',
    //              'src/compat.js',
    //              '<banner:meta.suffix>'
    //            ],
    //            dest: '<%= builddir %>/<%= pkg.name %>.js'
    //        }
    //    },
    //    min: {
    //        build: {
    //            src: ['<banner:meta.banner>', '<config:concat.build.dest>'],
    //            dest: '<%= builddir %>/<%= pkg.name %>.min.js'
    //        }
    //    },
    //    lint: {
    //        files: ['grunt.js', 'src/*.js', '<%= builddir %>/<%= pkg.name %>.js']
    //    },
    //    jshint: {
    //        options: {
    //            eqnull: true
    //        }
    //    },
    //    watch: {
    //        files: ['src/*.js', 'test/**/*.js'],
    //        tasks: 'build test'
    //    },
    //    typescript: {

    //    }
    //});

    //// Default task.
    //grunt.registerTask('build', 'concat min');
    //grunt.registerTask('dist', 'build jsdoc');
    //grunt.registerTask('default', 'build lint test');

    //grunt.registerTask('test-server', 'Start testacular server', function () {
    //    //Mark the task as async but never call done, so the server stays up
    //    var done = this.async();
    //    testacular.server.start({ configFile: 'test/test-config.js' });
    //});

    //grunt.registerTask('test', 'Run tests (make sure test-server task is run first)', function () {
    //    var done = this.async();
    //    grunt.utils.spawn({
    //        cmd: process.platform === 'win32' ? 'testacular.cmd' : 'testacular',
    //        args: process.env.TRAVIS ? ['start', 'testacular-config.js', '--single-run', '--no-auto-watch', '--reporter=dots', '--browsers=Firefox'] : ['run']
    //    }, function (error, result, code) {
    //        if (error) {
    //            grunt.warn("Make sure the testacular server is online: run `grunt test-server`.\n" +
    //              error.stdout + error.stderr);
    //            //the testacular runner somehow modifies the files if it errors(??).
    //            //this causes grunt's watch task to re-fire itself constantly,
    //            //unless we wait for a sec
    //            setTimeout(done, 1000);
    //        } else {
    //            grunt.log.write(result.stdout);
    //            done();
    //        }
    //    });
    //});

    //grunt.registerTask('jsdoc', 'Generate documentation', function () {
    //    var done = this.async();
    //    grunt.utils.spawn({
    //        cmd: 'node_modules/jsdoc/jsdoc',
    //        args: ['-c', 'jsdoc-conf.json', '-d', grunt.config('builddir') + '/doc', 'src']
    //    }, function (error, result, code) {
    //        if (error) {
    //            grunt.log.write(error.stderr + '\n');
    //            grunt.warn("jsdoc generation failed");
    //        } else {
    //            grunt.log.write(result.stderr + result.stdout);
    //        }
    //        done();
    //    });
    //});
<commit_msg>Cleaned the gruntfile<commit_after>/*global module:false*/
module.exports = function (grunt) {
    "use strict";

    // Project configuration.
    grunt.initConfig({
        pkg: grunt.file.readJSON('package.json'),
        banner: '/* THIS IS A BANNER */ \r\n',

        clean: {
            src: ['build']
        },

        typescript: {
            src: {
                src: ['src/**/*.ts'],
                dest: 'build/src',
                options: {
                    module: 'commonjs', //or commonjs
                    target: 'es5', //or es3
                    base_path: 'src',
                    sourcemap: false,
                    declaration: false
                }
            },
            test: {
                src: ['test/**/*.ts'],
                dest: 'build/test',
                options: {
                    module: 'commonjs', //or commonjs
                    target: 'es5', //or es3
                    base_path: 'test',
                    sourcemap: false,
                    declaration: false
                }
            },
        },

        concat: {
            options: {
                banner: '<%= banner %>',
                stripBanners: true
            },
            core: {
                src: [  'build/src/common.js',
                        'build/src/route.js',
                        'build/src/state.js',
                        'build/src/template.js',
                        'build/src/view.js',
                        'build/src/directives/jemView.js'],
                dest: 'build/<%= pkg.name %>.js'
            },
            legacy: {
                src: [ 'build/src/legacy/templateDecorator.js' ],
                dest: 'build/<%= pkg.name %>.legacy.js'
            }
        },

        uglify: {
            options: {
                banner: '<%= banner %>'
            },
            core: {
                src: '<%= concat.core.dest %>',
                dest: 'build/<%= pkg.name %>.min.js'
            },
            legacy: {
                src: '<%= concat.legacy.dest %>',
                dest: 'build/<%= pkg.name %>.legacy.min.js'
            },
        },

        karma: {
            unit: {
                configFile: 'test-grunt-config.js',
                runnerPort: 9999,
                singleRun: true,
                browsers: ['PhantomJS']
            }
        }
    });

    // These plugins provide necessary tasks.
    grunt.loadNpmTasks('grunt-contrib-clean');
    grunt.loadNpmTasks('grunt-contrib-concat');
    grunt.loadNpmTasks('grunt-contrib-uglify');
    grunt.loadNpmTasks('grunt-contrib-jshint');
    grunt.loadNpmTasks('grunt-contrib-watch');
    grunt.loadNpmTasks('grunt-typescript');
    grunt.loadNpmTasks('grunt-karma');

    // Default task.
    grunt.registerTask('default', ['clean', 'typescript', 'concat', 'uglify', 'karma']);
};<|endoftext|>


<commit_before>import React, {Component, PropTypes} from 'react'
import isEqual from 'lodash.isequal'

import SYMBOLS from './util/symbols'
import MapTypes from './util/types'
import api from './util/api'
import {EVENTS as EVENTS_MAP, separateEvents} from './util/events'

const EVENTS = EVENTS_MAP['Map']

export default class Map extends Component {

  static propTypes = {
    state: React.PropTypes.shape({
      center: MapTypes.coordinates,
      zoom: React.PropTypes.number,
    }),
    width: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
    height: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
  }

  static defaultProps = {
    state: {
      center: [0, 0],
      zoom: 1,
    },
    width: 640,
    height: 480,
  }

  constructor(...args) {
    super(...args)
    this.state = { instance: null, api: null }
  }

  onApiAvaliable() {
    return new Promise((resolve, reject) => {
      const instance = api.getAPI()
      instance.ready(() => {
        this.setState({api: instance}, () => {
          const cb = this.props.onApiAvaliable
          if (typeof cb === 'function') cb(instance)
          resolve()
        })
      })
    })
  }

  mountMap() {
    const {state, events, options} = separateEvents(this.props)
    const {api} = this.state
    const instance = new api.Map(this.mapNode, state, options)
    this.addEvents(instance, events)
    this.setState({instance}, () => {
      const cb = this.props.onMapAvaliable
      if (typeof cb === 'function') cb(instance)
    })
  }

  unmountMap() {
    const {events} = separateEvents(this.props)
    const {instance} = this.state

    if (instance !== null) {
      this.removeEvents(instance, events)
      instance.destroy()
    }
  }

  updateMap(prevProps = {}) {
    const {options: prevOptions, state: prevState, events: prevEvents} = separateEvents(prevProps)
    const {options, state, events} = separateEvents(this.props)
    const {instance} = this.state

    if (!isEqual(state.center, prevState.center)) {
      instance.setCenter(state.center)
    }

    if (!isEqual(state.bounds, prevState.bounds)) {
      instance.setBounds(state.bounds)
    }

    if (state.type !== prevState.type) {
      instance.setType(state.type)
    }

    if (state.zoom !== prevState.zoom) {
      instance.setZoom(state.zoom)
    }

    if (!isEqual(options, prevOptions)) {
      instance.options.set(options)
    }

    if (!isEqual(events, prevEvents)) {
      this.removeEvents(instance, prevEvents)
      this.addEvents(instance, events)
    }
  }

  addEvents(instance, events) {
    Object.keys(events).forEach((key) => {
      if (EVENTS[key] && typeof events[key] === 'function') {
        instance.events.add(EVENTS[key], events[key])
      }
    })
  }

  removeEvents(instance, events) {
    Object.keys(events).forEach((key) => {
      if (EVENTS[key] && typeof events[key] === 'function') {
        instance.events.remove(EVENTS[key], events[key])
      }
    })
  }

  renderGeoObject(child) {
    const {api} = this.state
    const {geoObjects: collection} = this.state.instance

    return React.cloneElement(child, { api, collection })
  }

  renderChildren() {
    const {children} = this.props
    const {instance} = this.state

    if (instance === null) return null

    return React.Children.map(children, (child) => {
      if (child === null) return null
      if (child.type[SYMBOLS.GEO_OBJECT]) return this.renderGeoObject(child)
    })
  }

  componentDidMount() {
    if (api.isAvaliable()) {
      this.onApiAvaliable()
        .then(() => this.mountMap())
    } else {
      api.load(/* TODO: add options support */)
        .then(() => this.onApiAvaliable())
        .then(() => this.mountMap())
    }
  }

  componentDidUpdate(prevProps) {
    this.updateMap(prevProps)
  }

  componentWillUnmount() {
    this.unmountMap()
  }

  render() {
    const {width, height} = this.props

    return (

        {this.renderChildren()}

 this.mapNode = ref}
        />

    )
  }
}
<commit_msg>Add renderControl method<commit_after>import React, {Component, PropTypes} from 'react'
import isEqual from 'lodash.isequal'

import SYMBOLS from './util/symbols'
import MapTypes from './util/types'
import api from './util/api'
import {EVENTS as EVENTS_MAP, separateEvents} from './util/events'

const EVENTS = EVENTS_MAP['Map']

export default class Map extends Component {

  static propTypes = {
    state: React.PropTypes.shape({
      center: MapTypes.coordinates,
      zoom: React.PropTypes.number,
    }),
    width: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
    height: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
  }

  static defaultProps = {
    state: {
      center: [0, 0],
      zoom: 1,
    },
    width: 640,
    height: 480,
  }

  constructor(...args) {
    super(...args)
    this.state = { instance: null, api: null }
  }

  onApiAvaliable() {
    return new Promise((resolve, reject) => {
      const instance = api.getAPI()
      instance.ready(() => {
        this.setState({api: instance}, () => {
          const cb = this.props.onApiAvaliable
          if (typeof cb === 'function') cb(instance)
          resolve()
        })
      })
    })
  }

  mountMap() {
    const {state, events, options} = separateEvents(this.props)
    const {api} = this.state
    const instance = new api.Map(this.mapNode, state, options)
    this.addEvents(instance, events)
    this.setState({instance}, () => {
      const cb = this.props.onMapAvaliable
      if (typeof cb === 'function') cb(instance)
    })
  }

  unmountMap() {
    const {events} = separateEvents(this.props)
    const {instance} = this.state

    if (instance !== null) {
      this.removeEvents(instance, events)
      instance.destroy()
    }
  }

  updateMap(prevProps = {}) {
    const {options: prevOptions, state: prevState, events: prevEvents} = separateEvents(prevProps)
    const {options, state, events} = separateEvents(this.props)
    const {instance} = this.state

    if (!isEqual(state.center, prevState.center)) {
      instance.setCenter(state.center)
    }

    if (!isEqual(state.bounds, prevState.bounds)) {
      instance.setBounds(state.bounds)
    }

    if (state.type !== prevState.type) {
      instance.setType(state.type)
    }

    if (state.zoom !== prevState.zoom) {
      instance.setZoom(state.zoom)
    }

    if (!isEqual(options, prevOptions)) {
      instance.options.set(options)
    }

    if (!isEqual(events, prevEvents)) {
      this.removeEvents(instance, prevEvents)
      this.addEvents(instance, events)
    }
  }

  addEvents(instance, events) {
    Object.keys(events).forEach((key) => {
      if (EVENTS[key] && typeof events[key] === 'function') {
        instance.events.add(EVENTS[key], events[key])
      }
    })
  }

  removeEvents(instance, events) {
    Object.keys(events).forEach((key) => {
      if (EVENTS[key] && typeof events[key] === 'function') {
        instance.events.remove(EVENTS[key], events[key])
      }
    })
  }

  renderGeoObject(child) {
    const {api} = this.state
    const {geoObjects: collection} = this.state.instance

    return React.cloneElement(child, { api, collection })
  }

  renderControl(child) {
    const {api} = this.state
    const {controls: collection} = this.state.instance

    return React.cloneElement(child, { api, collection })
  }

  renderChildren() {
    const {children} = this.props
    const {instance} = this.state

    if (instance === null) return null

    return React.Children.map(children, (child) => {
      if (child === null) return null
      if (child.type[SYMBOLS.GEO_OBJECT]) return this.renderGeoObject(child)
      if (child.type[SYMBOLS.CONTROL]) return this.renderControl(child)
    })
  }

  componentDidMount() {
    if (api.isAvaliable()) {
      this.onApiAvaliable()
        .then(() => this.mountMap())
    } else {
      api.load(/* TODO: add options support */)
        .then(() => this.onApiAvaliable())
        .then(() => this.mountMap())
    }
  }

  componentDidUpdate(prevProps) {
    this.updateMap(prevProps)
  }

  componentWillUnmount() {
    this.unmountMap()
  }

  render() {
    const {width, height} = this.props

    return (

        {this.renderChildren()}

 this.mapNode = ref}
        />

    )
  }
}
<|endoftext|>


<commit_before>'use strict';
module.exports = prepareGrunt;

/**
 * Sets up grunt for functioning with the specific add ins/modules and
 * configuration we provide in this gruntfile.js.
 * @param {grunt} grunt
 */
function prepareGrunt(grunt) {
    var allFiles = [
        'package.json',
        'Gruntfile.js',
        'lib/**/*.js',
        'spec/**/*',
        '.jshintrc',
        'spec/**/.jshintrc',
        'lib/**/.jshintrc'
    ];

    var allConfigFiles = [
        'Gruntfile.js'
    ];

    var allLibFiles = [
        'Gruntfile.js',
        'lib/**/*.js',
        'index.js'
    ];

    var allSpecFiles = [
        'spec/**/*-spec.js'
    ];

    var allIgnoreFiles = [
        'node_modules/**/*',
        'spec/**/*-disabled.js'
    ];

    // grunt configuration.
    grunt.initConfig({

        // grunt-contrib-jshint
            jshint: {
                // grunt jshint:source
                // will check the source files only
                source: {
                    src: allLibFiles
                },
                // grunt jshint:spec
                // will check the spec files only
                spec: {
                    src: allSpecFiles,
                    // use test specific .jshintrc for spec folder
                    options: {
                        jshintrc: 'spec/.jshintrc'
                    }
                },
                options: {
                    // default to the root .jshintrc
                    jshintrc: '.jshintrc',

                    // ensure we don't waste our time on files we have
                    // explicitly disabled or that we have brought in from
                    // other libraries.
                    ignores: allIgnoreFiles
                }
            },

        // grunt-contrib-watch
            watch: {
                // use with 'grunt watch' from command line ensure we reload
                // our watches when ANY of these files change.
                files: allConfigFiles,
                options: {
                    reload: true
                },
                jshint: {
                    files: allFiles,
                    tasks: ['jshint'],
                    options:{
                        interrupt: true,
                        atBegin: true
                    }
                },
                // grunt watch:mochaTest
                mochaTest: {
                    files: allFiles,
                    tasks: ['coverage'],
                    options:{
                        interrupt: true,
                        atBegin: true
                    }
                }
            },

        // grunt-mocha-test
            mochaTest: {
                // run all tests
                test: {
                    options: {
                        reporter: 'spec',
                        require: 'spec/helper'
                    },
                    src: allSpecFiles
                },
                min: {
                    options: {
                        reporter: 'spec',
                        require: 'spec/helper'
                    },
                    src: allSpecFiles
                }
            },

            'mocha_istanbul': {
                coverage: {
                    src: 'spec',
                    options: {
                        excludes: [
                            'spec/**/*.js'
                        ],
                        mask: '**/*-spec.js',
                        reportFormats: ['html'],
                        mochaOptions: [
                            '--require',
                            'spec/helper'
                        ]
                    }
                }
            }
        });

    // These plugins provide necessary tasks.
    grunt.loadNpmTasks('grunt-contrib-jshint');
    grunt.loadNpmTasks('grunt-contrib-watch');
    grunt.loadNpmTasks('grunt-mocha-test');
    grunt.loadNpmTasks('grunt-mocha-istanbul');
    grunt.loadNpmTasks('grunt-notify');

    // Whenever the "coverage" task is run, run these tasks
    grunt.registerTask('coverage', ['mocha_istanbul:coverage']);

    // Whenever the "test" task is run, run these tasks
    grunt.registerTask('test', ['jshint', 'mochaTest:test']);

    // By default, lint and run all tests.
    grunt.registerTask('default', ['test']);
}
<commit_msg>switch grunt watch to report min<commit_after>'use strict';
module.exports = prepareGrunt;

/**
 * Sets up grunt for functioning with the specific add ins/modules and
 * configuration we provide in this gruntfile.js.
 * @param {grunt} grunt
 */
function prepareGrunt(grunt) {
    var allFiles = [
        'package.json',
        'Gruntfile.js',
        'lib/**/*.js',
        'spec/**/*',
        '.jshintrc',
        'spec/**/.jshintrc',
        'lib/**/.jshintrc'
    ];

    var allConfigFiles = [
        'Gruntfile.js'
    ];

    var allLibFiles = [
        'Gruntfile.js',
        'lib/**/*.js',
        'index.js'
    ];

    var allSpecFiles = [
        'spec/**/*-spec.js'
    ];

    var allIgnoreFiles = [
        'node_modules/**/*',
        'spec/**/*-disabled.js'
    ];

    // grunt configuration.
    grunt.initConfig({

        // grunt-contrib-jshint
            jshint: {
                // grunt jshint:source
                // will check the source files only
                source: {
                    src: allLibFiles
                },
                // grunt jshint:spec
                // will check the spec files only
                spec: {
                    src: allSpecFiles,
                    // use test specific .jshintrc for spec folder
                    options: {
                        jshintrc: 'spec/.jshintrc'
                    }
                },
                options: {
                    // default to the root .jshintrc
                    jshintrc: '.jshintrc',

                    // ensure we don't waste our time on files we have
                    // explicitly disabled or that we have brought in from
                    // other libraries.
                    ignores: allIgnoreFiles
                }
            },

        // grunt-contrib-watch
            watch: {
                // use with 'grunt watch' from command line ensure we reload
                // our watches when ANY of these files change.
                files: allConfigFiles,
                options: {
                    reload: true
                },
                jshint: {
                    files: allFiles,
                    tasks: ['jshint'],
                    options:{
                        interrupt: true,
                        atBegin: true
                    }
                },
                // grunt watch:mochaTest
                mochaTest: {
                    files: allFiles,
                    tasks: ['coverage'],
                    options:{
                        interrupt: true,
                        atBegin: true
                    }
                }
            },

        // grunt-mocha-test
            mochaTest: {
                // run all tests
                test: {
                    options: {
                        reporter: 'spec',
                        require: 'spec/helper'
                    },
                    src: allSpecFiles
                },
                min: {
                    options: {
                        reporter: 'spec',
                        require: 'spec/helper'
                    },
                    src: allSpecFiles
                }
            },

            'mocha_istanbul': {
                coverage: {
                    src: 'spec',
                    options: {
                        excludes: [
                            'spec/**/*.js'
                        ],
                        mask: '**/*-spec.js',
                        reportFormats: ['html'],
                        mochaOptions: [
                            '--require',
                            'spec/helper',
                            '--reporter',
                            'min'
                        ]
                    }
                }
            }
        });

    // These plugins provide necessary tasks.
    grunt.loadNpmTasks('grunt-contrib-jshint');
    grunt.loadNpmTasks('grunt-contrib-watch');
    grunt.loadNpmTasks('grunt-mocha-test');
    grunt.loadNpmTasks('grunt-mocha-istanbul');
    grunt.loadNpmTasks('grunt-notify');

    // Whenever the "coverage" task is run, run these tasks
    grunt.registerTask('coverage', ['mocha_istanbul:coverage']);

    // Whenever the "test" task is run, run these tasks
    grunt.registerTask('test', ['jshint', 'mochaTest:test']);

    // By default, lint and run all tests.
    grunt.registerTask('default', ['test']);
}
<|endoftext|>


<commit_before>"use strict";

import pcsclite from '@pokusew/pcsclite';
import EventEmitter from 'events';
import Reader from './Reader';
import ACR122Reader from './ACR122Reader';

class NFC extends EventEmitter {

	pcsc = null;
	logger = null;

	constructor(logger) {
		super();

		this.pcsc = pcsclite();

		if (logger) {
			this.logger = logger;
		}
		else {
			this.logger = {
				log: function () {
				},
				debug: function () {
				},
				info: function () {
				},
				warn: function () {
				},
				error: function () {
				},
			};
		}

		this.pcsc.on('reader', (reader) => {

			this.logger.debug('new reader detected', reader.name);

			// create special object for ARC122U reader with commands specific to this reader
			if (reader.name.toLowerCase().indexOf('acr122') !== -1 ||
				reader.name.toLowerCase().indexOf('acr125') !== -1) {

				const device = new ACR122Reader(reader, this.logger);

				this.emit('reader', device);

				return;

			}

			const device = new Reader(reader, this.logger);

			this.emit('reader', device);

		});

		this.pcsc.on('error', (err) => {

			this.logger.error('PCSC error', err.message);

			this.emit('error', err);

		});

	}

	get readers() {
		return this.pcsc.readers;
	}

	close() {
		this.pcsc.close();
	}

}

export default NFC;
<commit_msg>Improve comments in NFC class code that handles Reader class matching<commit_after>"use strict";

import pcsclite from '@pokusew/pcsclite';
import EventEmitter from 'events';
import Reader from './Reader';
import ACR122Reader from './ACR122Reader';

class NFC extends EventEmitter {

	pcsc = null;
	logger = null;

	constructor(logger) {
		super();

		this.pcsc = pcsclite();

		if (logger) {
			this.logger = logger;
		}
		else {
			this.logger = {
				log: function () {
				},
				debug: function () {
				},
				info: function () {
				},
				warn: function () {
				},
				error: function () {
				},
			};
		}

		this.pcsc.on('reader', (reader) => {

			this.logger.debug('new reader detected', reader.name);

			// create special object for ARC122U reader with commands specific to this reader
			if (

				// 'acr122' matches ARC122U
				reader.name.toLowerCase().indexOf('acr122') !== -1

				// 'acr125' matches ACR1252U reader because ACR1252U has some common commands with ARC122U
				//   ACR1252U product page: https://www.acs.com.hk/en/products/342/acr1252u-usb-nfc-reader-iii-nfc-forum-certified-reader/
				//   TODO: in the future, this should be refactored:
				//         see discussion in PR#111 https://github.com/pokusew/nfc-pcsc/pull/111
				|| reader.name.toLowerCase().indexOf('acr125') !== -1

			) {

				const device = new ACR122Reader(reader, this.logger);

				this.emit('reader', device);

				return;

			}

			const device = new Reader(reader, this.logger);

			this.emit('reader', device);

		});

		this.pcsc.on('error', (err) => {

			this.logger.error('PCSC error', err.message);

			this.emit('error', err);

		});

	}

	get readers() {
		return this.pcsc.readers;
	}

	close() {
		this.pcsc.close();
	}

}

export default NFC;
<|endoftext|>


<commit_before>module.exports = function(grunt) {

	// Initialize config
	grunt.initConfig({

		// Load package.json
		pkg: require('./package.json'),

		// Project paths
		project: {
			assets: 'assets',
			styles: '<%= project.assets %>/stylesheets',
			styles_scss: '<%= project.styles %>/scss',
			styles_critical: '<%= project.styles %>/critical',
			scripts: '<%= project.assets %>/javascript',
			scripts_main: '<%= project.scripts %>/main',
			scripts_modules: '<%= project.scripts %>/modules',
			scripts_plugins: '<%= project.scripts %>/lib/plugins',
			scripts_polyfills: '<%= project.scripts %>/lib/polyfills',
			scripts_utils: '<%= project.scripts %>/lib/utils',
			design_assets: 'design/assets',
		},

		// Project banner
		tag: {
			banner_basic: '/**\n' +
					' * <%= pkg.description %> — v<%= pkg.version %> — <%= grunt.template.today("yyyy-mm-dd") %>\n' +
					' * <%= pkg.url %>\n' +
					' * Copyright (c) <%= grunt.template.today("yyyy") %> <%= pkg.copyright %>\n' +
					' * License <%= pkg.license %>\n' +
					' */\n',
			banner_extended: '/**\n' +
					' * <%= pkg.description %>\n' +
					' *\n' +
					' * @authors   <%= pkg.author %>\n' +
					' * @link      <%= pkg.url %>\n' +
					' * @version   <%= pkg.version %>\n' +
					' * @generated <%= grunt.template.today("yyyy-mm-dd:hh:mm") %>\n' +
					' * @copyright (c) <%= grunt.template.today("yyyy") %> <%= pkg.copyright %>\n' +
					' * @license   <%= pkg.license %>\n' +
					' */\n'
		},

		// JS files and order
		jsfiles: {
			head: [
				'<%= project.scripts_plugins %>/enhance/enhance.js',
				'<%= project.scripts_plugins %>/modernizr/modernizr.dev.js',
				// '<%= project.scripts_plugins %>/modernizr/modernizr.min.js',    // ALWAYS use custom build modernizr in production!
				'<%= project.scripts_main %>/head.scripts.js',
			],
			main: {
				plugins: [
					// '<%= project.scripts_plugins %>/debounce/debounce.js',
					'<%= project.scripts_plugins %>/domready/domready.js',      // ALWAYS out-comment. It is used in the `main` and `mobile` scripts files!
					'<%= project.scripts_plugins %>/fontfaceobserver/fontfaceobserver.js', // Out-comment when loading fonts
					// '<%= project.scripts_plugins %>/gumshoe/gumshoe.js',
					'<%= project.scripts_plugins %>/imageset/imageset.js',      // Out-comment when using imageset's lazyload
					'<%= project.scripts_plugins %>/imageset/ls.bgset.js',      // Out-comment when using imageset's lazyload + background images with a width descriptor, similar to how `srcset` works (e.g. for Safari 10.x)
					'<%= project.scripts_plugins %>/imageset/ls.parent-fit.js', // Out-comment when using imageset's lazyload + background images; when `bgset` plugin (background images) is used in combination with data-sizes="auto" and `background-size: cover|contain` (e.g. for Edge, IE11 and older, iOS 9, etc.)
					'<%= project.scripts_plugins %>/transitionend/transitionend.js', // Out-comment when using alerts or navmain module!
					'<%= project.scripts_plugins %>/smooth-scroll/smooth-scroll.js',
					// '<%= project.scripts_plugins %>/smooth-scroll/smooth-scroll.polyfills.js',
					// '<%= project.scripts_plugins %>/smooth-scroll/smooth-scroll-without-hash.js',
					// '<%= project.scripts_plugins %>/throttle/throttle.js',
				],
				polyfills: [
					'<%= project.scripts_polyfills %>/classlist.js',            // Adds cross-browser full `element.classList` support (IE11)
					'<%= project.scripts_polyfills %>/closest.js',
					// '<%= project.scripts_polyfills %>/promise.js',
					// '<%= project.scripts_polyfills %>/ls.respimg.js',           // Only partial `respimage` polyfill (see: https://github.com/aFarkas/lazysizes/tree/gh-pages/plugins/respimg); out-comment when not conditionally loading `respimage.min.js` in `head.scripts.js`
					// '<%= project.scripts_polyfills %>/svg4everybody.js',        // Adds external spritemaps support to otherwise svg-capable browsers (e.g. IE11)
				],
				utils: [
					// '<%= project.scripts_utils %>/ajax.util.js',
					// '<%= project.scripts_utils %>/cookie.util.js',              // Out-comment when enhance (and thus enhance.cookie) is not used
					// '<%= project.scripts_utils %>/domparents.util.js',
					'<%= project.scripts_utils %>/extend.util.js',              // Out-comment when using alerts module!
					// '<%= project.scripts_utils %>/isvisible.util.js',
				],
				modules: [
					'<%= project.scripts_modules %>/alerts.module.js',
					'<%= project.scripts_modules %>/expand.module.js',
					'<%= project.scripts_modules %>/fontobserver.module.js',
					'<%= project.scripts_modules %>/navmain.module.js',
					'<%= project.scripts_modules %>/popup.module.js',
				// Main
					'<%= project.scripts_main %>/main.scripts.js',
				],
			},
			mobile: {
				plugins: [
					// '<%= project.scripts_plugins %>/debounce/debounce.js',
					'<%= project.scripts_plugins %>/domready/domready.js',      // ALWAYS out-comment! It is used in the `main` and `mobile` scripts files!
					'<%= project.scripts_plugins %>/fontfaceobserver/fontfaceobserver.js', // Out-comment when loading fonts
					'<%= project.scripts_plugins %>/imageset/imageset.js',      // Out-comment when using imageset's lazyload
					'<%= project.scripts_plugins %>/imageset/ls.bgset.js',      // Out-comment when using imageset's lazyload + background images with a width descriptor, similar to how `srcset` works (e.g. for Safari 10.x)
					'<%= project.scripts_plugins %>/imageset/ls.parent-fit.js', // Out-comment when using imageset's lazyload + background images; when `bgset` plugin (background images) is used in combination with data-sizes="auto" and `background-size: cover|contain` (e.g. for Edge, IE11 and older, iOS 9, etc.)
					'<%= project.scripts_plugins %>/transitionend/transitionend.js', // Out-comment when using alerts or navmain module!
					'<%= project.scripts_plugins %>/smooth-scroll/smooth-scroll.js',
					// '<%= project.scripts_plugins %>/smooth-scroll/smooth-scroll-without-hash.js',
					// '<%= project.scripts_plugins %>/throttle/throttle.js',
				],
				polyfills: [
					'<%= project.scripts_polyfills %>/classlist.js',            // Adds cross-browser full `element.classList` support (IE11)
					// '<%= project.scripts_polyfills %>/promise.js',
					// '<%= project.scripts_polyfills %>/ls.respimg.js',           // Only partial `respimage` polyfill (see: https://github.com/aFarkas/lazysizes/tree/gh-pages/plugins/respimg); out-comment when not conditionally loading `respimage.min.js` in `head.scripts.js`
					// '<%= project.scripts_polyfills %>/svg4everybody.js',        // Adds external spritemaps support to otherwise svg-capable browsers (e.g. IE11)
				],
				utils: [
					// '<%= project.scripts_utils %>/ajax.util.js',
					// '<%= project.scripts_utils %>/cookie.util.js',              // Out-comment when enhance (and thus enhance.cookie) is not used
					// '<%= project.scripts_utils %>/domparents.util.js',
					'<%= project.scripts_utils %>/extend.util.js',              // Out-comment when using alerts module!
					// '<%= project.scripts_utils %>/isvisible.util.js',
				],
				modules: [
					'<%= project.scripts_modules %>/alerts.module.js',
					'<%= project.scripts_modules %>/expand.module.js',
					'<%= project.scripts_modules %>/fontobserver.module.js',
					'<%= project.scripts_modules %>/navmain.module.js',
				// Main
					'<%= project.scripts_main %>/mobile.scripts.js',
				],
			},
		},
	});

	// Load per-task config from separate files
	grunt.loadTasks('grunt/config');

	// Register alias tasks from separate files
	grunt.loadTasks('grunt/tasks');

	// Register default task
	grunt.registerTask('default', ['develop']);

};
<commit_msg>Add comment.<commit_after>module.exports = function(grunt) {

	// Initialize config
	grunt.initConfig({

		// Load package.json
		pkg: require('./package.json'),

		// Project paths
		project: {
			assets: 'assets',
			styles: '<%= project.assets %>/stylesheets',
			styles_scss: '<%= project.styles %>/scss',
			styles_critical: '<%= project.styles %>/critical',
			scripts: '<%= project.assets %>/javascript',
			scripts_main: '<%= project.scripts %>/main',
			scripts_modules: '<%= project.scripts %>/modules',
			scripts_plugins: '<%= project.scripts %>/lib/plugins',
			scripts_polyfills: '<%= project.scripts %>/lib/polyfills',
			scripts_utils: '<%= project.scripts %>/lib/utils',
			design_assets: 'design/assets',
		},

		// Project banner
		tag: {
			banner_basic: '/**\n' +
					' * <%= pkg.description %> — v<%= pkg.version %> — <%= grunt.template.today("yyyy-mm-dd") %>\n' +
					' * <%= pkg.url %>\n' +
					' * Copyright (c) <%= grunt.template.today("yyyy") %> <%= pkg.copyright %>\n' +
					' * License <%= pkg.license %>\n' +
					' */\n',
			banner_extended: '/**\n' +
					' * <%= pkg.description %>\n' +
					' *\n' +
					' * @authors   <%= pkg.author %>\n' +
					' * @link      <%= pkg.url %>\n' +
					' * @version   <%= pkg.version %>\n' +
					' * @generated <%= grunt.template.today("yyyy-mm-dd:hh:mm") %>\n' +
					' * @copyright (c) <%= grunt.template.today("yyyy") %> <%= pkg.copyright %>\n' +
					' * @license   <%= pkg.license %>\n' +
					' */\n'
		},

		// JS files and order
		jsfiles: {
			head: [
				'<%= project.scripts_plugins %>/enhance/enhance.js',
				'<%= project.scripts_plugins %>/modernizr/modernizr.dev.js',
				// '<%= project.scripts_plugins %>/modernizr/modernizr.min.js',    // ALWAYS use custom build modernizr in production!
				'<%= project.scripts_main %>/head.scripts.js',
			],
			main: {
				plugins: [
					// '<%= project.scripts_plugins %>/debounce/debounce.js',
					'<%= project.scripts_plugins %>/domready/domready.js',      // ALWAYS out-comment. It is used in the `main` and `mobile` scripts files!
					'<%= project.scripts_plugins %>/fontfaceobserver/fontfaceobserver.js', // Out-comment when loading fonts
					// '<%= project.scripts_plugins %>/gumshoe/gumshoe.js',
					'<%= project.scripts_plugins %>/imageset/imageset.js',      // Out-comment when using imageset's lazyload
					'<%= project.scripts_plugins %>/imageset/ls.bgset.js',      // Out-comment when using imageset's lazyload + background images with a width descriptor, similar to how `srcset` works (e.g. for Safari 10.x)
					'<%= project.scripts_plugins %>/imageset/ls.parent-fit.js', // Out-comment when using imageset's lazyload + background images; when `bgset` plugin (background images) is used in combination with data-sizes="auto" and `background-size: cover|contain` (e.g. for Edge, IE11 and older, iOS 9, etc.)
					'<%= project.scripts_plugins %>/transitionend/transitionend.js', // Out-comment when using alerts or navmain module!
					'<%= project.scripts_plugins %>/smooth-scroll/smooth-scroll.js',
					// '<%= project.scripts_plugins %>/smooth-scroll/smooth-scroll.polyfills.js',
					// '<%= project.scripts_plugins %>/smooth-scroll/smooth-scroll-without-hash.js',
					// '<%= project.scripts_plugins %>/throttle/throttle.js',
				],
				polyfills: [
					'<%= project.scripts_polyfills %>/classlist.js',            // Adds cross-browser full `element.classList` support (IE11)
					'<%= project.scripts_polyfills %>/closest.js',              // Adds cross-browser `element.closest` support (IE11)
					// '<%= project.scripts_polyfills %>/promise.js',
					// '<%= project.scripts_polyfills %>/ls.respimg.js',           // Only partial `respimage` polyfill (see: https://github.com/aFarkas/lazysizes/tree/gh-pages/plugins/respimg); out-comment when not conditionally loading `respimage.min.js` in `head.scripts.js`
					// '<%= project.scripts_polyfills %>/svg4everybody.js',        // Adds external spritemaps support to otherwise svg-capable browsers (e.g. IE11)
				],
				utils: [
					// '<%= project.scripts_utils %>/ajax.util.js',
					// '<%= project.scripts_utils %>/cookie.util.js',              // Out-comment when enhance (and thus enhance.cookie) is not used
					// '<%= project.scripts_utils %>/domparents.util.js',
					'<%= project.scripts_utils %>/extend.util.js',              // Out-comment when using alerts module!
					// '<%= project.scripts_utils %>/isvisible.util.js',
				],
				modules: [
					'<%= project.scripts_modules %>/alerts.module.js',
					'<%= project.scripts_modules %>/expand.module.js',
					'<%= project.scripts_modules %>/fontobserver.module.js',
					'<%= project.scripts_modules %>/navmain.module.js',
					'<%= project.scripts_modules %>/popup.module.js',
				// Main
					'<%= project.scripts_main %>/main.scripts.js',
				],
			},
			mobile: {
				plugins: [
					// '<%= project.scripts_plugins %>/debounce/debounce.js',
					'<%= project.scripts_plugins %>/domready/domready.js',      // ALWAYS out-comment! It is used in the `main` and `mobile` scripts files!
					'<%= project.scripts_plugins %>/fontfaceobserver/fontfaceobserver.js', // Out-comment when loading fonts
					'<%= project.scripts_plugins %>/imageset/imageset.js',      // Out-comment when using imageset's lazyload
					'<%= project.scripts_plugins %>/imageset/ls.bgset.js',      // Out-comment when using imageset's lazyload + background images with a width descriptor, similar to how `srcset` works (e.g. for Safari 10.x)
					'<%= project.scripts_plugins %>/imageset/ls.parent-fit.js', // Out-comment when using imageset's lazyload + background images; when `bgset` plugin (background images) is used in combination with data-sizes="auto" and `background-size: cover|contain` (e.g. for Edge, IE11 and older, iOS 9, etc.)
					'<%= project.scripts_plugins %>/transitionend/transitionend.js', // Out-comment when using alerts or navmain module!
					'<%= project.scripts_plugins %>/smooth-scroll/smooth-scroll.js',
					// '<%= project.scripts_plugins %>/smooth-scroll/smooth-scroll-without-hash.js',
					// '<%= project.scripts_plugins %>/throttle/throttle.js',
				],
				polyfills: [
					'<%= project.scripts_polyfills %>/classlist.js',            // Adds cross-browser full `element.classList` support (IE11)
					// '<%= project.scripts_polyfills %>/promise.js',
					// '<%= project.scripts_polyfills %>/ls.respimg.js',           // Only partial `respimage` polyfill (see: https://github.com/aFarkas/lazysizes/tree/gh-pages/plugins/respimg); out-comment when not conditionally loading `respimage.min.js` in `head.scripts.js`
					// '<%= project.scripts_polyfills %>/svg4everybody.js',        // Adds external spritemaps support to otherwise svg-capable browsers (e.g. IE11)
				],
				utils: [
					// '<%= project.scripts_utils %>/ajax.util.js',
					// '<%= project.scripts_utils %>/cookie.util.js',              // Out-comment when enhance (and thus enhance.cookie) is not used
					// '<%= project.scripts_utils %>/domparents.util.js',
					'<%= project.scripts_utils %>/extend.util.js',              // Out-comment when using alerts module!
					// '<%= project.scripts_utils %>/isvisible.util.js',
				],
				modules: [
					'<%= project.scripts_modules %>/alerts.module.js',
					'<%= project.scripts_modules %>/expand.module.js',
					'<%= project.scripts_modules %>/fontobserver.module.js',
					'<%= project.scripts_modules %>/navmain.module.js',
				// Main
					'<%= project.scripts_main %>/mobile.scripts.js',
				],
			},
		},
	});

	// Load per-task config from separate files
	grunt.loadTasks('grunt/config');

	// Register alias tasks from separate files
	grunt.loadTasks('grunt/tasks');

	// Register default task
	grunt.registerTask('default', ['develop']);

};
<|endoftext|>


<commit_before>import React, {Component, PropTypes} from 'react';

if (typeof window !== 'undefined') {
  require('pdfjs-dist/build/pdf.combined');
}

class Pdf extends Component {
  constructor(props) {
    super(props);
    this.onDocumentComplete = this.onDocumentComplete.bind(this);
    this.onPageComplete = this.onPageComplete.bind(this);
  }

  state = {};

  componentDidMount() {
    this.loadPDFDocument(this.props);
    this.renderPdf();
  }

  componentWillReceiveProps(newProps) {
    const {pdf} = this.state;
    if ((newProps.file && newProps.file !== this.props.file) ||
      (newProps.content && newProps.content !== this.props.content)) {
      this.loadPDFDocument(newProps);
    }
    if (pdf && newProps.page && newProps.page !== this.props.page) {
      this.setState({page: null});
      pdf.getPage(newProps.page).then(this.onPageComplete);
    }
  }

  onDocumentComplete(pdf) {
    this.setState({pdf: pdf});
    const {onDocumentComplete} = this.props;
    if (typeof onDocumentComplete === 'function') {
      onDocumentComplete(pdf.numPages);
    }
    pdf.getPage(this.props.page).then(this.onPageComplete);
  }

  onPageComplete(page) {
    this.setState({page: page});
    this.renderPdf();
    const {onPageComplete} = this.props;
    if (typeof onPageComplete === 'function') {
      onPageComplete(page.pageIndex + 1);
    }
  }

  loadByteArray(byteArray) {
    window.PDFJS.getDocument(byteArray).then(this.onDocumentComplete);
  }

  loadPDFDocument(props) {
    if (!!props.file) {
      if (typeof props.file === 'string') {
        return window.PDFJS.getDocument(props.file)
          .then(this.onDocumentComplete);
      }
      // Is a File object
      const reader = new FileReader();
      reader.onloadend = () =>
        this.loadByteArray(new Uint8Array(reader.result));
      reader.readAsArrayBuffer(props.file);
    } else if (!!props.content) {
      const bytes = window.atob(props.content);
      const byteLength = bytes.length;
      const byteArray = new Uint8Array(new ArrayBuffer(byteLength));
      for (let index = 0; index < byteLength; index++) {
        byteArray[index] = bytes.charCodeAt(index);
      }
      this.loadByteArray(byteArray);
    } else {
      throw new Error('React-PDFjs works with a file(URL) or (base64)content. At least one needs to be provided!');
    }
  }

  renderPdf() {
    const {page} = this.state;
    if (page) {
      let {canvas} = this.refs;
      if (canvas.getDOMNode) { // compatible with react 0.13
        canvas = canvas.getDOMNode();
      }
      const canvasContext = canvas.getContext('2d');
      const {scale} = this.props;
      const viewport = page.getViewport(scale);
      canvas.height = viewport.height;
      canvas.width = viewport.width;
      page.render({canvasContext, viewport});
    }
  }

  render() {
    const {loading} = this.props;
    const {page} = this.state;
    return page ? <canvas ref="canvas"/> : loading ||
Loading PDF...
;
  }
}
Pdf.displayName = 'React-PDFjs';
Pdf.propTypes = {
  content: PropTypes.string,
  file: PropTypes.string,
  loading: PropTypes.any,
  page: PropTypes.number,
  scale: PropTypes.number,
  onDocumentComplete: PropTypes.func,
  onPageComplete: PropTypes.func
};
Pdf.defaultProps = {page: 1, scale: 1.0};

export default Pdf;
<commit_msg>Added checks for changed scale prop in src/Pdf.js<commit_after>import React, {Component, PropTypes} from 'react';

if (typeof window !== 'undefined') {
  require('pdfjs-dist/build/pdf.combined');
}

class Pdf extends Component {
  constructor(props) {
    super(props);
    this.onDocumentComplete = this.onDocumentComplete.bind(this);
    this.onPageComplete = this.onPageComplete.bind(this);
    this.state = {};
  }

  // state = {};

  componentDidMount() {
    this.loadPDFDocument(this.props);
    this.renderPdf();
  }

  componentWillReceiveProps(newProps) {
    const {pdf} = this.state;
    if ((newProps.file && newProps.file !== this.props.file) ||
      (newProps.content && newProps.content !== this.props.content)) {
      this.loadPDFDocument(newProps);
    }
    // if (pdf && newProps.page && newProps.page !== this.props.page) {
    //   this.setState({page: null});
    //   pdf.getPage(newProps.page).then(this.onPageComplete);
    // }
    if (pdf && ((newProps.page && newProps.page !== this.props.page) ||
      (newProps.scale && newProps.scale !== this.props.scale))) {
      this.setState({page: null});
      pdf.getPage(newProps.page).then(this.onPageComplete)
    }
  }

  onDocumentComplete(pdf) {
    this.setState({pdf: pdf});
    const {onDocumentComplete} = this.props;
    if (typeof onDocumentComplete === 'function') {
      onDocumentComplete(pdf.numPages);
    }
    pdf.getPage(this.props.page).then(this.onPageComplete);
  }

  onPageComplete(page) {
    this.setState({page: page});
    this.renderPdf();
    const {onPageComplete} = this.props;
    if (typeof onPageComplete === 'function') {
      onPageComplete(page.pageIndex + 1);
    }
  }

  loadByteArray(byteArray) {
    window.PDFJS.getDocument(byteArray).then(this.onDocumentComplete);
  }

  loadPDFDocument(props) {
    if (!!props.file) {
      if (typeof props.file === 'string') {
        return window.PDFJS.getDocument(props.file)
          .then(this.onDocumentComplete);
      }
      // Is a File object
      const reader = new FileReader();
      reader.onloadend = () =>
        this.loadByteArray(new Uint8Array(reader.result));
      reader.readAsArrayBuffer(props.file);
    } else if (!!props.content) {
      const bytes = window.atob(props.content);
      const byteLength = bytes.length;
      const byteArray = new Uint8Array(new ArrayBuffer(byteLength));
      for (let index = 0; index < byteLength; index++) {
        byteArray[index] = bytes.charCodeAt(index);
      }
      this.loadByteArray(byteArray);
    } else {
      throw new Error('React-PDFjs works with a file(URL) or (base64)content. At least one needs to be provided!');
    }
  }

  renderPdf() {
    const {page} = this.state;
    if (page) {
      let {canvas} = this.refs;
      if (canvas.getDOMNode) { // compatible with react 0.13
        canvas = canvas.getDOMNode();
      }
      const canvasContext = canvas.getContext('2d');
      const {scale} = this.props;
      const viewport = page.getViewport(scale);
      canvas.height = viewport.height;
      canvas.width = viewport.width;
      page.render({canvasContext, viewport});
    }
  }

  render() {
    const {loading} = this.props;
    const {page} = this.state;
    return page ? <canvas ref="canvas"/> : loading ||
Loading PDF...
;
  }
}
Pdf.displayName = 'React-PDFjs';
Pdf.propTypes = {
  content: PropTypes.string,
  file: PropTypes.string,
  loading: PropTypes.any,
  page: PropTypes.number,
  scale: PropTypes.number,
  onDocumentComplete: PropTypes.func,
  onPageComplete: PropTypes.func
};
Pdf.defaultProps = {page: 1, scale: 1.0};

export default Pdf;
<|endoftext|>