<commit_before>require("babel-polyfill");

import config from "config";
import promisify from "promisify-node";
import log from "./log";
import * as dbCache from "./dbCache";
import * as image from "./image";
import aws from "./aws";
import {futureDate} from "./helper";
import metrics, {GENERATION, ORIGINAL, REDIRECT} from './metrics';

const fs = promisify('fs');

const imagePath = (name) => `${config.get('originals_dir')}/${name}`;

const redirectTimeout = config.has('redirect_cache_timeout') ? config.get('redirect_cache_timeout') : 0;

const didTimeout = (error) => {
  return error.message === 'gm() resulted in a timeout.';
};

// Determine whether the image exists on disk
// Returns either true of false
const doesImageExist = async (name) => {
  try {
    await fs.access(imagePath(name), fs.R_OK);
    return true;
  } catch (e) {
    return false;
  }
};

// GM does not always return a nice buffer
// https://github.com/aheckmann/gm/issues/572#issuecomment-293768810
const gmToBuffer = (data) => {
  return new Promise((resolve, reject) => {
    data.stream((err, stdout, stderr) => {
      if (err) {
        reject(err);
        return;
      }
      const chunks = [];
      stdout.on('data', (chunk) => {
        chunks.push(chunk);
      });
      // these are 'once' because they can and do fire multiple times for multiple errors,
      // but this is a promise so you'll have to deal with them one at a time
      stdout.once('end', () => {
        resolve(Buffer.concat(chunks));
      });
      stderr.once('data', (data) => {
        reject(String(data));
      });
    });
  });
};

const isRequestedImageWithinBounds = (params) => {
  // Check if we are allowed to serve an image of this size and optionally redirect
  return (params.width && params.height) &&
    (params.width <= config.get('constraints.max_width') && params.height <= config.get('constraints.max_height'));
};

const calculateNewBounds = async (params) => {
  // Resize the parameters
  // If no params are set then we load them from the actual image before calculating new bounds
  if (!params.width || !params.height) {
    const fileSize = await image.imageSize(imagePath(params.name));
    params.width = fileSize.width;
    params.height = fileSize.height;
  }
  const providedRatio = params.width / params.height;
  if (params.width > config.get('constraints.max_width')) {
    params.width = config.get('constraints.max_width');
    if (params.fit === 'crop') {
      params.height = params.width / providedRatio;
    }
  }
  if (params.height > config.get('constraints.max_height')) {
    params.height = config.get('constraints.max_height');
    if (params.fit === 'crop') {
      params.width = params.height * providedRatio;
    }
  }
  return params;
};

const isHeadRequest = method => (method === 'HEAD');

const redirectImageToWithinBounds = (params, response) => {
  const newLocation = `/${params.name}_${params.width}_${params.height}.${params.type}` +
    `?fit=${params.fit}&blur=${Boolean(params.blur)}&quality=${params.quality}`;
  return response.status(307).set({
    'X-Redirect-Info': 'The requested image size falls outside of the allowed boundaries of this service. We are directing you to the closest available match.', //eslint-disable-line max-len
    'Location': newLocation
  }).end();
};

const redirectToCachedEntity = (cacheUrl, params, response) => {
  // We redirect the user to the new location. We issue a temporary redirect such that the
  // request url stays the representitive url, and because temporary redirects generally
  // give less issues than permanent redirects for the odd chance that the underlying resource
  // does actually change
  const headers = {
    'Location': cacheUrl
  };
  if (redirectTimeout) {
    headers['Cache-Control'] = `max-age=${redirectTimeout}`;
  }

  response.status(307).set(headers).end();
  response.end();
};

const sendFoundHeaders = (params, response) => {
  // Get the image and resize it. We allow any intermediate servers and browsers to use this cached resource as well
  response.status(200).type(params.mime).set({
    'Expires': futureDate(),
    'Cache-Control': 'public'
  });
};

const imageKey = params =>
  `${params.name}_${params.width}x${params.height}.${params.fit}` +
  `.b-${Boolean(params.blur)}.q-${params.quality}.${params.type}`;

export async function magic(params, method, response, stats = undefined, metric = undefined) {
    if (params === null) {
      // Invalid, hence reject
      response.status(400).end();
      if (metric) {
        metric.addTag('status', 400);
        metrics.write(metric);
      }
      return;
    }

    const imageExists = await doesImageExist(params.name);
    if (!imageExists) {
      response.status(404).end();
      if (metric) {
        metric.addTag('status', 404);
        metrics.write(metric);
      }
      return;
    }

    // Image exists
    if (!isRequestedImageWithinBounds(params)) {
      redirectImageToWithinBounds(await calculateNewBounds(params), response);
      if (metric) {
        metric.addTag('status', 307);
        metrics.write(metric);
      }
      return;
    }

    const imageDescription = this.description(params);
    // Image exists and is within bounds.
    // This method is mainly used by browsers to serve retina images
    if (isHeadRequest(method)) {
      response.status(200).end();
      log('debug', `HEAD request for ${imageDescription} can be served`);
      // No metrics here
      return;
    }
    log('debug', `Request for ${imageDescription}`);

    const cacheValue = await dbCache.getFromCache(params);
    if (cacheValue) {
      log('debug', `Cache hit for ${imageDescription}`);
      if (stats) {
        stats.hits.incrementAndGet();
      }
      redirectToCachedEntity(cacheValue, params, response);
      if (metric) {
        metric.addTag('status', 200);
        metric.stop();
        metrics.write(metric);
        metrics.write(metric.copy(REDIRECT));
      }
      return;
    }
    if (stats) {
      stats.misses.incrementAndGet();
    }

    // Image is present but not in the correct setting
    log('debug', `Cache miss for ${imageDescription}`);
    sendFoundHeaders(params, response);

    const clientStartTime = new Date();
    const browserImage = await image.magic(imagePath(params.name), params);
    try {
      const browserBuffer = await gmToBuffer(browserImage);
      log('info', `Creating image took ${new Date() - clientStartTime}ms: ${imageDescription}`);

      const awsBuffer = Buffer.from(browserBuffer);
      response.end(browserBuffer);
      if (metric) {
        metric.addTag('status', 200);
        metric.stop();
        metrics.write(metric);
        metrics.write(metric.copy(GENERATION));
      }
      aws(imageKey(params), params, awsBuffer);
    } catch (err) {
      const status = didTimeout(err) ? 504 : 500;
      response.status(status).end();
      if (metric) {
        metric.addTag('status', status);
        metric.stop();
        metrics.write(metric);
        metrics.write(metric.copy(GENERATION));
      }
      log('error', `Error occurred while creating live image ${imageDescription}: ${err}`);
    }
  }

export async function original(params, method, response, metric = undefined) {
    const startTime = new Date();
    const imageExists = await doesImageExist(params.name);
    if (!imageExists) {
      response.status(404).end();
      if (metric) {
        metric.addTag('status', 404);
        metrics.write(metric);
      }
      return;
    }
    response.status(200).set({
      'Content-Type': params.mime,
      'Cache-Control': 'public',
      Etag: `${params.name}_${params.type}`,
      Expires: futureDate()
    });
    const data = await fs.readFile(imagePath(params.name));
    response.end(data);
    if (metric) {
      metric.addTag('status', 200);
      metric.stop();
      metrics.write(metric);
      metrics.write(metric.copy(ORIGINAL));
    }
    log('info', `Serving original image ${params.name} took ${new Date() - startTime}ms`);
  }
  export async function upload(name, path, cropParameters) {
    const destinationPath = `${config.get('originals_dir')}/${name}`;
    return await image.writeOriented(path, destinationPath, cropParameters);
  }
  export function description(params) {
    return `${params.name}.${params.type} (${params.width}x${params.height}px, fit: ${params.fit}, blur: ${Boolean(params.blur)}), quality: ${Number(params.quality) || 'auto'}`; //eslint-disable-line max-len
  }
  export async function hasAllowableImageSize(path, maxSizeInMegapixel) {
    const imageSize = await image.imageArea(path);
    return imageSize < (maxSizeInMegapixel * 1e6);
  }
<commit_msg>Log the database usage stats on requests<commit_after>require("babel-polyfill");

import config from "config";
import promisify from "promisify-node";
import log from "./log";
import * as dbCache from "./dbCache";
import * as image from "./image";
import aws from "./aws";
import {futureDate} from "./helper";
import metrics, {GENERATION, ORIGINAL, REDIRECT} from './metrics';

const fs = promisify('fs');

const imagePath = (name) => `${config.get('originals_dir')}/${name}`;

const redirectTimeout = config.has('redirect_cache_timeout') ? config.get('redirect_cache_timeout') : 0;

const didTimeout = (error) => {
  return error.message === 'gm() resulted in a timeout.';
};

// Determine whether the image exists on disk
// Returns either true of false
const doesImageExist = async (name) => {
  try {
    await fs.access(imagePath(name), fs.R_OK);
    return true;
  } catch (e) {
    return false;
  }
};

// GM does not always return a nice buffer
// https://github.com/aheckmann/gm/issues/572#issuecomment-293768810
const gmToBuffer = (data) => {
  return new Promise((resolve, reject) => {
    data.stream((err, stdout, stderr) => {
      if (err) {
        reject(err);
        return;
      }
      const chunks = [];
      stdout.on('data', (chunk) => {
        chunks.push(chunk);
      });
      // these are 'once' because they can and do fire multiple times for multiple errors,
      // but this is a promise so you'll have to deal with them one at a time
      stdout.once('end', () => {
        resolve(Buffer.concat(chunks));
      });
      stderr.once('data', (data) => {
        reject(String(data));
      });
    });
  });
};

const isRequestedImageWithinBounds = (params) => {
  // Check if we are allowed to serve an image of this size and optionally redirect
  return (params.width && params.height) &&
    (params.width <= config.get('constraints.max_width') && params.height <= config.get('constraints.max_height'));
};

const calculateNewBounds = async (params) => {
  // Resize the parameters
  // If no params are set then we load them from the actual image before calculating new bounds
  if (!params.width || !params.height) {
    const fileSize = await image.imageSize(imagePath(params.name));
    params.width = fileSize.width;
    params.height = fileSize.height;
  }
  const providedRatio = params.width / params.height;
  if (params.width > config.get('constraints.max_width')) {
    params.width = config.get('constraints.max_width');
    if (params.fit === 'crop') {
      params.height = params.width / providedRatio;
    }
  }
  if (params.height > config.get('constraints.max_height')) {
    params.height = config.get('constraints.max_height');
    if (params.fit === 'crop') {
      params.width = params.height * providedRatio;
    }
  }
  return params;
};

const isHeadRequest = method => (method === 'HEAD');

const redirectImageToWithinBounds = (params, response) => {
  const newLocation = `/${params.name}_${params.width}_${params.height}.${params.type}` +
    `?fit=${params.fit}&blur=${Boolean(params.blur)}&quality=${params.quality}`;
  return response.status(307).set({
    'X-Redirect-Info': 'The requested image size falls outside of the allowed boundaries of this service. We are directing you to the closest available match.', //eslint-disable-line max-len
    'Location': newLocation
  }).end();
};

const redirectToCachedEntity = (cacheUrl, params, response) => {
  // We redirect the user to the new location. We issue a temporary redirect such that the
  // request url stays the representitive url, and because temporary redirects generally
  // give less issues than permanent redirects for the odd chance that the underlying resource
  // does actually change
  const headers = {
    'Location': cacheUrl
  };
  if (redirectTimeout) {
    headers['Cache-Control'] = `max-age=${redirectTimeout}`;
  }

  response.status(307).set(headers).end();
  response.end();
};

const sendFoundHeaders = (params, response) => {
  // Get the image and resize it. We allow any intermediate servers and browsers to use this cached resource as well
  response.status(200).type(params.mime).set({
    'Expires': futureDate(),
    'Cache-Control': 'public'
  });
};

const imageKey = params =>
  `${params.name}_${params.width}x${params.height}.${params.fit}` +
  `.b-${Boolean(params.blur)}.q-${params.quality}.${params.type}`;

export async function magic(params, method, response, stats = undefined, metric = undefined) {
    if (params === null) {
      // Invalid, hence reject
      response.status(400).end();
      if (metric) {
        metric.addTag('status', 400);
        metrics.write(metric);
      }
      return;
    }

    const imageExists = await doesImageExist(params.name);
    if (!imageExists) {
      response.status(404).end();
      if (metric) {
        metric.addTag('status', 404);
        metrics.write(metric);
      }
      return;
    }

    // Image exists
    if (!isRequestedImageWithinBounds(params)) {
      redirectImageToWithinBounds(await calculateNewBounds(params), response);
      if (metric) {
        metric.addTag('status', 307);
        metrics.write(metric);
      }
      return;
    }

    const imageDescription = this.description(params);
    // Image exists and is within bounds.
    // This method is mainly used by browsers to serve retina images
    if (isHeadRequest(method)) {
      response.status(200).end();
      log('debug', `HEAD request for ${imageDescription} can be served`);
      // No metrics here
      return;
    }
    log('debug', `Request for ${imageDescription}`);

    const cacheValue = await dbCache.getFromCache(params);
    if (cacheValue) {
      log('debug', `Cache hit for ${imageDescription}`);
      if (stats) {
        stats.hits.incrementAndGet();
      }
      redirectToCachedEntity(cacheValue, params, response);
      if (metric) {
        metric.addFields(dbCache.stats());
        metric.addTag('status', 200);
        metric.stop();
        metrics.write(metric);
        metrics.write(metric.copy(REDIRECT));
      }
      return;
    }
    if (stats) {
      stats.misses.incrementAndGet();
    }

    // Image is present but not in the correct setting
    log('debug', `Cache miss for ${imageDescription}`);
    sendFoundHeaders(params, response);

    const clientStartTime = new Date();
    const browserImage = await image.magic(imagePath(params.name), params);
    try {
      const browserBuffer = await gmToBuffer(browserImage);
      log('info', `Creating image took ${new Date() - clientStartTime}ms: ${imageDescription}`);

      const awsBuffer = Buffer.from(browserBuffer);
      response.end(browserBuffer);
      if (metric) {
        metric.addFields(dbCache.stats());
        metric.addTag('status', 200);
        metric.stop();
        metrics.write(metric);
        metrics.write(metric.copy(GENERATION));
      }
      aws(imageKey(params), params, awsBuffer);
    } catch (err) {
      const status = didTimeout(err) ? 504 : 500;
      response.status(status).end();
      if (metric) {
        metric.addTag('status', status);
        metric.stop();
        metrics.write(metric);
        metrics.write(metric.copy(GENERATION));
      }
      log('error', `Error occurred while creating live image ${imageDescription}: ${err}`);
    }
  }

export async function original(params, method, response, metric = undefined) {
    const startTime = new Date();
    const imageExists = await doesImageExist(params.name);
    if (!imageExists) {
      response.status(404).end();
      if (metric) {
        metric.addTag('status', 404);
        metrics.write(metric);
      }
      return;
    }
    response.status(200).set({
      'Content-Type': params.mime,
      'Cache-Control': 'public',
      Etag: `${params.name}_${params.type}`,
      Expires: futureDate()
    });
    const data = await fs.readFile(imagePath(params.name));
    response.end(data);
    if (metric) {
      metric.addTag('status', 200);
      metric.stop();
      metrics.write(metric);
      metrics.write(metric.copy(ORIGINAL));
    }
    log('info', `Serving original image ${params.name} took ${new Date() - startTime}ms`);
  }
  export async function upload(name, path, cropParameters) {
    const destinationPath = `${config.get('originals_dir')}/${name}`;
    return await image.writeOriented(path, destinationPath, cropParameters);
  }
  export function description(params) {
    return `${params.name}.${params.type} (${params.width}x${params.height}px, fit: ${params.fit}, blur: ${Boolean(params.blur)}), quality: ${Number(params.quality) || 'auto'}`; //eslint-disable-line max-len
  }
  export async function hasAllowableImageSize(path, maxSizeInMegapixel) {
    const imageSize = await image.imageArea(path);
    return imageSize < (maxSizeInMegapixel * 1e6);
  }
<|endoftext|>


<commit_before>/*

The MIT License (MIT)

Copyright (c) 2013 Rémi Arnaud - Advanced Micro Devices, Inc.

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.*/
//
setViewer6Upload=function(upload){
    var header;
    var index;
    var buttonToReplace;
    upload.callOnClick(function(){
        header = upload.header();
        index=0;
    });
    var url = '/rest3d/upload',
        uploadButton = $('<button/>')
            .addClass('btn')
            .prop('disabled', true)
            .on('click', function (){
                var $this = $(this),
                    data = $this.data();
                $this
                    .off('click')
                    .on('click', function () {
                        $this.remove();
                        data.abort();
                    });
                data.submit();
                buttonToReplace=$(this);
                //.always(function () {
                //     $this.remove();
                // });
        }),
        // show the name of the file nicely
        formatName = function(data,file){
            var i = file.name.lastIndexOf('/');
            return file.name.substring(i+1);
        },
        convertButton = $('<button/>')
            .addClass('btn')
            .prop('disabled', true)
            .on('click', function () {
                var $this = $(this),
                    data = $this.data();
                $this
                    .off('click')
                    .prop('disabled',true)
                // user rest to convert dae into glTF
                var callback = function(data) {
                    $this.prop('disabled',true);
                    if(data.result.output){console.debug(data.result.output);}
                    if(data.result.code){console.debug("Exit code: "+data.result.code);}
                    if (data.error){
                        var span = $('
Error code='+data.error.code+' :: '+data.error.message+'
');
                        data.context.append(span);
                    } else {
                        // ennumerate all resulting files
                        var $conve = upload.header(data.file.name);
                        $.each(data.result.files, function (index, file) {
                            var href = $('');
                            upload.filesArea.append(href);
                            var $download = $("<button>Download</button>").on("click",function(){
                                href[0].click();
                            });

                        var url = decodeURIComponent(file.url);
                        var ext = url.match(/\.[^.]+$/);
                        if(ext==".json"){
                            var $dialog = $("<button>Launch your model</button>").on("click",function(){
                                glTF.load(url, viewer.parse_gltf);
                                window.notif(url);
                            });
                            var $preview = $("<button>Peview</button>").on("click",function(){
                                var gitHtml = '

';
                                gitPanel = $('body').append(gitHtml);
                                $("#dialog").dialog({
                                    autoOpen: true,
                                    width: 800,
                                    height: 600,
                                    open: function (ev, ui) {
                                        $('#myIframe').attr('src', '/viewer/easy-viewer.html?file=/rest3d/upload/'+decodeURIComponent(file.name));
                                    }
                                });
                            });
                                upload.convert($conve,formatName(data,file),$dialog,$download,$preview);
                        }
                        else{
                            upload.download($conve,formatName(data,file),$download);//
                        }
                        });
                    }
                }

                rest3d.convert(data,callback);
            });

    upload.object.on('fileuploadadd', function (e, data) {
        upload.object=$(this);
        data.context = header;
        $.each(data.files, function (index, file) {
            if (!index) {
                var url =  file.url;
                console.debug(file + file.url);
                var $dialog = $("<button>Launch your model</button>").on("click",function(){
                                COLLADA.load(url, viewer.parse_dae);
                                window.notif(url);
                            });
                 var $preview = $("<button>Peview</button>").on("click",function(){
                                var gitHtml = '

';
                                gitPanel = $('body').append(gitHtml);
                                $("#dialog").dialog({
                                    autoOpen: true,
                                    width: 800,
                                    height: 600,
                                    open: function (ev, ui) {
                                        $('#myIframe').attr('src', '/viewer/easy-viewer.html?file=/rest3d/upload/'+decodeURIComponent(file.name));
                                    }//
                                });
                            });
                upload.upload(header,file.name,$preview,$dialog,uploadButton.clone(true).data(data));}//
            else{this.upload(header,file.name);}
        });
    }).on('fileuploadprocessalways', function (e, data) {
            var indexI = data.index,
            file = data.files[indexI],
             node = $(data.context.children('div')[index+1]);
        if (file.preview) {
            node
                .prepend('
')
                .prepend(file.preview);
        }
        if (file.error) {//
          GUI.addTooltip({
                    parent: node.find('button'),
                    content: file.error,
                });//
        }
        if (indexI + 1 === data.files.length) {
            node.find('button')
                .prop('disabled', !!data.files.error);
        }
        index++;//
    }).on('fileuploadprogressall', function (e, data) {
        var progress = parseInt(data.loaded / data.total * 100, 10);
        upload.progress.setValue(progress);
    }).on('fileuploaddone', function (e, data) {
        $.each(data.result.files, function (index, file) {
            file.assetName = data.result.files[index].name;
            var $node = convertButton.clone(true).data({file: file, context: data.context})
                .prop('disabled', !/dae$/i.test(file.url))
            buttonToReplace
                .replaceWith($node)
                .prop("id","nodeClose");//
                GUI.addIcon($node, "ui-icon-check", "", "before");
        });
    }).on('fileuploadfail', function (e, data) {
        if (!data.result) {
            $(data.context.children()[0])
                .append('
')
                .append('error communicating with server')
                .find('button').remove();
        } else
        $.each(data.result.files, function (index, file) {
            var error = $('').text(file.error);
            $(data.context.children()[index])
                .append('
')
                .append(error)
                .find('button').remove();
        });
    });
}
<commit_msg>dashjdhsa:wq<commit_after>/*

The MIT License (MIT)

Copyright (c) 2013 Rémi Arnaud - Advanced Micro Devices, Inc.

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.*/
//
setViewer6Upload=function(upload){
    var header;
    var index;
    var buttonToReplace;
    upload.callOnClick(function(){
        header = upload.header();
        index=0;
    });
    var url = '/rest3d/upload',
        uploadButton = $('<button/>')
            .addClass('btn')
            .prop('disabled', true)
            .on('click', function (){
                var $this = $(this),
                    data = $this.data();
                $this
                    .off('click')
                    .on('click', function () {
                        $this.remove();
                        data.abort();
                    });
                data.submit();
                buttonToReplace=$(this);
                //.always(function () {
                //     $this.remove();
                // });
        }),
        // show the name of the file nicely
        formatName = function(data,file){
            var i = file.name.lastIndexOf('/');
            return file.name.substring(i+1);
        },
        convertButton = $('<button/>')
            .addClass('btn')
            .prop('disabled', true)
            .on('click', function () {
                var $this = $(this),
                    data = $this.data();
                $this
                    .off('click')
                    .prop('disabled',true)
                // user rest to convert dae into glTF
                var callback = function(data) {
                    $this.prop('disabled',true);
                    if(data.result.output){console.debug(data.result.output);}
                    if(data.result.code){console.debug("Exit code: "+data.result.code);}
                    if (data.error){
                        var span = $('
Error code='+data.error.code+' :: '+data.error.message+'
');
                        data.context.append(span);
                    } else {
                        // ennumerate all resulting files
                        var $conve = upload.header(data.file.name);
                        $.each(data.result.files, function (index, file) {
                            var href = $('');
                            upload.filesArea.append(href);
                            var $download = $("<button>Download</button>").on("click",function(){
                                href[0].click();
                            });

                        var url = decodeURIComponent(file.url);
                        var ext = url.match(/\.[^.]+$/);
                        if(ext==".json"){
                            var $dialog = $("<button>Launch your model</button>").on("click",function(){
                                glTF.load(url, viewer.parse_gltf);
                                window.notif(url);
                            });
                            var $preview = $("<button>Peview</button>").on("click",function(){
                                var gitHtml = '

';
                                gitPanel = $('body').append(gitHtml);
                                $("#dialog").dialog({
                                    autoOpen: true,
                                    width: 800,
                                    height: 600,
                                    open: function (ev, ui) {
                                        $('#myIframe').attr('src', '/viewer/easy-viewer.html?file=/rest3d/upload/'+decodeURIComponent(file.name));
                                    }
                                });
                            });
                                upload.convert($conve,formatName(data,file),$dialog,$download,$preview);
                        }
                        else{
                            upload.download($conve,formatName(data,file),$download);//
                        }
                        });
                    }
                }

                rest3d.convert(data,callback);
            });

    upload.object.on('fileuploadadd', function (e, data) {
        upload.object=$(this);
        data.context = header;
        $.each(data.files, function (index, file) {
            if (!index) {
                var url =  file.url;
                console.debug(file + file.url);
                var $dialog = $("<button>Launch your model</button>").on("click",function(){
                                COLLADA.load(url, viewer.parse_dae);
                                window.notif(url);
                            });
                 var $preview = $("<button>Peview</button>").on("click",function(){
                                var gitHtml = '

';
                                gitPanel = $('body').append(gitHtml);
                                $("#dialog").dialog({
                                    autoOpen: true,
                                    width: 800,
                                    height: 600,
                                    open: function (ev, ui) {
                                        $('#myIframe').attr('src', '/viewer/easy-viewer.html?file=/rest3d/upload/'+decodeURIComponent(file.name));
                                    }//
                                });
                            });
                upload.upload(header,file.name,$preview,$dialog,uploadButton.clone(true).data(data));}//
            else{this.upload(header,file.name);}
        });
    }).on('fileuploadprocessalways', function (e, data) {
            var indexI = data.index,
            file = data.files[indexI],
             node = $(data.context.children('div')[index+1]);//s
        if (file.preview) {
            node
                .prepend('
')
                .prepend(file.preview);
        }
        if (file.error) {//
          GUI.addTooltip({
                    parent: node.find('button'),
                    content: file.error,
                });//
        }
        if (indexI + 1 === data.files.length) {
            node.find('button')
                .prop('disabled', !!data.files.error);
        }
        index++;//
    }).on('fileuploadprogressall', function (e, data) {
        var progress = parseInt(data.loaded / data.total * 100, 10);
        upload.progress.setValue(progress);
    }).on('fileuploaddone', function (e, data) {
        $.each(data.result.files, function (index, file) {
            file.assetName = data.result.files[index].name;
            var $node = convertButton.clone(true).data({file: file, context: data.context})
                .prop('disabled', !/dae$/i.test(file.url))
            buttonToReplace
                .replaceWith($node)
                .prop("id","nodeClose");//
                GUI.addIcon($node, "ui-icon-check", "", "before");
        });
    }).on('fileuploadfail', function (e, data) {
        if (!data.result) {
            $(data.context.children()[0])
                .append('
')
                .append('error communicating with server')
                .find('button').remove();
        } else
        $.each(data.result.files, function (index, file) {
            var error = $('').text(file.error);
            $(data.context.children()[index])
                .append('
')
                .append(error)
                .find('button').remove();
        });
    });
}
<|endoftext|>


<commit_before>FamousFramework.scene('famous-tests:physics:basic:particle', {
    behaviors: {
        '.particle': {
            'size': [200, 200],
            'align': [0.5, 0.5],
            'mount-point': [0.5, 0.5],
            'position': function(position) {
                return position;
            },
            'style': {
                'background': 'whitesmoke',
                'border-radius': '50%'
            }
        }
    },
    events: {
        '$lifecycle': {
            'post-load': function($state) {
                $state.applyPhysicsForce('gravity1D', [ 'position' ]);
            }
        }
    },
    states: {
        'position': [0, 0, 0]
    },
    tree: `
        <node class="particle"></node>
    `
});
<commit_msg>example: Add example of applying physics constraint<commit_after>FamousFramework.scene('famous-tests:physics:basic:particle', {
    behaviors: {
        '.particle': {
            'size': [200, 200],
            'align': [0.5, 0.5],
            'mount-point': [0.5, 0.5],
            'style': {
                'background': 'whitesmoke',
                'border-radius': '50%'
            }
        },
        '.one': {
            'position': function(position1) {
                return position1;
            }
        },
        '.two': {
            'position': function(position2) {
                return position2;
            }
        }
    },
    events: {
        '$lifecycle': {
            'post-load': function($state) {
                $state
                    .applyPhysicsForce('gravity1D', [ 'position1' ])
                    .applyPhysicsConstraint('distance', [ 'position1', 'position2' ], {
                        length: 400
                    });
            }
        }
    },
    states: {
        'position1': [0, 0, 0],
        'position2': [0, -200, 0]
    },
    tree: `
        <node class="particle one"></node>
        <node class="particle two"></node>
    `
});
<|endoftext|>


<commit_before><commit_msg>Replace var by const / let<commit_after><|endoftext|>


<commit_before>var map;
var center = new google.maps.LatLng(39.395, -76.609);

function drawMap() {
  //document.getElementById('map_div').style.display = "none";
  var mapOptions = {
    zoom : 8,
    center : new google.maps.LatLng(39.395, -76.609),
    mapTypeId : google.maps.MapTypeId.TERRAIN
  };

  //console.log(document.getElementById('map_div'));
  map = new google.maps.Map(document.getElementById('map_div'), mapOptions);

  //Fusion table load much faster than KML layers! (perhaps because the USGS kml loads more slowly than the fusion table.)
  //fusion table ID: 11Ujto70g1r7bWNSax5X84KYYuTpwPGmWeacAhkwP

  var fusionLayer = new google.maps.FusionTablesLayer({
    query : {
      select : 'location',
      from : '1Rt_U4LqeNPi6Tk1-kq8ta-6OP748nJJJTqdwlC0Q'
    }
  });
  fusionLayer.setMap(map);

  google.maps.event.addListener(fusionLayer, 'click', function(event) {
    console.log(event);
    var re = /[0-9]+/;
    var sId = re.exec(event.row.site_no.value)[0];
    //console.log(sId);
    viewModel.siteId(sId);
    viewModel.siteName(event.row.station_nm.value);
    viewModel.siteArray.push({id: sId, name: event.row.station_nm.value, area: +event.row.drain_area_va.value});
    //This siteIdArray.push won't capture the first data requested.
    viewModel.siteIdArray.push(sId);
    //console.log(viewModel.siteId());
    console.log(viewModel.siteArray());
    //viewModel.siteName.push(kmlEvent.featureData.name);

    //console.log(sId);
    //console.log(viewModel.siteId());
    //console.log(viewModel.siteName());

    getUSGS(sId);
    getTuNexrad(sId);
  });

/*
  //KML layers can use real-time updates from USGS, and will plot 5,000 points quickly... compared to plotting 5,000 points using other methods.
  //The KML layer still loads too slowly, or not at all.
  var points = new google.maps.KmlLayer("http://waterwatch.usgs.gov/kmls/real.kmz", {
    suppressInfoWindows : true,
    preserveViewport : true,
    map : map
  });

  google.maps.event.addListener(fusionLayer, 'click', function(event) {
    console.log(event);
    var re = /^[0-9]+/;
    var sId = re.exec(event.featureData.name)[0];
    viewModel.siteId(sId);
    viewModel.siteName(event.featureData.name);
    //This siteIdArray.push won't capture the first data requested.
    viewModel.siteIdArray.push(+sId);
    console.log(viewModel.siteId());
    console.log(viewModel.siteIdArray());
    //viewModel.siteName.push(kmlEvent.featureData.name);

    //console.log(sId);
    //console.log(viewModel.siteId());
    //console.log(viewModel.siteName());

    getUSGS(sId);
  });
*/

  tileNEX = new google.maps.ImageMapType({
    getTileUrl : function(tile, zoom) {
      return "http://mesonet.agron.iastate.edu/cache/tile.py/1.0.0/nexrad-n0q-900913/" + zoom + "/" + tile.x + "/" + tile.y + ".png?" + (new Date()).getTime();
    },
    tileSize : new google.maps.Size(256, 256),
    opacity : 0.90,
    name : 'NEXRAD',
    isPng : true
  });

  goes = new google.maps.ImageMapType({
    getTileUrl : function(tile, zoom) {
      return "http://mesonet.agron.iastate.edu/cache/tile.py/1.0.0/goes-east-vis-1km-900913/" + zoom + "/" + tile.x + "/" + tile.y + ".png?" + (new Date()).getTime();
    },
    tileSize : new google.maps.Size(256, 256),
    opacity : 0.40,
    name : 'GOES East Vis',
    isPng : true
  });

  map.overlayMapTypes.push(null);
  //create empty overlay entry
  //map.overlayMapTypes.setAt("0",goes);
  map.overlayMapTypes.setAt("1", tileNEX);
  google.maps.event.addListener(map, 'bounds_changed', function() {
    //console.log(map.getBounds());
  });

}

google.maps.event.addDomListener(window, 'load', drawMap);

<commit_msg>change Fusion Table that gets displayed<commit_after>var map;
var center = new google.maps.LatLng(39.395, -76.609);

function drawMap() {
  //document.getElementById('map_div').style.display = "none";
  var mapOptions = {
    zoom : 8,
    center : new google.maps.LatLng(39.395, -76.609),
    mapTypeId : google.maps.MapTypeId.TERRAIN
  };

  //console.log(document.getElementById('map_div'));
  map = new google.maps.Map(document.getElementById('map_div'), mapOptions);

  //Fusion tables load much faster than KML layers!
  //fusion table ID: 11Ujto70g1r7bWNSax5X84KYYuTpwPGmWeacAhkwP
  var fusionLayer = new google.maps.FusionTablesLayer({
    query : {
      select : 'location',
      //real.kmz, has more sites than Gages II, but less info.
      //from : '1Rt_U4LqeNPi6Tk1-kq8ta-6OP748nJJJTqdwlC0Q'

      //from Gages II merge, too large, takes too long to load
      from : '1wtRBQVozXdLsn5t0PL8egL5wL7tUzFQvw6NYQizn'

      //from Gages II merge- small
      //from : '103gQIyU069THrk7KGZYtbC8_My1rW4JwaHhX1ehe'
      //where : 'DRAIN_SQKM < 20'
    },
  });
  fusionLayer.setMap(map);

  google.maps.event.addListener(fusionLayer, 'click', function(event) {
    //Parse real.kmz
    //var re = /[0-9]+/;
    //var sId = re.exec(event.row.site_no.value)[0];
    //var siteName = event.row.station_nm.value;
    //var siteArray = {id: sId, name: event.row.station_nm.value, area: +event.row.drain_area_va.value};

    //Parsing Gages II merge
    var sId = event.row.STAID.value;
    var siteName = event.row.STANAME.value;
    var siteArray = {id: sId, name: siteName, area: +event.row.DRAIN_SQKM.value, impervious: +event.row.IMPNLCD06.value};
    viewModel.siteId(sId);
    viewModel.siteName(siteName);
    viewModel.siteArray.push(siteArray);
    //This siteIdArray.push won't capture the first data requested.
    viewModel.siteIdArray.push(sId);
    //console.log(viewModel.siteId());
    console.log(viewModel.siteArray());
    //viewModel.siteName.push(kmlEvent.featureData.name);

    getUSGS(sId);
    getTuNexrad(sId);
  });

/*
  //KML layers can use real-time updates from USGS, and will plot 5,000 points quickly... compared to plotting 5,000 points using other methods.
  //The KML layer still loads too slowly, or not at all.
  var points = new google.maps.KmlLayer("http://waterwatch.usgs.gov/kmls/real.kmz", {
    suppressInfoWindows : true,
    preserveViewport : true,
    map : map
  });

  google.maps.event.addListener(fusionLayer, 'click', function(event) {
    console.log(event);
    var re = /^[0-9]+/;
    var sId = re.exec(event.featureData.name)[0];
    viewModel.siteId(sId);
    viewModel.siteName(event.featureData.name);
    //This siteIdArray.push won't capture the first data requested.
    viewModel.siteIdArray.push(+sId);
    console.log(viewModel.siteId());
    console.log(viewModel.siteIdArray());
    //viewModel.siteName.push(kmlEvent.featureData.name);

    //console.log(sId);
    //console.log(viewModel.siteId());
    //console.log(viewModel.siteName());

    getUSGS(sId);
  });
*/

  tileNEX = new google.maps.ImageMapType({
    getTileUrl : function(tile, zoom) {
      return "http://mesonet.agron.iastate.edu/cache/tile.py/1.0.0/nexrad-n0q-900913/" + zoom + "/" + tile.x + "/" + tile.y + ".png?" + (new Date()).getTime();
    },
    tileSize : new google.maps.Size(256, 256),
    opacity : 0.90,
    name : 'NEXRAD',
    isPng : true
  });

  goes = new google.maps.ImageMapType({
    getTileUrl : function(tile, zoom) {
      return "http://mesonet.agron.iastate.edu/cache/tile.py/1.0.0/goes-east-vis-1km-900913/" + zoom + "/" + tile.x + "/" + tile.y + ".png?" + (new Date()).getTime();
    },
    tileSize : new google.maps.Size(256, 256),
    opacity : 0.40,
    name : 'GOES East Vis',
    isPng : true
  });

  map.overlayMapTypes.push(null);
  //create empty overlay entry
  //map.overlayMapTypes.setAt("0",goes);
  map.overlayMapTypes.setAt("1", tileNEX);
  google.maps.event.addListener(map, 'bounds_changed', function() {
    //console.log(map.getBounds());
  });
}
google.maps.event.addDomListener(window, 'load', drawMap);
<|endoftext|>


<commit_before>//==================================================
//  Task
//  test:css
//  For the description see [SupeflyCSS Test Task](https://github.com/superfly-css/superfly-css-task-test/)
//==================================================

var gulp = require('gulp');
var cheerio = require('gulp-cheerio');
var highlight = require('gulp-highlight');
var path = require('path');

var PLI = require('superfly-css-pli');

var renderTestBlock = '\n

';

//---------------------------------
gulp.task('test:css', function() {
  return gulp
    .src(PLI.SRC_TEST_HTML)
    .pipe(cheerio(function($, file) {
      $('.Test-markup > code').each(function() {
        var markup = $(this).html();
        //Insert the Test-render block after the Test-then block
        //Append the markup block to the Test-render block
        $($(this).parent().next()).after(renderTestBlock).next().append(markup);
      });
    }))
    .pipe(highlight())
    .pipe(gulp.dest(PLI.target.test.html));
});
<commit_msg>Updated task with predentation<commit_after>//==================================================
//  Task
//  test:css
//  For the description see
//  [SupeflyCSS Test Task](https://github.com/superfly-css/superfly-css-task-test/)
//  IMPLEMENTATION
//  1: Cheerio copies content from `Test-markup`
//     and appends it to the markup to the `Test-render`
//
//==================================================

var gulp = require('gulp');

// -1-
var cheerio = require('gulp-cheerio');
var highlight = require('gulp-highlight');

var transform = require('vinyl-transform');
var predentation = require('predentation');

var pre = transform(function(options) {
  return predentation(options);
});

var PLI = require('superfly-css-pli');

var renderTestBlock = '\n

';

//---------------------------------
gulp.task('test:css', function() {
  return gulp
    .src(PLI.SRC_TEST_HTML)
    .pipe(cheerio(function($, file) {
      $('.Test-markup > code').each(function() {
        var markup = $(this).html();
        //Insert the Test-render block after the Test-then block
        //Append the markup block to the Test-render block
        $($(this).parent().next()).after(renderTestBlock).next().append(markup);
      });
    }))
    .pipe(highlight())
    .pipe(pre)
    .pipe(gulp.dest(PLI.target.test.html));
});
<|endoftext|>


<commit_before>/*jslint newcap: true */
/*global XMLHttpRequest: false, inlineAttach: false, FormData: false */
/*
 * Inline Text Attachment
 *
 * Copyright 2012 Roy van Kaathoven.
 * Contact: royvankaathoven@hotmail.com
 *
 * Licensed under the MIT License.
 */
(function(document, window) {
    "use strict";

    /**
     * Simple function to merge the given objects
     *
     * @param {Object[]} object Multiple object parameters
     * @returns {Object}
     */
    function merge() {
        var result = {};
        for (var i = arguments.length - 1; i >= 0; i--) {
            var obj = arguments[i];
            for (var k in obj) {
                result[k] = obj[k];
            }
        }
        return result;
    }

    /**
     * @param {Object} options
     */
    window.inlineAttach = function(options, instance) {

        var settings = merge(options, inlineAttach.defaults),
            editor = instance,
            filenameTag = '{filename}',
            lastValue,
            me = this;

        /**
         * Upload a given file blob
         *
         * @param {Blob} file
         */
        this.uploadFile = function(file) {
            var formData = new FormData(),
                xhr = new XMLHttpRequest();

            formData.append(settings.uploadFieldName, file);

            xhr.open('POST', settings.uploadUrl);
            xhr.upload.onprogress = function(event) {
                // TODO show progress in text
            };
            xhr.onload = function(e) {
                // If HTTP status is OK or Created
                if (xhr.status === 200 || xhr.status === 201) {
                    var data = JSON.parse(xhr.responseText);
                    me.onUploadedFile(data);
                }
            };
            xhr.send(formData);
        };

        /**
         * Check if the given file is allowed
         *
         * @param {File} file
         */
        this.isAllowedFile = function(file) {
            return settings.allowedTypes.indexOf(file.type) >= 0;
        };

        /**
         * When a file has finished uploading
         *
         * @param {Object} data
         */
        this.onUploadedFile = function(data) {
            var result = settings.onUploadedFile(data);
            if (result !== false && data.filename) {
                var text = editor.getValue().replace(lastValue, settings.urlText.replace(filenameTag, data.filename));
                editor.setValue(text);
            }
        };

        /**
         * When a file has been received by a drop or paste event
         * @param {Blob} file
         */
        this.onReceivedFile = function(file) {
            var result = settings.onReceivedFile(file);
            if (result !== false) {
                lastValue = settings.progressText;
                editor.setValue(editor.getValue() + "\n\n" + lastValue);
            }
        };

        /**
         * Catches the paste event
         *
         * @param {Event} e
         * @returns {Boolean} If a file is handled
         */
        this.onPaste = function(e) {
            var result = false,
                clipboardData = e.clipboardData;

            if (typeof clipboardData === "object" && clipboardData.items !== null) {
                for (var i = 0; i < clipboardData.items.length; i++) {
                    var item = clipboardData.items[i];
                    if (me.isAllowedFile(item)) {
                        result = true;
                        this.onReceivedFile(item.getAsFile());
                        this.uploadFile(item.getAsFile());
                    }
                }
            }

            return result;
        };

        /**
         * Catches onDrop event
         *
         * @param {Event} e
         * @returns {Boolean} If a file is handled
         */
        this.onDrop = function(e) {
            var result = false;
            for (var i = 0; i < e.dataTransfer.files.length; i++) {
                var file = e.dataTransfer.files[i];
                if (me.isAllowedFile(file)) {
                    result = true;
                    this.onReceivedFile(file);
                    this.uploadFile(file);
                }
            }

            return result;
        };
    };

    /**
     * Editor
     */
    window.inlineAttach.Editor = function(instance) {

        var input = instance;

        return {
            getValue: function() {
                return input.value;
            },
            setValue: function(val) {
                input.value = val;
            }
        };
    };

    /**
     * Default configuration
     */
    window.inlineAttach.defaults = {
        // URL to upload the attachment
        uploadUrl: 'upload_attachment.php',
        // Request field name where the attachment will be placed in the form data
        uploadFieldName: 'file',
        allowedTypes: [
            'image/jpeg',
            'image/png',
            'image/jpg',
            'image/gif'
        ],

        /**
         * Will be inserted on a drop or paste event
         */
        progressText: '![Uploading file...]()',

        /**
         * When a file has successfully been uploaded the last inserted text
         * will be replaced by the urlText, the {filename} tag will be replaced
         * by the filename that has been returned by the server
         */
        urlText: "![file]({filename})",

        /**
         * When a file is received by drag-drop or paste
         *
         * @param {Blob} file
         */
        onReceivedFile: function(file) {},

        /**
         * When a file has succesfully been uploaded
         *
         * @param {Object} json Received json data
         */
        onUploadedFile: function(json) {}
    };

    /**
     * Attach to a standard input field
     *
     * @param {Input} input
     * @param {Object} options
     */
    window.inlineAttach.attachToInput = function(input, options) {

        options = options || {};

        var editor          = new inlineAttach.Editor(input),
            inlineattach    = new inlineAttach(options, editor);

        input.addEventListener('paste', function(e) {
            inlineattach.onPaste(e);
        }, false);
        input.addEventListener('drop', function(e) {
            e.stopPropagation();
            e.preventDefault();
            inlineattach.onDrop(e);
        }, false);
        input.addEventListener('dragenter', function(e) {
            e.stopPropagation();
            e.preventDefault();
        }, false);
        input.addEventListener('dragover', function(e) {
            e.stopPropagation();
            e.preventDefault();
        }, false);
    };

})(document, window);
<commit_msg>Better newline handling for pastes<commit_after>/*jslint newcap: true */
/*global XMLHttpRequest: false, inlineAttach: false, FormData: false */
/*
 * Inline Text Attachment
 *
 * Copyright 2012 Roy van Kaathoven.
 * Contact: royvankaathoven@hotmail.com
 *
 * Licensed under the MIT License.
 */
(function(document, window) {
    "use strict";

    /**
     * Simple function to merge the given objects
     *
     * @param {Object[]} object Multiple object parameters
     * @returns {Object}
     */
    function merge() {
        var result = {};
        for (var i = arguments.length - 1; i >= 0; i--) {
            var obj = arguments[i];
            for (var k in obj) {
                result[k] = obj[k];
            }
        }
        return result;
    }

    /**
     * @param {Object} options
     */
    window.inlineAttach = function(options, instance) {

        var settings = merge(options, inlineAttach.defaults),
            editor = instance,
            filenameTag = '{filename}',
            lastValue,
            me = this;

        /**
         * Upload a given file blob
         *
         * @param {Blob} file
         */
        this.uploadFile = function(file) {
            var formData = new FormData(),
                xhr = new XMLHttpRequest();

            formData.append(settings.uploadFieldName, file);

            xhr.open('POST', settings.uploadUrl);
            xhr.upload.onprogress = function(event) {
                // TODO show progress in text
            };
            xhr.onload = function(e) {
                // If HTTP status is OK or Created
                if (xhr.status === 200 || xhr.status === 201) {
                    var data = JSON.parse(xhr.responseText);
                    me.onUploadedFile(data);
                }
            };
            xhr.send(formData);
        };

        /**
         * Check if the given file is allowed
         *
         * @param {File} file
         */
        this.isAllowedFile = function(file) {
            return settings.allowedTypes.indexOf(file.type) >= 0;
        };

        /**
         * When a file has finished uploading
         *
         * @param {Object} data
         */
        this.onUploadedFile = function(data) {
            var result = settings.onUploadedFile(data);
            if (result !== false && data.filename) {
                var text = editor.getValue().replace(lastValue, settings.urlText.replace(filenameTag, data.filename));
                editor.setValue(text);
            }
        };

        /**
         * Append a line of text at the bottom, ensuring there aren't unnecessary newlines
         */
        function appendInItsOwnLine(previous, appended) {
          return (previous + "\n\n[[D]]" + appended)
                  .replace(/(\n{2,})\[\[D\]\]/, "\n\n")
                  .replace(/^(\n*)/, "");
        }

        /**
         * When a file has been received by a drop or paste event
         * @param {Blob} file
         */
        this.onReceivedFile = function(file) {
            var result = settings.onReceivedFile(file), text;
            if (result !== false) {
                lastValue = settings.progressText;
                editor.setValue(appendInItsOwnLine(editor.getValue(), lastValue));
            }
        };

        /**
         * Catches the paste event
         *
         * @param {Event} e
         * @returns {Boolean} If a file is handled
         */
        this.onPaste = function(e) {
            var result = false,
                clipboardData = e.clipboardData;

            if (typeof clipboardData === "object" && clipboardData.items !== null) {
                for (var i = 0; i < clipboardData.items.length; i++) {
                    var item = clipboardData.items[i];
                    if (me.isAllowedFile(item)) {
                        result = true;
                        this.onReceivedFile(item.getAsFile());
                        this.uploadFile(item.getAsFile());
                    }
                }
            }

            return result;
        };

        /**
         * Catches onDrop event
         *
         * @param {Event} e
         * @returns {Boolean} If a file is handled
         */
        this.onDrop = function(e) {
            var result = false;
            for (var i = 0; i < e.dataTransfer.files.length; i++) {
                var file = e.dataTransfer.files[i];
                if (me.isAllowedFile(file)) {
                    result = true;
                    this.onReceivedFile(file);
                    this.uploadFile(file);
                }
            }

            return result;
        };
    };

    /**
     * Editor
     */
    window.inlineAttach.Editor = function(instance) {

        var input = instance;

        return {
            getValue: function() {
                return input.value;
            },
            setValue: function(val) {
                input.value = val;
            }
        };
    };

    /**
     * Default configuration
     */
    window.inlineAttach.defaults = {
        // URL to upload the attachment
        uploadUrl: 'upload_attachment.php',
        // Request field name where the attachment will be placed in the form data
        uploadFieldName: 'file',
        allowedTypes: [
            'image/jpeg',
            'image/png',
            'image/jpg',
            'image/gif'
        ],

        /**
         * Will be inserted on a drop or paste event
         */
        progressText: '![Uploading file...]()',

        /**
         * When a file has successfully been uploaded the last inserted text
         * will be replaced by the urlText, the {filename} tag will be replaced
         * by the filename that has been returned by the server
         */
        urlText: "![file]({filename})",

        /**
         * When a file is received by drag-drop or paste
         *
         * @param {Blob} file
         */
        onReceivedFile: function(file) {},

        /**
         * When a file has succesfully been uploaded
         *
         * @param {Object} json Received json data
         */
        onUploadedFile: function(json) {}
    };

    /**
     * Attach to a standard input field
     *
     * @param {Input} input
     * @param {Object} options
     */
    window.inlineAttach.attachToInput = function(input, options) {

        options = options || {};

        var editor          = new inlineAttach.Editor(input),
            inlineattach    = new inlineAttach(options, editor);

        input.addEventListener('paste', function(e) {
            inlineattach.onPaste(e);
        }, false);
        input.addEventListener('drop', function(e) {
            e.stopPropagation();
            e.preventDefault();
            inlineattach.onDrop(e);
        }, false);
        input.addEventListener('dragenter', function(e) {
            e.stopPropagation();
            e.preventDefault();
        }, false);
        input.addEventListener('dragover', function(e) {
            e.stopPropagation();
            e.preventDefault();
        }, false);
    };

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


<commit_before>'use strict'

const fs = require('fs-extra')
const path = require('path')
const spawn = require('child_process').spawn
const debug = require('debug')('windows-build-tools')
const chalk = require('chalk')
const Spinner = require('cli-spinner').Spinner

const launchInstaller = require('./launch')
const Tailer = require('./tailer')
const utils = require('../utils')

let spinner

/**
 * Installs the build tools, tailing the installation log file
 * to understand what's happening
 *
 * @returns {Promise.<Object>} - Promise that resolves with the installation result
 */

function install (cb) {
  console.log(chalk.green('Starting installation...'))

  launchInstaller()
    .then(() => launchSpinner())
    .then(() => Promise.all([installBuildTools(), installPython()]))
    .then((paths) => {
      stopSpinner()

      const variables = {
        buildTools: paths[0],
        python: paths[1]
      }
      cb(variables)
    })
    .catch((error) => {
      stopSpinner()

      console.log(error)
    })
}

function stopSpinner() {
  if (spinner) {
    spinner.stop(false)
  }
}

function launchSpinner() {
  console.log('Launched installers, now waiting for them to finish.')
  console.log('This will likely take some time - please be patient!')

  spinner = new Spinner(`Waiting for installers... %s`)
  spinner.setSpinnerDelay(180)
}

function installBuildTools () {
  return new Promise((resolve, reject) => {
    const tailer = new Tailer(utils.getBuildToolsInstallerPath().logPath)

    tailer.on('exit', (result, details) => {
      debug('build tools tailer exited');
      if (result === 'error') {
        debug('Installer: Tailer found error with installer', details)
        reject(err)
      }

      if (result === 'success') {
        console.log(chalk.bold.green('Successfully installed Visual Studio Build Tools.'))
        debug('Installer: Successfully installed Visual Studio Build Tools according to tailer')
        resolve()
      }

      if (result === 'failure') {
        console.log(chalk.bold.red('Could not install Visual Studio Build Tools.'))
        console.log('Please find more details in the log files, which can be found at')
        console.log(utils.getWorkDirectory())
        debug('Installer: Failed to install according to tailer')
        resolve()
      }
    })

    tailer.start()
  })
}

function installPython () {
  return new Promise((resolve, reject) => {
    // The log file for msiexe is utf-16
    const tailer = new Tailer(utils.getPythonInstallerPath().logPath, 'ucs2')

    tailer.on('exit', (result, details) => {
      debug('python tailer exited');
      if (result === 'error') {
        debug('Installer: Tailer found error with installer', details)
        reject(err)
      }

      if (result === 'success') {
        console.log(chalk.bold.green('Successfully installed Python 2.7'))
        debug('Installer: Successfully installed Python 2.7 according to tailer')

        var variables = {
          pythonPath: details || utils.getPythonInstallerPath().targetPath
        }
        resolve(variables)
      }

      if (result === 'failure') {
        console.log(chalk.bold.red('Could not install Python 2.7.'))
        console.log('Please find more details in the log files, which can be found at')
        console.log(utils.getWorkDirectory())
        debug('Installer: Failed to install Python 2.7 according to tailer')
        resolve(undefined)
      }
    })

    tailer.start()
  })
}

module.exports = install<commit_msg>:wrench: Actually start the spinner :|<commit_after>'use strict'

const fs = require('fs-extra')
const path = require('path')
const spawn = require('child_process').spawn
const debug = require('debug')('windows-build-tools')
const chalk = require('chalk')
const Spinner = require('cli-spinner').Spinner

const launchInstaller = require('./launch')
const Tailer = require('./tailer')
const utils = require('../utils')

let spinner

/**
 * Installs the build tools, tailing the installation log file
 * to understand what's happening
 *
 * @returns {Promise.<Object>} - Promise that resolves with the installation result
 */

function install (cb) {
  console.log(chalk.green('Starting installation...'))

  launchInstaller()
    .then(() => launchSpinner())
    .then(() => Promise.all([installBuildTools(), installPython()]))
    .then((paths) => {
      stopSpinner()

      const variables = {
        buildTools: paths[0],
        python: paths[1]
      }
      cb(variables)
    })
    .catch((error) => {
      stopSpinner()

      console.log(error)
    })
}

function stopSpinner() {
  if (spinner) {
    spinner.stop(false)
  }
}

function launchSpinner() {
  console.log('Launched installers, now waiting for them to finish.')
  console.log('This will likely take some time - please be patient!')

  spinner = new Spinner(`Waiting for installers... %s`)
  spinner.setSpinnerDelay(180)
  spinner.start()
}

function installBuildTools () {
  return new Promise((resolve, reject) => {
    const tailer = new Tailer(utils.getBuildToolsInstallerPath().logPath)

    tailer.on('exit', (result, details) => {
      debug('build tools tailer exited');
      if (result === 'error') {
        debug('Installer: Tailer found error with installer', details)
        reject(err)
      }

      if (result === 'success') {
        console.log(chalk.bold.green('Successfully installed Visual Studio Build Tools.'))
        debug('Installer: Successfully installed Visual Studio Build Tools according to tailer')
        resolve()
      }

      if (result === 'failure') {
        console.log(chalk.bold.red('Could not install Visual Studio Build Tools.'))
        console.log('Please find more details in the log files, which can be found at')
        console.log(utils.getWorkDirectory())
        debug('Installer: Failed to install according to tailer')
        resolve()
      }
    })

    tailer.start()
  })
}

function installPython () {
  return new Promise((resolve, reject) => {
    // The log file for msiexe is utf-16
    const tailer = new Tailer(utils.getPythonInstallerPath().logPath, 'ucs2')

    tailer.on('exit', (result, details) => {
      debug('python tailer exited');
      if (result === 'error') {
        debug('Installer: Tailer found error with installer', details)
        reject(err)
      }

      if (result === 'success') {
        console.log(chalk.bold.green('Successfully installed Python 2.7'))
        debug('Installer: Successfully installed Python 2.7 according to tailer')

        var variables = {
          pythonPath: details || utils.getPythonInstallerPath().targetPath
        }
        resolve(variables)
      }

      if (result === 'failure') {
        console.log(chalk.bold.red('Could not install Python 2.7.'))
        console.log('Please find more details in the log files, which can be found at')
        console.log(utils.getWorkDirectory())
        debug('Installer: Failed to install Python 2.7 according to tailer')
        resolve(undefined)
      }
    })

    tailer.start()
  })
}

module.exports = install<|endoftext|>


<commit_before>define([
    'vellum/mugs',
    'vellum/widgets',
    'vellum/util',
    'underscore',
    'jquery',
    'vellum/core'
], function (
    mugs,
    widgets,
    util,
    _,
    $
) {
    "use strict";
    var DEFAULT_XMLNS = "http://opendatakit.org/xforms";
    function makeODKXIntentTag (form, nodeID, path) {
        return new ODKXIntentTag(form, {
            path: path || "",
            xmlns: DEFAULT_XMLNS,
            extra: {},
            response: {},
            unknownAttributes: {},
            initialNodeID: nodeID
        });
    }
    function ODKXIntentTag(form, data) {
        util.BoundPropertyMap.call(this, form, data);
    }
    ODKXIntentTag.prototype = Object.create(util.BoundPropertyMap.prototype);
    ODKXIntentTag.prototype.clone = function () {
        return new ODKXIntentTag(this._form, this._data);
    };
    var parseInnerTags = function (tagObj, innerTag) {
        var store = {};
        _.each(tagObj.find(innerTag), function (inner) {
            var $innerTag = $(inner);
            store[$innerTag.attr('key')] = $innerTag.attr('ref');
        });
        return store;
    };
    var writeInnerTagXML = function(xmlWriter, innerTag, store) {
        if (store) {
            _.each(store, function (ref, key) {
                if (key) {
                    xmlWriter.writeStartElement(innerTag);
                    xmlWriter.writeAttributeString("key", key);
                    xmlWriter.writeAttributeString("ref", ref);
                    xmlWriter.writeEndElement();
                }
            });
        }
    };
    ODKXIntentTag.prototype.writeXML = function (xmlWriter, currentNodeID) {
        xmlWriter.writeStartElement('odkx:intent');
        xmlWriter.writeAttributeString("xmlns:odkx", this.getAttr('xmlns'));
        xmlWriter.writeAttributeString("id", currentNodeID || this.getAttr('initialNodeID'));
        xmlWriter.writeAttributeString("class", this.getAttr('path'));
        _.each(this.getAttr('unknownAttributes'), function (value, name) {
            xmlWriter.writeAttributeString(name, value);
        });
        writeInnerTagXML(xmlWriter, 'extra', this.getAttr('extra'));
        writeInnerTagXML(xmlWriter, 'response', this.getAttr('response'));
        xmlWriter.writeEndElement('odkx:intent');
    };

    var intentManager = function () {
        var that = {};
        that.unmappedIntentTags = {};

        that.parseIntentTagsFromHead = function (tags) {
            _.each(tags, function (tagXML) {
                var $tag, tagId, newTag, xmlns;
                $tag = $(tagXML);

                tagId = $tag.attr('id');
                newTag = makeODKXIntentTag(null, tagId, $tag.attr('class'));

                xmlns = $tag.attr('xmlns:odkx');
                newTag.setAttr('xmlns', xmlns || newTag.getAttr('xmlns'));
                newTag.setAttr('extra', parseInnerTags($tag, 'extra'));
                newTag.setAttr('response', parseInnerTags($tag, 'response'));
                var unknowns = newTag.getAttr('unknownAttributes');
                _.each(tagXML.attributes, function (attr) {
                    if (attr.nodeName !== 'id' &&
                        attr.nodeName !== 'class' &&
                        attr.nodeName !== 'xmlns:odkx')
                    {
                        unknowns[attr.nodeName] = attr.nodeValue;
                    }
                });
                that.unmappedIntentTags[tagId] = newTag;
            });
        };

        that.getParsedIntentTagWithID = function (nodeID) {
            var intentTag = null;
            _.each(that.unmappedIntentTags, function (tag) {
                if (tag.getAttr('initialNodeID') === nodeID) {
                    intentTag = tag;
                }
            });
            return intentTag;
        };

        that.syncMugWithIntent = function (mug) {
            // called when initializing a mug from a parsed form
            if (mug.__className === "AndroidIntent") {
                var nodeID = mug.p.nodeID,
                    tag = that.getParsedIntentTagWithID(nodeID);
                if (!tag) {
                    var path = (mug.intentTag) ? mug.intentTag.getAttr('path') : null;
                    tag = makeODKXIntentTag(mug.form, nodeID, path);
                }
                mug.intentTag = tag;
                mug.intentTag._form = mug.form;
                delete that.unmappedIntentTags[tag.getAttr('initialNodeID')];
            }
        };

        that.writeIntentXML = function (xmlWriter, tree) {
            // make sure any leftover intent tags are still kept
            _.each(that.unmappedIntentTags, function (tag) {
               tag.writeXML(xmlWriter, null);
            });

            var intents,
                getIntentMugs = function(node) {
                    var mug = node.getValue();
                    if (!mug || node.isRootNode) {
                        return null;
                    }
                    if (mug.options.dataType === 'intent') {
                        return mug;
                    } else {
                        return null;
                    }
                };
            intents = tree.treeMap(getIntentMugs);
            if (intents.length > 0) {
                intents.map(function (intentMug) {
                    intentMug.intentTag.writeXML(
                        xmlWriter, intentMug.p.nodeID);
                });
            }
        };
        return that;
    };

    function androidIntentAppId(mug, options) {
        var widget = widgets.text(mug, options),
            input = widget.input;

        input.attr('placeholder', 'Insert Android Application ID');
        widget.currentValue = (mug.intentTag) ? mug.intentTag.getAttr('path') : "";

        widget.updateValue = function () {
            widget.mug.intentTag.setAttr('path', widget.getValue());
        };

        return widget;
    }

    function androidIntentExtra(mug, options) {
        options.id = "intent-extra";
        var widget = widgets.baseKeyValue(mug, options);
        widget.currentValue = (mug.intentTag) ? mug.intentTag.getAttr('extra') : {};
        widget.definition = mug.p.getDefinition('androidIntentExtra');

        widget.save = function () {
            if (widget.mug.intentTag) {
                widget.mug.intentTag.setAttr('extra', widget.getValidValues());
            }
        };

        return widget;
    }

    function androidIntentResponse(mug, options) {
        options.id = "intent-response";
        var widget = widgets.baseKeyValue(mug, options);
        widget.currentValue = (mug.intentTag) ? mug.intentTag.getAttr('response') : {};
        widget.definition = mug.p.getDefinition('androidIntentResponse');

        widget.save = function () {
            if (widget.mug.intentTag) {
                widget.mug.intentTag.setAttr('response', widget.getValidValues());
            }
        };

        return widget;
    }

    var AndroidIntent = util.extend(mugs.defaultOptions, {
        typeName: 'Android App Callout',
        dataType: 'intent',
        tagName: 'input',
        icon: 'icon-vellum-android-intent',
        isODKOnly: true,
        isTypeChangeable: false,
        intentTag: null,
        init: function (mug, form) {
        },
        spec: {
            androidIntentAppId: {
                lstring: 'Intent ID',
                visibility: 'visible',
                widget: androidIntentAppId,
                serialize: function (value, key, mug, data) {
                    function dropBlank(item) {
                        item = _.clone(item);
                        if (item[""] === "") {
                            delete item[""];
                        }
                        return item;
                    }
                    var keys = {
                            "path": _.identity,
                            "xmlns": function (v) { return v !== DEFAULT_XMLNS ? v : null; },
                            "extra": dropBlank,
                            "response": dropBlank,
                            "unknownAttributes": _.identity,
                        },
                        tag = mug.intentTag,
                        values = {};
                    if (tag) {
                        _.each(keys, function (valueOf, attr) {
                            var val = valueOf(tag.getAttr(attr));
                            if (!_.isEmpty(val)) {
                                values[attr] = val;
                            }
                        });
                        if (!_.isEmpty(values)) {
                            data.intent = values;
                        }
                    }
                },
                deserialize: function (data, key, mug) {
                    var tag = makeODKXIntentTag(mug.form, mug.p.nodeID, null);
                    if (data.intent) {
                        _.each([
                            "path",
                            "xmlns",
                            "extra",
                            "response",
                            "unknownAttributes"
                        ], function (attr) {
                            if (!_.isEmpty(data.intent[attr])) {
                                tag.setAttr(attr, data.intent[attr]);
                            }
                        });
                    }
                    mug.intentTag = tag;
                }
            },
            androidIntentExtra: {
                lstring: 'Extra',
                visibility: 'visible',
                widget: androidIntentExtra
            },
            androidIntentResponse: {
                lstring: 'Response',
                visibility: 'visible',
                widget: androidIntentResponse
            }
        },
        // todo: move to spec system
        getAppearanceAttribute: function (mug) {
            return 'intent:' + mug.p.nodeID;
        }
    });

    $.vellum.plugin("intents", {}, {
        loadXML: function (xml) {
            var manager = intentManager(null);
            this.data.intents.manager = manager;
            this.data.intents.manager.parseIntentTagsFromHead(
                $(xml).find('h\\:head, head')
                    .children("odkx\\:intent, intent"));

            this.__callOld();
        },
        contributeToHeadXML: function (xmlWriter, form) {
            this.__callOld();
            this.data.intents.manager.writeIntentXML(xmlWriter, form.tree);
        },
        handleNewMug: function (mug) {
            var ret = this.__callOld();
            this.data.intents.manager.syncMugWithIntent(mug);
            return ret;
        },
        handleMugParseFinish: function (mug) {
            this.__callOld();
            this.data.intents.manager.syncMugWithIntent(mug);
        },
        getMugTypes: function () {
            var types = this.__callOld();
            types.normal.AndroidIntent = AndroidIntent;
            return types;
        },
        getMainProperties: function () {
            return this.__callOld().concat([
                "androidIntentAppId",
                "androidIntentExtra",
                "androidIntentResponse"
            ]);
        }
    });
});
<commit_msg>android app id correctly updates mug<commit_after>define([
    'vellum/mugs',
    'vellum/widgets',
    'vellum/util',
    'underscore',
    'jquery',
    'vellum/core'
], function (
    mugs,
    widgets,
    util,
    _,
    $
) {
    "use strict";
    var DEFAULT_XMLNS = "http://opendatakit.org/xforms";
    function makeODKXIntentTag (form, nodeID, path) {
        return new ODKXIntentTag(form, {
            path: path || "",
            xmlns: DEFAULT_XMLNS,
            extra: {},
            response: {},
            unknownAttributes: {},
            initialNodeID: nodeID
        });
    }
    function ODKXIntentTag(form, data) {
        util.BoundPropertyMap.call(this, form, data);
    }
    ODKXIntentTag.prototype = Object.create(util.BoundPropertyMap.prototype);
    ODKXIntentTag.prototype.clone = function () {
        return new ODKXIntentTag(this._form, this._data);
    };
    var parseInnerTags = function (tagObj, innerTag) {
        var store = {};
        _.each(tagObj.find(innerTag), function (inner) {
            var $innerTag = $(inner);
            store[$innerTag.attr('key')] = $innerTag.attr('ref');
        });
        return store;
    };
    var writeInnerTagXML = function(xmlWriter, innerTag, store) {
        if (store) {
            _.each(store, function (ref, key) {
                if (key) {
                    xmlWriter.writeStartElement(innerTag);
                    xmlWriter.writeAttributeString("key", key);
                    xmlWriter.writeAttributeString("ref", ref);
                    xmlWriter.writeEndElement();
                }
            });
        }
    };
    ODKXIntentTag.prototype.writeXML = function (xmlWriter, mug) {
        xmlWriter.writeStartElement('odkx:intent');
        xmlWriter.writeAttributeString("xmlns:odkx", this.getAttr('xmlns'));
        xmlWriter.writeAttributeString("id", mug ? mug.p.nodeID : this.getAttr('initialNodeID'));
        xmlWriter.writeAttributeString("class", mug ? mug.p.androidIntentAppId : this.getAttr('path'));
        _.each(this.getAttr('unknownAttributes'), function (value, name) {
            xmlWriter.writeAttributeString(name, value);
        });
        writeInnerTagXML(xmlWriter, 'extra', this.getAttr('extra'));
        writeInnerTagXML(xmlWriter, 'response', this.getAttr('response'));
        xmlWriter.writeEndElement('odkx:intent');
    };

    var intentManager = function () {
        var that = {};
        that.unmappedIntentTags = {};

        that.parseIntentTagsFromHead = function (tags) {
            _.each(tags, function (tagXML) {
                var $tag, tagId, newTag, xmlns;
                $tag = $(tagXML);

                tagId = $tag.attr('id');
                newTag = makeODKXIntentTag(null, tagId, $tag.attr('class'));

                xmlns = $tag.attr('xmlns:odkx');
                newTag.setAttr('xmlns', xmlns || newTag.getAttr('xmlns'));
                newTag.setAttr('extra', parseInnerTags($tag, 'extra'));
                newTag.setAttr('response', parseInnerTags($tag, 'response'));
                var unknowns = newTag.getAttr('unknownAttributes');
                _.each(tagXML.attributes, function (attr) {
                    if (attr.nodeName !== 'id' &&
                        attr.nodeName !== 'class' &&
                        attr.nodeName !== 'xmlns:odkx')
                    {
                        unknowns[attr.nodeName] = attr.nodeValue;
                    }
                });
                that.unmappedIntentTags[tagId] = newTag;
            });
        };

        that.getParsedIntentTagWithID = function (nodeID) {
            var intentTag = null;
            _.each(that.unmappedIntentTags, function (tag) {
                if (tag.getAttr('initialNodeID') === nodeID) {
                    intentTag = tag;
                }
            });
            return intentTag;
        };

        that.syncMugWithIntent = function (mug) {
            // called when initializing a mug from a parsed form
            if (mug.__className === "AndroidIntent") {
                var nodeID = mug.p.nodeID,
                    tag = that.getParsedIntentTagWithID(nodeID);
                if (!tag) {
                    var path = (mug.intentTag) ? mug.intentTag.getAttr('path') : null;
                    tag = makeODKXIntentTag(mug.form, nodeID, path);
                }
                mug.intentTag = tag;
                mug.intentTag._form = mug.form;
                mug.p.androidIntentAppId = tag.getAttr('path');
                delete that.unmappedIntentTags[tag.getAttr('initialNodeID')];
            }
        };

        that.writeIntentXML = function (xmlWriter, tree) {
            // make sure any leftover intent tags are still kept
            _.each(that.unmappedIntentTags, function (tag) {
               tag.writeXML(xmlWriter, null);
            });

            var intents,
                getIntentMugs = function(node) {
                    var mug = node.getValue();
                    if (!mug || node.isRootNode) {
                        return null;
                    }
                    if (mug.options.dataType === 'intent') {
                        return mug;
                    } else {
                        return null;
                    }
                };
            intents = tree.treeMap(getIntentMugs);
            if (intents.length > 0) {
                intents.map(function (intentMug) {
                    intentMug.intentTag.writeXML(xmlWriter, intentMug);
                });
            }
        };
        return that;
    };

    function androidIntentAppId(mug, options) {
        var widget = widgets.text(mug, options),
            input = widget.input;
        input.attr('placeholder', 'Insert Android Application ID');
        return widget;
    }

    function androidIntentExtra(mug, options) {
        options.id = "intent-extra";
        var widget = widgets.baseKeyValue(mug, options);
        widget.currentValue = (mug.intentTag) ? mug.intentTag.getAttr('extra') : {};
        widget.definition = mug.p.getDefinition('androidIntentExtra');

        widget.save = function () {
            if (widget.mug.intentTag) {
                widget.mug.intentTag.setAttr('extra', widget.getValidValues());
            }
        };

        return widget;
    }

    function androidIntentResponse(mug, options) {
        options.id = "intent-response";
        var widget = widgets.baseKeyValue(mug, options);
        widget.currentValue = (mug.intentTag) ? mug.intentTag.getAttr('response') : {};
        widget.definition = mug.p.getDefinition('androidIntentResponse');

        widget.save = function () {
            if (widget.mug.intentTag) {
                widget.mug.intentTag.setAttr('response', widget.getValidValues());
            }
        };

        return widget;
    }

    var AndroidIntent = util.extend(mugs.defaultOptions, {
        typeName: 'Android App Callout',
        dataType: 'intent',
        tagName: 'input',
        icon: 'icon-vellum-android-intent',
        isODKOnly: true,
        isTypeChangeable: false,
        intentTag: null,
        init: function (mug, form) {
        },
        spec: {
            androidIntentAppId: {
                lstring: 'Intent ID',
                visibility: 'visible',
                widget: androidIntentAppId,
                serialize: function (value, key, mug, data) {
                    function dropBlank(item) {
                        item = _.clone(item);
                        if (item[""] === "") {
                            delete item[""];
                        }
                        return item;
                    }
                    var keys = {
                            "path": _.identity,
                            "xmlns": function (v) { return v !== DEFAULT_XMLNS ? v : null; },
                            "extra": dropBlank,
                            "response": dropBlank,
                            "unknownAttributes": _.identity,
                        },
                        tag = mug.intentTag,
                        values = {};
                    if (tag) {
                        _.each(keys, function (valueOf, attr) {
                            var val = valueOf(tag.getAttr(attr));
                            if (!_.isEmpty(val)) {
                                values[attr] = val;
                            }
                        });
                        if (!_.isEmpty(values)) {
                            data.intent = values;
                        }
                    }
                },
                deserialize: function (data, key, mug) {
                    var tag = makeODKXIntentTag(mug.form, mug.p.nodeID, null);
                    if (data.intent) {
                        _.each([
                            "path",
                            "xmlns",
                            "extra",
                            "response",
                            "unknownAttributes"
                        ], function (attr) {
                            if (!_.isEmpty(data.intent[attr])) {
                                tag.setAttr(attr, data.intent[attr]);
                            }
                        });
                    }
                    mug.intentTag = tag;
                }
            },
            androidIntentExtra: {
                lstring: 'Extra',
                visibility: 'visible',
                widget: androidIntentExtra
            },
            androidIntentResponse: {
                lstring: 'Response',
                visibility: 'visible',
                widget: androidIntentResponse
            }
        },
        // todo: move to spec system
        getAppearanceAttribute: function (mug) {
            return 'intent:' + mug.p.nodeID;
        }
    });

    $.vellum.plugin("intents", {}, {
        loadXML: function (xml) {
            var manager = intentManager(null);
            this.data.intents.manager = manager;
            this.data.intents.manager.parseIntentTagsFromHead(
                $(xml).find('h\\:head, head')
                    .children("odkx\\:intent, intent"));

            this.__callOld();
        },
        contributeToHeadXML: function (xmlWriter, form) {
            this.__callOld();
            this.data.intents.manager.writeIntentXML(xmlWriter, form.tree);
        },
        handleNewMug: function (mug) {
            var ret = this.__callOld();
            this.data.intents.manager.syncMugWithIntent(mug);
            return ret;
        },
        handleMugParseFinish: function (mug) {
            this.__callOld();
            this.data.intents.manager.syncMugWithIntent(mug);
        },
        getMugTypes: function () {
            var types = this.__callOld();
            types.normal.AndroidIntent = AndroidIntent;
            return types;
        },
        getMainProperties: function () {
            return this.__callOld().concat([
                "androidIntentAppId",
                "androidIntentExtra",
                "androidIntentResponse"
            ]);
        }
    });
});
<|endoftext|>


<commit_before>export const TCB = 'TCB';
export const CONSTANT = 'CONSTANT';
export const LINEAR = 'LINEAR';
export const HALT = 'HALT';
export const MANUAL = 'MANUAL';
export const UNDEFINED = 'UNDEFINED';
export const NIL = 'NIL';
export const CLAMPED = 'CLAMPED';
<commit_msg>Changed enum so it will actually work down the road<commit_after>export const TCB = 0;
export const CONSTANT = 1;
export const LINEAR = 2;
export const HALT = 3;
export const MANUAL = 4;
export const UNDEFINED = 5;
export const NIL = 6;
export const CLAMPED = 7;
<|endoftext|>


<commit_before>//TODO put all of the constants that apply globally here

var modConstants = {

  //Function to determine a modifier for the room used to calculate numbers of creeps
  //returns a number between 0 and 3 inclusive
  getConLvlMod:function(room){
    var controllerLvl = room.controller.level;
    return Math.ceil((controllerLvl-1)/3);
  }

};

module.exports = modConstants;
<commit_msg>added two more constants<commit_after>//TODO put all of the constants that apply globally here

var modConstants = {

  //Function to determine a modifier for the room used to calculate numbers of creeps
  //returns a number between 0 and 3 inclusive
  getConLvlMod:function(room){
    var controllerLvl = room.controller.level;
    return Math.ceil((controllerLvl-1)/3);
  },
  roomEnergyBuffer: 300000,
  structBuffer:1000

};

module.exports = modConstants;
<|endoftext|>