<commit_before>"use strict";
/* jshint evil : true, esnext: true, globalstrict: true, undef: true */
/* global _ : true, console, require, process  */

const cheerio = require('cheerio');
const _       = require('lodash');
const Hogan   = require('hogan.js');
const fs      = require('fs');
const util    = require('util');
const path    = require('path');
const he      = require('he');
const SNIPPET_REUSE_TAGS = ['head', 'tail', 'top', 'bottom'];
const FILES_USED = [];

var args     = process.argv.slice(2);

switch (args.length) {
  case 3: break;
  case 4: break;
  default:
    console.error(to_string(args));
    console.error("!!! arguments.length incorrect. Try: [layout]  template_file  output_dir  public_path");
    process.exit(1);
}

var public_dir=args.pop();
var dir      = args.pop();
var template = args.pop();
var layout   = args.pop();

should_be(public_dir, is_string);
should_be(dir,        is_dir);
should_be(template,   is_file);
if (layout)
  should_be(layout,  is_file);

var name              = path.basename(template, '.html');
var template_contents = read_and_cache_file_name(template);
var layout_contents   = layout && fs.readFileSync(layout).toString();
var $layout           = layout && cheerio.load(layout_contents, {recognizeSelfClosing: true});
var $template         = cheerio.load(template_contents, {recognizeSelfClosing: true});
var has_conditionals  = $template('when').length > 0;
$template = var_pipeline(
  $template,
  scripts_to_tag,
  styles_to_tag,
  tag_template_to_script,
  conditionals_to_files,
  markup_to_file,
  layout ? to_func(merge_with_layout, $layout) : identity
);

// === Finish writing file.
if (layout) {
  var page_file_path = ABOUT('new-file', '.html');
  fs.writeFileSync(page_file_path, $template.html());
  log(page_file_path);
}

function ABOUT(key) { // === Function that returns state.
  switch (key) {
    case 'name':
      return name;
    case 'dir':
      return dir;
    case 'new-file':
      return dir + '/' + name + '-' + arguments[1];
    case 'public-dir':
      return public_dir;
    case 'has-conditionals':
      return has_conditionals;
    case 'json-file':
      return path.join(ABOUT('dir'), "conditions.json");
    case 'layout':
      return $layout;
    default:
      error("!!! Unknown key: " + key);
  }
}

function merge_with_layout($layout, $template) {
    log(arguments[1].html);
  if (!$layout)
    log(arguments.length, $template.html);
  if (!$layout)
    return $template;
  if ($template('html').length === 0)
    return $template;

  _.each($template.children(), function (raw) {
    switch (raw.name) {
      case 'head':
        $layout('head').append($template(raw).html());
        $template(raw).remove();
        break;

      case 'tail':
        $layout('body').after($template(raw).html());
        $template(raw).remove();
        break;

      case 'top':
        $layout('body').prepend($template(raw).html());
        $template(raw).remove();
        break;

      case 'bottom':
        $layout('body').append($template(raw).html());
        $template(raw).remove();
        break;
    } // == switch
  });

  $layout('markup').replaceWith($template.html());
  return $layout;
} // === merge_markup

function conditionals_to_files($) {
  _.each($('when'), function (raw_when) {
    var conds = tag_when_to_object($, raw_when);
    merge_and_write_conds(conds);
    $(raw_when).remove();

    var $no_whens = $.load($.html());
    $no_whens('when').remove();

    var new_file_name = ABOUT('new-file', (is_partial($)) ? 'markup.' : '.') + conds.name + '.html';
    fs.writeFileSync( new_file_name, $no_whens.html());
  });

  return $;
} // === conditionals_to_files

function error(msg) {
  console.error(msg);
  process.exit(1);
}

function to_mustache(html) {
  return Hogan.compile(html, {asString: 1, delimiters: '[[ ]]'});
}

function get_comments($_) {
  return _.compact(_.map($_.contents(), function (node) {
    if (node.type === 'comment')
      return node.data;
  }));
}

function get_inline_vars(html) {
  var $ = cheerio(html);
  var attrs = {};
  _.each(get_comments($), function (data) {
    var lines  = data.split("\n");
    var key    = _.trim(lines.shift());
    attrs[key] = _.trim(lines.join("\n"));
  });

  return(attrs);
}

function compiled_to_compiler(code) {
  var f = new Function('Hogan', 'return new Hogan.Template(' + code + ');' );
  return f(Hogan);
}

function cheerio_to_mustache_to_html($) {
  var mustache        = to_mustache($.html());
  var inline_vars     = _.extend({}, get_inline_vars($.html()));

  return tag_template_to_script(
    compiled_to_compiler(mustache).render(inline_vars)
  );
} // === mustache_to_html

// ===  "template" tags can be deeply nested,
// so we process the inner-most ones first,
// then move on to the outer ones.
function tag_template_to_script($) {
  let tags = $('template');

  // === If no tags, we are done processing:
  if (tags.length === 0)
    return $;

  // === Find the inner-most (ie nested) template
  // tag and process it:
  let raw = _.find(tags, function (r) {
    return $('template', r).length === 0;
  });

  let type = $(raw).attr('type');
  if (!type || _.trim(type) === '')
    $(raw).attr('type', "application/template");
  let escaped = he.encode($(raw).html() || '', { useNamedReferences: false });
  $(raw).text(escaped);

  raw.name = "script";

  // === Recurse to handle outer template tags:
  return tag_template_to_script($);
}

function rel_path(file) {
  var public_dir = path.resolve(ABOUT('public-dir'));
  var full       = path.resolve(file);
  var fin        = full.replace(public_dir, '');
  if (fin.indexOf('/') !== 0)
    fin = '/' + fin;
  return fin;
}

function styles_to_tag($) {
  var new_path = ABOUT('new-file', 'style.css');
  var contents = to_html_and_remove($, $('style'));

  if (is_blank_string(contents))
    return $;

  fs.writeFileSync(new_path, contents);

  return append_to_tag(
    'head',
    $,
    '<link rel="stylesheet" type="text/css" href="' + rel_path(new_path) + '" />'
  );
}

function scripts_to_tag($) {
  var new_path = ABOUT('new-file', 'script.js');
  var contents = to_html_and_remove($, $('script'));

  if (is_blank_string(contents))
    return $;

  fs.writeFileSync(new_path, contents);

  return append_to_tag(
    'bottom',
    $,
    '<script type="application/javascript" src="' + rel_path(new_path)  + '"></script>'
  );
} // === scripts_to_tag

function to_html_and_remove($, coll) {
  return _.compact(_.map(coll, function (raw) {
    var html = $(raw).html();
    $(raw).remove();
    return html;
  })).join("\n");
}

function append_to_tag(tag_name, $, html) {
  var target = $(tag_name);
  if (!target[0]) {
    $.root().prepend('<' + tag_name + '></' + tag_name + '>');
    target = $(tag_name);
  }
  target.append(html);
  return $;
} // === append_to_tag

function markup_to_file($) {
  if (ABOUT('has-conditionals'))
    return $;

  fs.writeFileSync(
    ABOUT('new-file', (is_partial($)) ? 'markup.html' : '.html'),
    $.html()
  );
  return $;
}

// === Used to help other functions check if
// file has already been used before. Counterpart
// is `read_and_cache_file_name`.
function is_file_loaded(file) {
  return _.includes(FILES_USED, fs.realpathSync(file));
}

function read_and_cache_file_name(file) {
  let canon = fs.realpathSync(file);
  if (!_.includes(FILES_USED, canon))
    FILES_USED.push(canon);
  return fs.readFileSync(file).toString();
}

function var_pipeline() {
  var funcs = _.toArray(arguments);
  var v     = funcs.shift();

  return _.reduce(funcs, function (acc, f) {
    return f(acc);
  }, v);
}

function log(_args) {
  return console.log.apply(console, arguments);
}

function log_and_return(v) {
  log(v);
  return v;
}

function to_func_first() {
  var args = _.toArray(arguments);
  var func = args.shift();

  return function () {
    return func.apply( null, _.toArray(arguments).concat(args));
  };
}

function to_func() {
  var args = _.toArray(arguments);
  var func = args.shift();

  return function () {
    return func.apply(
      null,
      [].concat(args).concat(_.toArray(arguments))
    );
  };
}

function read_conds() {
  var file = ABOUT('json-file');
  if (!is_file(file))
    return {};
  var raw = fs.readFileSync(file).toString();
  if (is_blank_string(raw))
    return {};
  return JSON.parse(raw);
}

function merge_and_write_conds(new_conds) {
  var conds = read_conds();
  if (!conds[ABOUT('name')])
    conds[ABOUT('name')] = [];
  conds[ABOUT('name')].push(new_conds);
  fs.writeFileSync(ABOUT('json-file'), JSON.stringify(conds));
}

function tag_when_to_object($, raw) {
  var when_name = var_pipeline(
    $(raw).attr('name'),
    to_func_first(should_be, is_non_blank_string),
    _.trim
  );

  var conds = _.reduce($('val', raw), function (acc, raw_val) {
    var name = var_pipeline(
      $(raw_val).attr('name'),
      to_func_first(should_be, is_string),
      _.trim
    );

    var val  = var_pipeline(
      $(raw_val).attr('val'),
      to_func_first(should_be, is_string),
      _.trim
    );

    should_be(acc[name], is_null_or_undefined);
    acc[name] = val;
    return acc;
  }, {});

  return {conditions: conds, name: when_name}
}

function should_be(val, _funcs) {
  _funcs = _.toArray(arguments);
  val    = _funcs.shift();

  _.each(_funcs, function (f) {
    if (!f(val)) {
      console.error("!!! " + to_string(val) + ' should be: ' + to_string(f));
      process.exit(1);
    }
  });

  return val;
}

function to_string(v) {
  if (v === undefined)
    return 'undefined';
  if (v === null)
    return 'null';
  if (v === false)
    return 'false';
  if (v === true)
    return 'true';
  if (_.isFunction(v))
    return (v.name) ? v.name + ' (function)' : v.toString();
  if (_.isString(v))
    return '"' + v + '"';
  if (_.isArray(v))
    return '[' + _.map(_.toArray(v), to_string).join(', ') + '] (Array)';
  if (v.constructor === arguments.constructor)
    return '[' + _.map(_.toArray(v), to_string).join(', ') + '] (arguments)';
  return util.inspect(v);
}

function is_string(v) { return _.isString(v); }
function is_dir(v) { try { return fs.lstatSync(v).isDirectory(); } catch (e) { return false; } }
function is_file(v) { try { return fs.lstatSync(v).isFile(); } catch (e) { return false; } }
function is_blank_string(str) { return _.trim(str).length === 0; }
function is_non_blank_string(str) { return is_string(str) && !is_blank_string(str); }
function identity(v) { return v; }
function is_null_or_undefined(v) { return v === null || v === undefined; }
function is_partial($) { return $('html').length === 0; }

<commit_msg>Added: sort conditions by keys.<commit_after>"use strict";
/* jshint evil : true, esnext: true, globalstrict: true, undef: true */
/* global _ : true, console, require, process  */

const cheerio = require('cheerio');
const _       = require('lodash');
const Hogan   = require('hogan.js');
const fs      = require('fs');
const util    = require('util');
const path    = require('path');
const he      = require('he');
const SNIPPET_REUSE_TAGS = ['head', 'tail', 'top', 'bottom'];
const FILES_USED = [];

var args     = process.argv.slice(2);

switch (args.length) {
  case 3: break;
  case 4: break;
  default:
    console.error(to_string(args));
    console.error("!!! arguments.length incorrect. Try: [layout]  template_file  output_dir  public_path");
    process.exit(1);
}

var public_dir=args.pop();
var dir      = args.pop();
var template = args.pop();
var layout   = args.pop();

should_be(public_dir, is_string);
should_be(dir,        is_dir);
should_be(template,   is_file);
if (layout)
  should_be(layout,  is_file);

var name              = path.basename(template, '.html');
var template_contents = read_and_cache_file_name(template);
var layout_contents   = layout && fs.readFileSync(layout).toString();
var $layout           = layout && cheerio.load(layout_contents, {recognizeSelfClosing: true});
var $template         = cheerio.load(template_contents, {recognizeSelfClosing: true});
var has_conditionals  = $template('when').length > 0;
$template = var_pipeline(
  $template,
  scripts_to_tag,
  styles_to_tag,
  tag_template_to_script,
  conditionals_to_files,
  markup_to_file,
  layout ? to_func(merge_with_layout, $layout) : identity
);

// === Finish writing file.
if (layout) {
  var page_file_path = ABOUT('new-file', '.html');
  fs.writeFileSync(page_file_path, $template.html());
  log(page_file_path);
}

function ABOUT(key) { // === Function that returns state.
  switch (key) {
    case 'name':
      return name;
    case 'dir':
      return dir;
    case 'new-file':
      return dir + '/' + name + '-' + arguments[1];
    case 'public-dir':
      return public_dir;
    case 'has-conditionals':
      return has_conditionals;
    case 'json-file':
      return path.join(ABOUT('dir'), "conditions.json");
    case 'layout':
      return $layout;
    default:
      error("!!! Unknown key: " + key);
  }
}

function merge_with_layout($layout, $template) {
    log(arguments[1].html);
  if (!$layout)
    log(arguments.length, $template.html);
  if (!$layout)
    return $template;
  if ($template('html').length === 0)
    return $template;

  _.each($template.children(), function (raw) {
    switch (raw.name) {
      case 'head':
        $layout('head').append($template(raw).html());
        $template(raw).remove();
        break;

      case 'tail':
        $layout('body').after($template(raw).html());
        $template(raw).remove();
        break;

      case 'top':
        $layout('body').prepend($template(raw).html());
        $template(raw).remove();
        break;

      case 'bottom':
        $layout('body').append($template(raw).html());
        $template(raw).remove();
        break;
    } // == switch
  });

  $layout('markup').replaceWith($template.html());
  return $layout;
} // === merge_markup

function conditionals_to_files($) {
  _.each($('when'), function (raw_when) {
    var conds = tag_when_to_object($, raw_when);
    merge_and_write_conds(conds);
    $(raw_when).remove();

    var $no_whens = $.load($.html());
    $no_whens('when').remove();

    var new_file_name = ABOUT('new-file', (is_partial($)) ? 'markup.' : '.') + conds.name + '.html';
    fs.writeFileSync( new_file_name, $no_whens.html());
  });

  return $;
} // === conditionals_to_files

function error(msg) {
  console.error(msg);
  process.exit(1);
}

function to_mustache(html) {
  return Hogan.compile(html, {asString: 1, delimiters: '[[ ]]'});
}

function get_comments($_) {
  return _.compact(_.map($_.contents(), function (node) {
    if (node.type === 'comment')
      return node.data;
  }));
}

function get_inline_vars(html) {
  var $ = cheerio(html);
  var attrs = {};
  _.each(get_comments($), function (data) {
    var lines  = data.split("\n");
    var key    = _.trim(lines.shift());
    attrs[key] = _.trim(lines.join("\n"));
  });

  return(attrs);
}

function compiled_to_compiler(code) {
  var f = new Function('Hogan', 'return new Hogan.Template(' + code + ');' );
  return f(Hogan);
}

function cheerio_to_mustache_to_html($) {
  var mustache        = to_mustache($.html());
  var inline_vars     = _.extend({}, get_inline_vars($.html()));

  return tag_template_to_script(
    compiled_to_compiler(mustache).render(inline_vars)
  );
} // === mustache_to_html

// ===  "template" tags can be deeply nested,
// so we process the inner-most ones first,
// then move on to the outer ones.
function tag_template_to_script($) {
  let tags = $('template');

  // === If no tags, we are done processing:
  if (tags.length === 0)
    return $;

  // === Find the inner-most (ie nested) template
  // tag and process it:
  let raw = _.find(tags, function (r) {
    return $('template', r).length === 0;
  });

  let type = $(raw).attr('type');
  if (!type || _.trim(type) === '')
    $(raw).attr('type', "application/template");
  let escaped = he.encode($(raw).html() || '', { useNamedReferences: false });
  $(raw).text(escaped);

  raw.name = "script";

  // === Recurse to handle outer template tags:
  return tag_template_to_script($);
}

function rel_path(file) {
  var public_dir = path.resolve(ABOUT('public-dir'));
  var full       = path.resolve(file);
  var fin        = full.replace(public_dir, '');
  if (fin.indexOf('/') !== 0)
    fin = '/' + fin;
  return fin;
}

function styles_to_tag($) {
  var new_path = ABOUT('new-file', 'style.css');
  var contents = to_html_and_remove($, $('style'));

  if (is_blank_string(contents))
    return $;

  fs.writeFileSync(new_path, contents);

  return append_to_tag(
    'head',
    $,
    '<link rel="stylesheet" type="text/css" href="' + rel_path(new_path) + '" />'
  );
}

function scripts_to_tag($) {
  var new_path = ABOUT('new-file', 'script.js');
  var contents = to_html_and_remove($, $('script'));

  if (is_blank_string(contents))
    return $;

  fs.writeFileSync(new_path, contents);

  return append_to_tag(
    'bottom',
    $,
    '<script type="application/javascript" src="' + rel_path(new_path)  + '"></script>'
  );
} // === scripts_to_tag

function to_html_and_remove($, coll) {
  return _.compact(_.map(coll, function (raw) {
    var html = $(raw).html();
    $(raw).remove();
    return html;
  })).join("\n");
}

function append_to_tag(tag_name, $, html) {
  var target = $(tag_name);
  if (!target[0]) {
    $.root().prepend('<' + tag_name + '></' + tag_name + '>');
    target = $(tag_name);
  }
  target.append(html);
  return $;
} // === append_to_tag

function markup_to_file($) {
  if (ABOUT('has-conditionals'))
    return $;

  fs.writeFileSync(
    ABOUT('new-file', (is_partial($)) ? 'markup.html' : '.html'),
    $.html()
  );
  return $;
}

// === Used to help other functions check if
// file has already been used before. Counterpart
// is `read_and_cache_file_name`.
function is_file_loaded(file) {
  return _.includes(FILES_USED, fs.realpathSync(file));
}

function read_and_cache_file_name(file) {
  let canon = fs.realpathSync(file);
  if (!_.includes(FILES_USED, canon))
    FILES_USED.push(canon);
  return fs.readFileSync(file).toString();
}

function var_pipeline() {
  var funcs = _.toArray(arguments);
  var v     = funcs.shift();

  return _.reduce(funcs, function (acc, f) {
    return f(acc);
  }, v);
}

function log(_args) {
  return console.log.apply(console, arguments);
}

function log_and_return(v) {
  log(v);
  return v;
}

function to_func_first() {
  var args = _.toArray(arguments);
  var func = args.shift();

  return function () {
    return func.apply( null, _.toArray(arguments).concat(args));
  };
}

function to_func() {
  var args = _.toArray(arguments);
  var func = args.shift();

  return function () {
    return func.apply(
      null,
      [].concat(args).concat(_.toArray(arguments))
    );
  };
}

function read_conds() {
  var file = ABOUT('json-file');
  if (!is_file(file))
    return {};
  var raw = fs.readFileSync(file).toString();
  if (is_blank_string(raw))
    return {};
  return JSON.parse(raw);
}

function merge_and_write_conds(new_conds) {
  var conds = read_conds();
  if (!conds[ABOUT('name')])
    conds[ABOUT('name')] = [];
  conds[ABOUT('name')] = [].concat(conds[ABOUT('name')]).concat([new_conds]).sort(function (a,b) {
    return to_length(a.conditions) - to_length(b.conditions);
  });
  fs.writeFileSync(ABOUT('json-file'), JSON.stringify(conds));
}

function tag_when_to_object($, raw) {
  var when_name = var_pipeline(
    $(raw).attr('name'),
    to_func_first(should_be, is_non_blank_string),
    _.trim
  );

  var conds = _.reduce($('val', raw), function (acc, raw_val) {
    var name = var_pipeline(
      $(raw_val).attr('name'),
      to_func_first(should_be, is_string),
      _.trim
    );

    var val  = var_pipeline(
      $(raw_val).attr('val'),
      to_func_first(should_be, is_string),
      _.trim
    );

    should_be(acc[name], is_null_or_undefined);
    acc[name] = val;
    return acc;
  }, {});

  return {conditions: conds, name: when_name}
}

function should_be(val, _funcs) {
  _funcs = _.toArray(arguments);
  val    = _funcs.shift();

  _.each(_funcs, function (f) {
    if (!f(val)) {
      console.error("!!! " + to_string(val) + ' should be: ' + to_string(f));
      process.exit(1);
    }
  });

  return val;
}

function to_string(v) {
  if (v === undefined)
    return 'undefined';
  if (v === null)
    return 'null';
  if (v === false)
    return 'false';
  if (v === true)
    return 'true';
  if (_.isFunction(v))
    return (v.name) ? v.name + ' (function)' : v.toString();
  if (_.isString(v))
    return '"' + v + '"';
  if (_.isArray(v))
    return '[' + _.map(_.toArray(v), to_string).join(', ') + '] (Array)';
  if (v.constructor === arguments.constructor)
    return '[' + _.map(_.toArray(v), to_string).join(', ') + '] (arguments)';
  return util.inspect(v);
}

function is_string(v) { return _.isString(v); }
function is_dir(v) { try { return fs.lstatSync(v).isDirectory(); } catch (e) { return false; } }
function is_file(v) { try { return fs.lstatSync(v).isFile(); } catch (e) { return false; } }
function is_blank_string(str) { return _.trim(str).length === 0; }
function is_non_blank_string(str) { return is_string(str) && !is_blank_string(str); }
function identity(v) { return v; }
function is_null_or_undefined(v) { return v === null || v === undefined; }
function is_partial($) { return $('html').length === 0; }
function is_array(v) { return _.isArray(v); }

function sort_by_length(arr) {
  return arr.sort(function (a,b) {
    return to_length(a) - to_length(b);
  });
}

function to_length(v) {
  if (is_string(v) || is_array(v))
    return v.length;
  if (_.isPlainObject(v))
    return _.keys(v).length;

  console.error("!!! Invalid value: " + to_string(v));
  process.exit(1);
}

<|endoftext|>


<commit_before>var Datos = {

    estacionesSelectId: "estacionesSelect",

    Estaciones: {},

    EstacionSeleccionada: null,

    Eventos: null,

    EventosMapa: {
        Pin_Click: function (pin) {
            //Cacha el Click por cada pin
            Datos.SeleccionarPin(pin);
        }
    },

    SeleccionarPin: function (pin) {
        //analiza los datos del pin y manda a seleccionar la estación
        Datos.SeleccionarEstacion($(pin._htmlContent).attr('id').replace('estacion', ''));
    },

    SeleccionarEstacion: function (estacionNumero) {
        var estacion = Datos.Estaciones[estacionNumero];
        if (Datos.EstacionSeleccionada != estacion) {
            Datos.EstacionSeleccionada = estacion;
            //Selecciona en el Select la estación correspondiente (por si fue seleccionada en el mapa)
            if ($("#" + Datos.estacionesSelectId).val() != estacion.Numero) {
                $("#" + Datos.estacionesSelectId + " option").removeAttr("selected");
                $("#" + Datos.estacionesSelectId + " option[value=" + estacionNumero + "]").attr("selected", "selected");
            }
            Datos.Eventos.EstacionSeleccionada(estacion);
        }
    },

    CargarEstaciones: function () {
        var Params = {};
        Params.type = 'GET';
        Params.url = 'http://www.climapuebla.org.mx/ClientBin/Sysne-Clima-Portal-Svr-RIAService.svc/JSON/GetEstaciones';
        //Params.dataType = 'json';
        Params.success = function (res) {
            var data = $.parseJSON(res.responseText);
            for (var i in data.GetEstacionesResult.RootResults) {
                var estacion = data.GetEstacionesResult.RootResults[i];
                var htmlContent = "";
                if (!estacion.PosicionDatos || estacion.PosicionDatos != "") {
                    switch (estacion.PosicionDatos) {
                        case "I":
                            htmlContent = '

10

';
                            break;
                        case "S":
                            htmlContent = '

10

';
                            break;
                        case "A":
                            htmlContent = '

10

';
                            break;
                        case "D":
                        default:
                            htmlContent = '

10

';
                            break;
                    }
                }
                Datos.Estaciones[estacion.Numero] = estacion;
                var pin = Mapa.AgregarPin(htmlContent, estacion.Latitud, estacion.Longitud);
                /*Llenar el select*/
                $("#" + Datos.estacionesSelectId).append('<option value="' + estacion.Numero + '">' + estacion.Nombre + '</option>');
                $("#" + Datos.estacionesSelectId).change(function () {
                    Datos.SeleccionarEstacion($(this).val());
                });
            }
        };
        $.ajax(Params);
    },

    Limpiar: function () {

    },

    MostrarDato: function (numEstacion, htmlContent) {

    },

    CargarInfo: null,
    CargarCondicionesActuales: null,
    CargarTemperatura: null,
    CargarPrecipitacion: null,
    CargarViento: null,
    CargarHumedadRelativa: null,
    CargarPuntoDeRocio: null,
    CargarGraficas: null,
    CargarObservaciones: null,
    CargarRadiacionSolar: null,
    CargarSateliteNacional: null,
    CargarIR4: null,
    CargarRadar: null
}<commit_msg>Probando click de iPhone<commit_after>var Datos = {

    estacionesSelectId: "estacionesSelect",

    Estaciones: {},

    EstacionSeleccionada: null,

    Eventos: null,

    EventosMapa: {
        Pin_Click: function (pin) {
            alert($(pin._htmlContent).attr('id'));
            //Cacha el Click por cada pin
            Datos.SeleccionarPin(pin);
        }
    },

    SeleccionarPin: function (pin) {
        //analiza los datos del pin y manda a seleccionar la estación
        Datos.SeleccionarEstacion($(pin._htmlContent).attr('id').replace('estacion', ''));
    },

    SeleccionarEstacion: function (estacionNumero) {
        var estacion = Datos.Estaciones[estacionNumero];
        if (Datos.EstacionSeleccionada != estacion) {
            Datos.EstacionSeleccionada = estacion;
            //Selecciona en el Select la estación correspondiente (por si fue seleccionada en el mapa)
            if ($("#" + Datos.estacionesSelectId).val() != estacion.Numero) {
                $("#" + Datos.estacionesSelectId + " option").removeAttr("selected");
                $("#" + Datos.estacionesSelectId + " option[value=" + estacionNumero + "]").attr("selected", "selected");
            }
            Datos.Eventos.EstacionSeleccionada(estacion);
        }
    },

    CargarEstaciones: function () {
        var Params = {};
        Params.type = 'GET';
        Params.url = 'http://www.climapuebla.org.mx/ClientBin/Sysne-Clima-Portal-Svr-RIAService.svc/JSON/GetEstaciones';
        //Params.dataType = 'json';
        Params.success = function (res) {
            var data = $.parseJSON(res.responseText);
            for (var i in data.GetEstacionesResult.RootResults) {
                var estacion = data.GetEstacionesResult.RootResults[i];
                var htmlContent = "";
                if (!estacion.PosicionDatos || estacion.PosicionDatos != "") {
                    switch (estacion.PosicionDatos) {
                        case "I":
                            htmlContent = '

10

';
                            break;
                        case "S":
                            htmlContent = '

10

';
                            break;
                        case "A":
                            htmlContent = '

10

';
                            break;
                        case "D":
                        default:
                            htmlContent = '

10

';
                            break;
                    }
                }
                Datos.Estaciones[estacion.Numero] = estacion;
                var pin = Mapa.AgregarPin(htmlContent, estacion.Latitud, estacion.Longitud);
                /*Llenar el select*/
                $("#" + Datos.estacionesSelectId).append('<option value="' + estacion.Numero + '">' + estacion.Nombre + '</option>');
                $("#" + Datos.estacionesSelectId).change(function () {
                    Datos.SeleccionarEstacion($(this).val());
                });
            }
        };
        $.ajax(Params);
    },

    Limpiar: function () {

    },

    MostrarDato: function (numEstacion, htmlContent) {

    },

    CargarInfo: null,
    CargarCondicionesActuales: null,
    CargarTemperatura: null,
    CargarPrecipitacion: null,
    CargarViento: null,
    CargarHumedadRelativa: null,
    CargarPuntoDeRocio: null,
    CargarGraficas: null,
    CargarObservaciones: null,
    CargarRadiacionSolar: null,
    CargarSateliteNacional: null,
    CargarIR4: null,
    CargarRadar: null
}<|endoftext|>


<commit_before>// A selfie toggle button that can be attached to the document.
function GitHubSelfieButtons() {
  this.elem = $(
    '
' +
    '<button type="button" class="totallyAwesomeSelfieButton btn btn-default">' +
    '' +
    ' Selfie!' +
    '</button>' +
    '
');

  this.selfieButton = this.elem.find('.totallyAwesomeSelfieButton');
  this.videoPreview = null;

  // TODO: This passthrough sucks
  this.onselfie = function(isDynamic, video, canvas, ctx, callback) {};

  this.selfieButton
    .on('click', function() {
      if (this.videoPreview === null) {
        this.selfieButton.addClass('selected');
        this.showVideoPreview();
        this.videoPreview.startPreview();
      } else {
        this.videoPreview.destroy();
        this.videoPreview = null;
        this.selfieButton.removeClass('selected');
      }
    }.bind(this));
}

GitHubSelfieButtons.prototype = {
  insert: function(element) {
    // Be careful not to insert more than one!
    this.elem.eq(0).appendTo(element);
  },

  destroy: function() {
    this.elem.remove();
    this.selfieButton = null;
    if (this.videoPreview !== null) {
      this.videoPreview.destroy();
    }
    this.videoPreview = null;
    this.onselfie = null;
  },

  showVideoPreview: function() {
    if (this.videoPreview === null) {
      // Insert the preview before the buttons
      this.videoPreview = new GitHubSelfieVideoPreview();
      this.videoPreview.onselfie = this.onselfie;
      this.videoPreview.insert(this.elem);
    }
  },

  disableButton: function() {
    this.selfieButton.prop('disabled', true);
  },

  enableButton: function() {
    this.selfieButton.prop('disabled', false);
  }
};

// A selfie video preview and buttons that can be attached to the document.
function GitHubSelfieVideoPreview() {
  this.elem = $(
    '
' +
    '

' +
    '
' +
    '

' +
    '<video autoplay id="selfieVideo"></video>' +
    '
' +
    '

' +
    '<canvas id="selfieCanvas" class="hidden"></canvas>' +
    '
' +
    '<button type="button" class="selfiePhotoButton btn btn-sm selected">' +
    '' +
    ' Photo' +
    '</button>' +
    '<button type="button" class="selfieVideoButton btn btn-sm">' +
    '' +
    ' Video' +
    '</button>' +
    '
' +
    '<button type="button" class="selfieTakeButton btn btn-primary btn-sm">' +
    ' Take a selfie!' +
    '</button>' +
    '
'
  );

  this.onselfie = function(isDynamic, video, canvas, ctx, callback) {};

  this.videoElem = this.elem.find('video').get(0);
  this.canvasElem = this.elem.find('canvas').get(0);
  this.textOverlay = this.elem.find('.selfieVideoOverlay');
  this.counterContainer = this.elem.find('.counter-container');
  this.photoButton = this.elem.find('.selfiePhotoButton');
  this.photoButton
    .on('click', this.setSelfieType.bind(this));
  this.videoButton = this.elem.find('.selfieVideoButton');
  this.videoButton
    .on('click', this.setSelfieType.bind(this));
  this.takeButton = this.elem.find('.selfieTakeButton');
  this.takeButton
    .on('click', function() { this.onselfie(this.dynamic); }.bind(this));
  this.stream = null;
  this.dynamic = false; // dynamic == video
  // TODO: remember preference?

  // Turn off the preview when the page is not visible to save battery
  // and reduce the creepy feeling when your camera light is on because of
  // a page you forgot about.
  document.addEventListener("visibilitychange", function() {
    if (document.visibilityState === 'visible') {
      if (this.stream === null) {
        this.startPreview();
      }
    } else {
      this.stopPreview();
    }
  }.bind(this), false);
}

GitHubSelfieVideoPreview.prototype = {
  insert: function(element) {
    // Be careful not to insert more than one!
    this.elem.eq(0).insertAfter(element);
  },

  // Video (dynamic) needs to be resized smaller for preformnce
  resizeCanvas: function(isDynamic) {
    var scalefactor = 1 / (isDynamic ? 3 : 1);
    this.canvasElem.setAttribute('height', Math.round(this.videoElem.videoHeight * scalefactor));
    this.canvasElem.setAttribute('width', Math.round(this.videoElem.videoWidth * scalefactor));
  },

  setMessage: function(message) {
    if (this.textOverlay !== null) {
      this.textOverlay.text(message);
    }
  },

  destroy: function() {
    // No memory leaks here!
    this.stopPreview();
    this.elem.remove();
    this.videoElem = null;
    this.canvasElem = null;
    this.textOverlay = null;
    this.counterContainer = null;
    this.photoButton = null;
    this.videoButton = null;
    this.takeButton = null;
    this.onselfie = null;
  },

  setSelfieType: function(e) {
    if ((e.target === this.photoButton.get(0) &&
         !this.photoButton.hasClass('selected')) ||
        (e.target === this.videoButton.get(0) &&
         !this.videoButton.hasClass('selected'))) {
      this.videoButton.toggleClass('selected');
      this.photoButton.toggleClass('selected');
      this.dynamic = !this.dynamic;
    }
  },

  startPreview: function() {
    this.setMessage('Fetching camera stream...');

    var getUserMedia;
    if (typeof navigator.webkitGetUserMedia === 'function') {
      getUserMedia = navigator.webkitGetUserMedia.bind(navigator);
    } else if (typeof navigator.mozGetUserMedia === 'function') {
      getUserMedia = navigator.mozGetUserMedia.bind(navigator);
    } else {
      getUserMedia = function() { alert("Your browser does not support camera input!"); };
    }

    getUserMedia({video: true}, function(_stream) {
      this.setMessage('');
      this.stream = _stream;
      this.videoElem.src = window.URL.createObjectURL(_stream);
    }.bind(this), function(e) {
      this.setMessage("You don't have a camera available!");
    }.bind(this));
  },

  stopPreview: function() {
    if (this.stream !== null) {
      this.stream.getTracks()[0].stop();
    }
    this.stream = null;
  },

  snapSelfie: function(dynamic, takeSelfieCallback, fileCallback) {
    this.resizeCanvas(dynamic);
    var ctx = this.canvasElem.getContext('2d');
    this.selfieCountdown(
      3,
      takeSelfieCallback(this.videoElem, this.canvasElem, ctx, fileCallback));
  },

  selfieCountdown: function(count, callback) {
    if (count > 0) {
      this.counterContainer.html('
' + count + '
');
      setTimeout(function() {
        this.selfieCountdown(count - 1, callback);
      }.bind(this), 1000);
    } else {
      this.counterContainer.empty();
      if (!this.dynamic) {
        // Camera flash
        this.counterContainer.addClass('selfie-flash');
        setTimeout(function() {
          this.counterContainer.removeClass('selfie-flash');
        }.bind(this), 1100);
      }
      callback();
    }
  }
};

// Just to be tidy, only allow one of these to exist at once. Clean up
// the old ones if we add a new one.
var selfieSingleton = null;

function GitHubSelfies() {
  this.selfiesTaken = 0;
  // Milliseconds between frames in a GIF. Yields ~10fps.
  this.interval = 100;
  // Imgur client ID
  this.clientId = 'cc9df57988494ca';
  // This selector works on all pages
  this.insertAt = '.timeline-new-comment .form-actions:visible';
  this.buttons = null;
  this.textarea = null;
}

GitHubSelfies.prototype = {
  setupSelfieStream: function() {
    if (selfieSingleton !== null) {
      selfieSingleton.destroy();
      selfieSingleton = null;
    }
    selfieSingleton = this;

    var candidate = $(this.insertAt);
    if (candidate.length === 0) {
      // Try again later
      setTimeout(function() { this.setupSelfieStream(); }.bind(this), 250);
    } else {
      $('.form-actions-protip').hide();
      this.buttons = new GitHubSelfieButtons();
      this.buttons.insert(candidate);
      // TODO: this is too fragile!
      this.textarea = this.buttons.elem.parent().parent().find('textarea');
      this.setupEvents();
    }
  },

  destroy: function() {
    if (this.buttons !== null) {
      this.buttons.destroy();
    }
    this.buttons = null;
    this.textarea = null;
  },

  hideAllElements: function() {
    $('.github-selfies').addClass('hidden');
  },

  showAllElements: function() {
    $('.github-selfies').removeClass('hidden');
  },

  setupEvents: function() {
    this.buttons.onselfie = this.addSelfie.bind(this);
    $('.write-tab').on('click', this.showAllElements.bind(this));
    $('.preview-tab').on('click', this.hideAllElements.bind(this));
  },

  addSelfie: function(isDynamic) {
    var thisSelfieNumber = this.selfiesTaken++;

    this.addSelfiePlaceholder(thisSelfieNumber);
    this.buttons.videoPreview.snapSelfie(
      isDynamic,
      isDynamic ? this.dynamicSelfie.bind(this) : this.staticSelfie.bind(this),
      imageSuccess.bind(this));

    function imageSuccess (_imageData) {
      this.uploadSelfie(_imageData, success.bind(this), function(err) {
        this.buttons.videoPreview.setMessage("Something went wrong :-(");
        console.error("Error uploading selfie", err);
      }.bind(this));
    }

    function success (res) {
      this.replacePlaceholderInBody(thisSelfieNumber, res.data.link);
    }
  },

  staticSelfie: function(video, canvas, ctx, callback) {
    return function() {
      var imgBinary;

      ctx.save();
      // We used to flip this, but now the preview is just flipped in CSS.
      // Save the image non-mirrored so writing goes the right way.
      //ctx.translate(video.videoWidth, 0);
      //ctx.scale(-1, 1);
      ctx.drawImage(video, 0, 0, video.videoWidth, video.videoHeight, 0, 0, video.videoWidth, video.videoHeight);
      imgBinary = canvas.toDataURL('/image/jpeg', 1).split(',')[1];
      ctx.restore();
      callback(imgBinary);
    };
  },

  dynamicSelfie: function(video, canvas, ctx, callback) {
    return function() {
      var encoder = new GIFEncoder();
      var frame   = 0;
      var clock;

      encoder.setRepeat(0);
      encoder.setDelay(this.interval);
      encoder.start();

      clock = setInterval(function() {
        var videoWidth  = $(video).width();
        var totalFrames = 20;
        var binaryGif;
        var dataUrl;

        if (frame >= totalFrames) {
          encoder.finish();
          binaryGif = encoder.stream().getData();
          dataUrl   = 'data:image/gif;base64,'+ encode64(binaryGif);
          callback(encode64(binaryGif));
          clearInterval(clock);
          $('.selfieProgress').css('width', 0);
        } else {
          ctx.save();
          // We used to flip this, but now the preview is just flipped in CSS.
          // Save the image non-mirrored so writing goes the right way.
          //ctx.translate(video.videoWidth / 3, 0);
          //ctx.scale(-1, 1);
          ctx.drawImage(video, 0, 0, video.videoWidth, video.videoHeight, 0, 0, video.videoWidth / 3, video.videoHeight / 3);
          encoder.addFrame(ctx);
          ctx.restore();
          frame++;
          $('.selfieProgress').css('width', (videoWidth / totalFrames) * frame);
        }
      }, this.interval);
    };
  },

  uploadSelfie: function(imageData, successCb, errorCb) {
    $.ajax({
      url  : 'https://api.imgur.com/3/upload',
      type : 'POST',
      beforeSend: function(xhr) {
        xhr.setRequestHeader('Authorization', 'Client-ID ' + this.clientId);
      }.bind(this),
      data: {
        type  : 'base64',
        image : imageData
      },
      dataType : 'json',
      success  : successCb,
      error    : errorCb
    });
  },

  addSelfiePlaceholder: function(number) {
    var currentContents = this.textarea.val();
    if (currentContents !== '') {
      this.textarea.val(currentContents + '\n');
    }
    this.textarea.val(currentContents + '[[selfie-placeholder-' + number + ']]\n');
  },

  replacePlaceholderInBody: function(number, link) {
    var toReplace = '[[selfie-placeholder-' + number + ']]';

    this.textarea.val(
      this.textarea.val()
        .replace(toReplace, '![selfie-' + number + '](' + link + ')'));
    this.textarea.focus();
    this.textarea.get(0)
      .setSelectionRange(this.textarea.textLength, this.textarea.textLength);
  }
};
<commit_msg>Link embedded selfies to this repo, for self promotion.<commit_after>// A selfie toggle button that can be attached to the document.
function GitHubSelfieButtons() {
  this.elem = $(
    '
' +
    '<button type="button" class="totallyAwesomeSelfieButton btn btn-default">' +
    '' +
    ' Selfie!' +
    '</button>' +
    '
');

  this.selfieButton = this.elem.find('.totallyAwesomeSelfieButton');
  this.videoPreview = null;

  // TODO: This passthrough sucks
  this.onselfie = function(isDynamic, video, canvas, ctx, callback) {};

  this.selfieButton
    .on('click', function() {
      if (this.videoPreview === null) {
        this.selfieButton.addClass('selected');
        this.showVideoPreview();
        this.videoPreview.startPreview();
      } else {
        this.videoPreview.destroy();
        this.videoPreview = null;
        this.selfieButton.removeClass('selected');
      }
    }.bind(this));
}

GitHubSelfieButtons.prototype = {
  insert: function(element) {
    // Be careful not to insert more than one!
    this.elem.eq(0).appendTo(element);
  },

  destroy: function() {
    this.elem.remove();
    this.selfieButton = null;
    if (this.videoPreview !== null) {
      this.videoPreview.destroy();
    }
    this.videoPreview = null;
    this.onselfie = null;
  },

  showVideoPreview: function() {
    if (this.videoPreview === null) {
      // Insert the preview before the buttons
      this.videoPreview = new GitHubSelfieVideoPreview();
      this.videoPreview.onselfie = this.onselfie;
      this.videoPreview.insert(this.elem);
    }
  },

  disableButton: function() {
    this.selfieButton.prop('disabled', true);
  },

  enableButton: function() {
    this.selfieButton.prop('disabled', false);
  }
};

// A selfie video preview and buttons that can be attached to the document.
function GitHubSelfieVideoPreview() {
  this.elem = $(
    '
' +
    '

' +
    '
' +
    '

' +
    '<video autoplay id="selfieVideo"></video>' +
    '
' +
    '

' +
    '<canvas id="selfieCanvas" class="hidden"></canvas>' +
    '
' +
    '<button type="button" class="selfiePhotoButton btn btn-sm selected">' +
    '' +
    ' Photo' +
    '</button>' +
    '<button type="button" class="selfieVideoButton btn btn-sm">' +
    '' +
    ' Video' +
    '</button>' +
    '
' +
    '<button type="button" class="selfieTakeButton btn btn-primary btn-sm">' +
    ' Take a selfie!' +
    '</button>' +
    '
'
  );

  this.onselfie = function(isDynamic, video, canvas, ctx, callback) {};

  this.videoElem = this.elem.find('video').get(0);
  this.canvasElem = this.elem.find('canvas').get(0);
  this.textOverlay = this.elem.find('.selfieVideoOverlay');
  this.counterContainer = this.elem.find('.counter-container');
  this.photoButton = this.elem.find('.selfiePhotoButton');
  this.photoButton
    .on('click', this.setSelfieType.bind(this));
  this.videoButton = this.elem.find('.selfieVideoButton');
  this.videoButton
    .on('click', this.setSelfieType.bind(this));
  this.takeButton = this.elem.find('.selfieTakeButton');
  this.takeButton
    .on('click', function() { this.onselfie(this.dynamic); }.bind(this));
  this.stream = null;
  this.dynamic = false; // dynamic == video
  // TODO: remember preference?

  // Turn off the preview when the page is not visible to save battery
  // and reduce the creepy feeling when your camera light is on because of
  // a page you forgot about.
  document.addEventListener("visibilitychange", function() {
    if (document.visibilityState === 'visible') {
      if (this.stream === null) {
        this.startPreview();
      }
    } else {
      this.stopPreview();
    }
  }.bind(this), false);
}

GitHubSelfieVideoPreview.prototype = {
  insert: function(element) {
    // Be careful not to insert more than one!
    this.elem.eq(0).insertAfter(element);
  },

  // Video (dynamic) needs to be resized smaller for preformnce
  resizeCanvas: function(isDynamic) {
    var scalefactor = 1 / (isDynamic ? 3 : 1);
    this.canvasElem.setAttribute('height', Math.round(this.videoElem.videoHeight * scalefactor));
    this.canvasElem.setAttribute('width', Math.round(this.videoElem.videoWidth * scalefactor));
  },

  setMessage: function(message) {
    if (this.textOverlay !== null) {
      this.textOverlay.text(message);
    }
  },

  destroy: function() {
    // No memory leaks here!
    this.stopPreview();
    this.elem.remove();
    this.videoElem = null;
    this.canvasElem = null;
    this.textOverlay = null;
    this.counterContainer = null;
    this.photoButton = null;
    this.videoButton = null;
    this.takeButton = null;
    this.onselfie = null;
  },

  setSelfieType: function(e) {
    if ((e.target === this.photoButton.get(0) &&
         !this.photoButton.hasClass('selected')) ||
        (e.target === this.videoButton.get(0) &&
         !this.videoButton.hasClass('selected'))) {
      this.videoButton.toggleClass('selected');
      this.photoButton.toggleClass('selected');
      this.dynamic = !this.dynamic;
    }
  },

  startPreview: function() {
    this.setMessage('Fetching camera stream...');

    var getUserMedia;
    if (typeof navigator.webkitGetUserMedia === 'function') {
      getUserMedia = navigator.webkitGetUserMedia.bind(navigator);
    } else if (typeof navigator.mozGetUserMedia === 'function') {
      getUserMedia = navigator.mozGetUserMedia.bind(navigator);
    } else {
      getUserMedia = function() { alert("Your browser does not support camera input!"); };
    }

    getUserMedia({video: true}, function(_stream) {
      this.setMessage('');
      this.stream = _stream;
      this.videoElem.src = window.URL.createObjectURL(_stream);
    }.bind(this), function(e) {
      this.setMessage("You don't have a camera available!");
    }.bind(this));
  },

  stopPreview: function() {
    if (this.stream !== null) {
      this.stream.getTracks()[0].stop();
    }
    this.stream = null;
  },

  snapSelfie: function(dynamic, takeSelfieCallback, fileCallback) {
    this.resizeCanvas(dynamic);
    var ctx = this.canvasElem.getContext('2d');
    this.selfieCountdown(
      3,
      takeSelfieCallback(this.videoElem, this.canvasElem, ctx, fileCallback));
  },

  selfieCountdown: function(count, callback) {
    if (count > 0) {
      this.counterContainer.html('
' + count + '
');
      setTimeout(function() {
        this.selfieCountdown(count - 1, callback);
      }.bind(this), 1000);
    } else {
      this.counterContainer.empty();
      if (!this.dynamic) {
        // Camera flash
        this.counterContainer.addClass('selfie-flash');
        setTimeout(function() {
          this.counterContainer.removeClass('selfie-flash');
        }.bind(this), 1100);
      }
      callback();
    }
  }
};

// Just to be tidy, only allow one of these to exist at once. Clean up
// the old ones if we add a new one.
var selfieSingleton = null;

function GitHubSelfies() {
  this.selfiesTaken = 0;
  // Milliseconds between frames in a GIF. Yields ~10fps.
  this.interval = 100;
  // Imgur client ID
  this.clientId = 'cc9df57988494ca';
  // This selector works on all pages
  this.insertAt = '.timeline-new-comment .form-actions:visible';
  this.buttons = null;
  this.textarea = null;
}

GitHubSelfies.prototype = {
  setupSelfieStream: function() {
    if (selfieSingleton !== null) {
      selfieSingleton.destroy();
      selfieSingleton = null;
    }
    selfieSingleton = this;

    var candidate = $(this.insertAt);
    if (candidate.length === 0) {
      // Try again later
      setTimeout(function() { this.setupSelfieStream(); }.bind(this), 250);
    } else {
      $('.form-actions-protip').hide();
      this.buttons = new GitHubSelfieButtons();
      this.buttons.insert(candidate);
      // TODO: this is too fragile!
      this.textarea = this.buttons.elem.parent().parent().find('textarea');
      this.setupEvents();
    }
  },

  destroy: function() {
    if (this.buttons !== null) {
      this.buttons.destroy();
    }
    this.buttons = null;
    this.textarea = null;
  },

  hideAllElements: function() {
    $('.github-selfies').addClass('hidden');
  },

  showAllElements: function() {
    $('.github-selfies').removeClass('hidden');
  },

  setupEvents: function() {
    this.buttons.onselfie = this.addSelfie.bind(this);
    $('.write-tab').on('click', this.showAllElements.bind(this));
    $('.preview-tab').on('click', this.hideAllElements.bind(this));
  },

  addSelfie: function(isDynamic) {
    var thisSelfieNumber = this.selfiesTaken++;

    this.addSelfiePlaceholder(thisSelfieNumber);
    this.buttons.videoPreview.snapSelfie(
      isDynamic,
      isDynamic ? this.dynamicSelfie.bind(this) : this.staticSelfie.bind(this),
      imageSuccess.bind(this));

    function imageSuccess (_imageData) {
      this.uploadSelfie(_imageData, success.bind(this), function(err) {
        this.buttons.videoPreview.setMessage("Something went wrong :-(");
        console.error("Error uploading selfie", err);
      }.bind(this));
    }

    function success (res) {
      this.replacePlaceholderInBody(thisSelfieNumber, res.data.link);
    }
  },

  staticSelfie: function(video, canvas, ctx, callback) {
    return function() {
      var imgBinary;

      ctx.save();
      // We used to flip this, but now the preview is just flipped in CSS.
      // Save the image non-mirrored so writing goes the right way.
      //ctx.translate(video.videoWidth, 0);
      //ctx.scale(-1, 1);
      ctx.drawImage(video, 0, 0, video.videoWidth, video.videoHeight, 0, 0, video.videoWidth, video.videoHeight);
      imgBinary = canvas.toDataURL('/image/jpeg', 1).split(',')[1];
      ctx.restore();
      callback(imgBinary);
    };
  },

  dynamicSelfie: function(video, canvas, ctx, callback) {
    return function() {
      var encoder = new GIFEncoder();
      var frame   = 0;
      var clock;

      encoder.setRepeat(0);
      encoder.setDelay(this.interval);
      encoder.start();

      clock = setInterval(function() {
        var videoWidth  = $(video).width();
        var totalFrames = 20;
        var binaryGif;
        var dataUrl;

        if (frame >= totalFrames) {
          encoder.finish();
          binaryGif = encoder.stream().getData();
          dataUrl   = 'data:image/gif;base64,'+ encode64(binaryGif);
          callback(encode64(binaryGif));
          clearInterval(clock);
          $('.selfieProgress').css('width', 0);
        } else {
          ctx.save();
          // We used to flip this, but now the preview is just flipped in CSS.
          // Save the image non-mirrored so writing goes the right way.
          //ctx.translate(video.videoWidth / 3, 0);
          //ctx.scale(-1, 1);
          ctx.drawImage(video, 0, 0, video.videoWidth, video.videoHeight, 0, 0, video.videoWidth / 3, video.videoHeight / 3);
          encoder.addFrame(ctx);
          ctx.restore();
          frame++;
          $('.selfieProgress').css('width', (videoWidth / totalFrames) * frame);
        }
      }, this.interval);
    };
  },

  uploadSelfie: function(imageData, successCb, errorCb) {
    $.ajax({
      url  : 'https://api.imgur.com/3/upload',
      type : 'POST',
      beforeSend: function(xhr) {
        xhr.setRequestHeader('Authorization', 'Client-ID ' + this.clientId);
      }.bind(this),
      data: {
        type  : 'base64',
        image : imageData
      },
      dataType : 'json',
      success  : successCb,
      error    : errorCb
    });
  },

  addSelfiePlaceholder: function(number) {
    var currentContents = this.textarea.val();
    if (currentContents !== '') {
      this.textarea.val(currentContents + '\n');
    }
    this.textarea.val(currentContents + '[[selfie-placeholder-' + number + ']]\n');
  },

  replacePlaceholderInBody: function(number, link) {
    var toReplace = '[[selfie-placeholder-' + number + ']]';

    this.textarea.val(
      this.textarea.val()
        .replace(toReplace, '[![selfie-' + number + '](' + link + ')](https://github.com/thieman/github-selfies/)'));
    this.textarea.focus();
    this.textarea.get(0)
      .setSelectionRange(this.textarea.textLength, this.textarea.textLength);
  }
};
<|endoftext|>


<commit_before>/*global define, window */
// blame type structure

/*
 * contract is composed from the test and a label
 * type is composed from a contract and the type's name
 *
 * Notes:
 *  * Think about combining contracts, it could be reused to provide a form of subtyping
 *  * A cast from * to Num -> Num should be split into two casts, * => * -> * => Num -> Num,
 *    recall what blame mentions about it in the publications.
 *  * Figure out if representing sub types is needed, and how it should be done.
 *    - For example, you could represent a type as a partial order, and a set of contracts,
 *      and then just test those contracts in bulk (although this is probably not needed)
 *  * Once you lay down the foundation you should start writing a parser for type definitions,
 *    and start testing some type performance
 *
 *  * Add some **polymorphic** types
 */
define('blame', [], function () {
  'use strict';

  // TODO Change blame into a polyfill function
  function blame () {
    // Polyfilling the global object
    var e;
    for (e in blame) {
      if (blame.hasOwnProperty(e)) {
        window[e] = blame[e];
      }
    }
  }

  var Type = function(name, contract) {
    if (!(this instanceof Type)) {
      return new Type(name, contract);
    }

    this.name = String(name);
    this.contract = contract;

    // TODO: Consider removing this
    //Object.freeze(this);
  };

  // Shortcut to define a type
  function typedef (identifier, contract) {
    blame[identifier] = new Type(identifier, contract);
  }

  function ground(name) {
    return function (e) {
      return (typeof e === name);
    };
  }

  // A simple first order contract
  function contract(test) {
    return function(value, label) {
      var result = test(value);

      if (!result) {
        throw new Error(label);
      }
      return value;
    };
  }

  typedef('Num', contract(ground('number')));
  typedef('Str', contract(ground('string')));
  typedef('Bool', contract(ground('boolean')));

  function wrap(type, value, label) {
    // In Future it should be able to parse the type
    // And generate the label
    return type.contract(value, label);
  }

  // Testing if an argument is indeed a function;
  function isFunction(func) {
    var O = {};
    return func && O.toString.call(func) === '[object Function]';
  }

  // Compose two contracts
  // Inject the context passed to compose into both methods.
  function compose(f, g) {
    return function(value, label) {
      return g.call(this, f.call(this, value, label), label);
    };
  }

  // * => *
  typedef('Fun', contract(isFunction));

  function TFun(A, B) {
    if(!(A instanceof Type)) {
      throw new TypeError('A is not a Type');
    }

    if(!(B instanceof Type)) {
      throw new TypeError('B is not a Type');
    }

    var name = A.name + ' -> ' + B.name;
    // Not sure if I should perform composition on whole types, not just contracts
    var new_contract = compose.call(this, blame.Fun.contract, function(func, label) {
      var negated = '~' + label;
      var that = this;

      // This will be replaced by a Proxy
      return function(e) {
        return B.contract.call(that, func(A.contract.call(that, e, negated)), label, true);
      };
    });

    return new Type(name, new_contract);
  }

  // Adding polymorphic types
  // Forall('X', Fun(Tyvar('X'), Tyvar('X')))

  // Can a function forall('X', 'X -> X') be cast to 'Num -> Num'
  // It is more concerning about types...

  // I need to get a pair of functions, one for sealing and one for unsealing

  function polymorphic (func, label) {
    // How can I possibly make the function completely secure?
    return (function () {
      var token = Object.create(null);
      Object.freeze(token);
      var keep;

      // Sealing function
      var seal = function (x) {
        keep = x;
        return token;
      };

      // Unsealing function
      var unseal = function (x) {
        if (x === token) {
          return keep;
        }
        throw new Error(label);
      };

      return function(x) {
        return unseal(func(seal(x)));
      };
    }());
  }

  // Forall just ignore the argument
  // How can I make the type definition?
  function TForall(X) {
    var name = 'forall ' + X + ', ' + X + ' -> ' + X;
    return new Type(name, compose(blame.Fun.contract, polymorphic));
  }

  // Forall constructor
  var Forall = function(X, type) {
    if (!(this instanceof Forall)) {
      return new Forall(X, type);
    }

    // Consider checking for the type
    var name = 'forall ' + X + ', ' + type.name;

    // Create secure storage for keeps:
    (function(obj) {
      var tokens = Object.create(null);
      var token = Object.create(null);
      Object.freeze(token);
      tokens[X] = token;
      var storage = Object.create(null);

      obj.have = function(name, value) {
        return tokens[name] && tokens[name] === value && true;
      };

      obj.get = function(name, value) {
        if (tokens[name] === value) {
          return storage[name];
        }
      };

      obj.keep = function(name, value) {
        if (tokens[name]) {
          if (storage[name]) {
            // Return token, only if the values are identical
            if (storage[name] === value) {
              return tokens[name];
            }
          }
          storage[name] = value;
          return tokens[name];
        }
      };
    }(this));

    Type.call(this, name, type.contract);
  };

  // Tyvar constructor
  var Tyvar = function(name) {
    if (!(this instanceof Tyvar)) {
      return new Tyvar(name);
    }

    var ty_contract = function(value, label, isLast) {
      if (!(this instanceof Forall)) {
        // TODO: Figure what to throw later
        throw new Error();
      }
      // Attempt to unseal the value:
      if(isLast) {
        var result = this.have(name, value);
        if (!result) {
          throw new Error(label);
        }
        return this.get(name, value);
      }
      // Attempt to seal the value:
      var token = this.keep(name, value);
      if (!token) {
        throw new Error(label);
      }
      return token;
    };

    Type.call(this, name, ty_contract);
  };

  // TODO: Consider changing the prototype chain to forall -> (Fun|TFun) -> Type
  Forall.prototype = Object.create(Type.prototype);
  Tyvar.prototype = Object.create(Type.prototype);

  // Exporting
  blame.wrap = wrap;
  blame.TFun = TFun;
  blame.TForall = TForall;
  blame.Forall = Forall;
  blame.Tyvar = Tyvar;

  return blame;
});

// vim: set ts=2 sw=2 sts=2 et :
<commit_msg>Add simple implementation for nary functions<commit_after>/*global define, window */
// blame type structure

/*
 * contract is composed from the test and a label
 * type is composed from a contract and the type's name
 *
 * Notes:
 *  * Think about combining contracts, it could be reused to provide a form of subtyping
 *  * A cast from * to Num -> Num should be split into two casts, * => * -> * => Num -> Num,
 *    recall what blame mentions about it in the publications.
 *  * Figure out if representing sub types is needed, and how it should be done.
 *    - For example, you could represent a type as a partial order, and a set of contracts,
 *      and then just test those contracts in bulk (although this is probably not needed)
 *  * Once you lay down the foundation you should start writing a parser for type definitions,
 *    and start testing some type performance
 *
 *  * Add some **polymorphic** types
 */
define('blame', [], function () {
  'use strict';

  // TODO Change blame into a polyfill function
  function blame () {
    // Polyfilling the global object
    var e;
    for (e in blame) {
      if (blame.hasOwnProperty(e)) {
        window[e] = blame[e];
      }
    }
  }

  var Type = function(name, contract) {
    if (!(this instanceof Type)) {
      return new Type(name, contract);
    }

    this.name = String(name);
    this.contract = contract;

    // TODO: Consider removing this
    //Object.freeze(this);
  };

  // Shortcut to define a type
  function typedef (identifier, contract) {
    blame[identifier] = new Type(identifier, contract);
  }

  function ground(name) {
    return function (e) {
      return (typeof e === name);
    };
  }

  // A simple first order contract
  function contract(test) {
    return function(value, label) {
      var result = test(value);

      if (!result) {
        throw new Error(label);
      }
      return value;
    };
  }

  typedef('Num', contract(ground('number')));
  typedef('Str', contract(ground('string')));
  typedef('Bool', contract(ground('boolean')));

  function wrap(type, value, label) {
    // In Future it should be able to parse the type
    // And generate the label
    return type.contract(value, label);
  }

  // Testing if an argument is indeed a function;
  function isFunction(func) {
    var O = {};
    return func && O.toString.call(func) === '[object Function]';
  }

  // Compose two contracts
  // Inject the context passed to compose into both methods.
  function compose(f, g) {
    return function(value, label) {
      return g.call(this, f.call(this, value, label), label);
    };
  }

  // * => *
  typedef('Fun', contract(isFunction));

  // TODO: Add support for n-ary function definitions
  function TFun() {
    var args = Array.prototype.slice.call(arguments || []);
    var names = [];

    if (args.length < 2) {
      throw new Error('Not enough arguments');
    }

    args.forEach(function (type, index) {
      if (!(type instanceof Type)) {
        throw new TypeError('argument ' + index + ' is not a Type');
      }
      names.push( '(' + type.name + ')');
    });

    var domain = args.slice(0, -1);
    var range = args[args.length - 1];

    var name = names.join(' -> ');
    // Not sure if I should perform composition on whole types, not just contracts
    var new_contract = compose.call(this, blame.Fun.contract, function(func, label) {
      var negated = '~' + label;
      var that = this;

      // This will be replaced by a Proxy
      return function() {
        var fun_args = Array.prototype.slice.call(arguments || []);
        var wrapped_args = [];

        fun_args.forEach(function (arg, index) {
          if (index < domain.length) {
            wrapped_args.push(domain[index].contract.call(that, arg, negated));
          }
          else {
            wrapped_args.push(arg);
          }
        });

        return range.contract.call(that, func.apply(undefined, wrapped_args), label, true);
      };
    });

    return new Type(name, new_contract);
  }

  // Adding polymorphic types
  // Forall('X', Fun(Tyvar('X'), Tyvar('X')))

  // Can a function forall('X', 'X -> X') be cast to 'Num -> Num'
  // It is more concerning about types...

  // I need to get a pair of functions, one for sealing and one for unsealing

  function polymorphic (func, label) {
    // How can I possibly make the function completely secure?
    return (function () {
      var token = Object.create(null);
      Object.freeze(token);
      var keep;

      // Sealing function
      var seal = function (x) {
        keep = x;
        return token;
      };

      // Unsealing function
      var unseal = function (x) {
        if (x === token) {
          return keep;
        }
        throw new Error(label);
      };

      return function(x) {
        return unseal(func(seal(x)));
      };
    }());
  }

  // Forall just ignore the argument
  // How can I make the type definition?
  function TForall(X) {
    var name = 'forall ' + X + ', ' + X + ' -> ' + X;
    return new Type(name, compose(blame.Fun.contract, polymorphic));
  }

  // Forall constructor
  var Forall = function(X, type) {
    if (!(this instanceof Forall)) {
      return new Forall(X, type);
    }

    // Consider checking for the type
    var name = 'forall ' + X + ', ' + type.name;

    // Create secure storage for keeps:
    (function(obj) {
      var tokens = Object.create(null);
      var token = Object.create(null);
      Object.freeze(token);
      tokens[X] = token;
      var storage = Object.create(null);

      obj.have = function(name, value) {
        return tokens[name] && tokens[name] === value && true;
      };

      obj.get = function(name, value) {
        if (tokens[name] === value) {
          return storage[name];
        }
      };

      obj.keep = function(name, value) {
        if (tokens[name]) {
          if (storage[name]) {
            // Return token, only if the values are identical
            if (storage[name] === value) {
              return tokens[name];
            }
          }
          storage[name] = value;
          return tokens[name];
        }
      };
    }(this));

    Type.call(this, name, type.contract);
  };

  // Tyvar constructor
  var Tyvar = function(name) {
    if (!(this instanceof Tyvar)) {
      return new Tyvar(name);
    }

    var ty_contract = function(value, label, isLast) {
      if (!(this instanceof Forall)) {
        // TODO: Figure what to throw later
        throw new Error();
      }
      // Attempt to unseal the value:
      if(isLast) {
        var result = this.have(name, value);
        if (!result) {
          throw new Error(label);
        }
        return this.get(name, value);
      }
      // Attempt to seal the value:
      var token = this.keep(name, value);
      if (!token) {
        throw new Error(label);
      }
      return token;
    };

    Type.call(this, name, ty_contract);
  };

  // TODO: Consider changing the prototype chain to forall -> (Fun|TFun) -> Type
  Forall.prototype = Object.create(Type.prototype);
  Tyvar.prototype = Object.create(Type.prototype);

  // Exporting
  blame.wrap = wrap;
  blame.TFun = TFun;
  blame.TForall = TForall;
  blame.Forall = Forall;
  blame.Tyvar = Tyvar;

  return blame;
});

// vim: set ts=2 sw=2 sts=2 et :
<|endoftext|>


<commit_before>"use strict";
var p = require('path')
var Promise = require('bluebird')
var prettyBytes = require('pretty-bytes')
var util = require('util')
var moment = require('moment')
var utils = require('./utils.js')

var debug = require('debug')('explorer:tree')
var fs = Promise.promisifyAll(require('fs'))

/**
 * Build Breadcrumb from paths
 * @param string root
 * @param string path
 * @return array of objects {path, name} where name will be linked to path
 */
var buildBreadcrumb = function(root, path) {
  var breadcrumbs = [{path: root, name: root}]

  if(!path) {
    return breadcrumbs
  }

  let paths = path.replace(root, '')
    .split('/')
    .filter(function(v) { return v != '' })

  for(let i in paths) {
    breadcrumbs[parseInt(i)+1] = {
      path: p.join(breadcrumbs[i].path, paths[i]),
      name: paths[i]
    }
  }

  return breadcrumbs
}

/**
 * @param string file
 * @param object root (@see pathInfo)
 * @param object options:
 *   - int maxDepth (10)
 *   - function filter (@see noDotFiles)
 */
var directorySize = function(file, root, options) {

  var filePath = p.resolve(root.path, file)
  var depth = file.split(p.sep).length

  if(root.depth < depth) {
    root.depth = depth
  }

  if(root.depth >= options.maxDepth) {
    root.depth = Infinity
    return Promise.resolve(root)
  }

  return fs.statAsync(filePath)
  .then(function(stat) {
    if(stat.isDirectory()) {
      return fs.readdirAsync(filePath)
      .filter(options.filter, {concurrency: options.concurrency})
      .filter(options.skip, {concurrency: options.concurrency})
      .each(v => directorySize(p.join(file, v), root, options))
    } else {
      root.size += stat.size
      return Promise.resolve(root)
    }
  })
  .catch(function(err) {

    if(err.code != 'EACCES' && err.code != 'ENOENT') {
      return Promise.reject(err)
    }

    if(err.code == 'EACCES')
      console.error('No file access (check rights)', filePath)
    else
      console.error('No file entry (file does not exist ?!)', filePath)

    return Promise.resolve(root)
  })
}

var recursiveReaddir = function(root, options) {
  return fs.readdirAsync(root)
  .filter(options.filter, {concurrency: options.concurrency})
  .filter(options.skip, {concurrency: options.concurrency})
  .map(function(f) {
    var path = p.join(root, f)

    return fs.statAsync(path)
    .then(function(stat) {

      if(options.depth > options.maxDepth)
        return path

      if(stat.isDirectory()) {
        options.depth++
        return recursiveReaddir(path, options)
      }

      return path
    })
  }).then(function(paths) {
    paths.push(root)
    return [].concat.apply([], paths)
  })
}

/**
 * Handles path argument, readdir if it's a string or return Promise.all
 * @param mixed path
 * @return array Promises
 */
var paths = function(path, options) {
  if(options.recursive === true) {
    return recursiveReaddir(path, util._extend(options, {root: path, depth: 1}))
    .map(function(e) {
      return p.relative(path, e)
    })
  } else if(typeof path === 'string') {
    return fs.readdirAsync(path)
  } else if(util.isArray(path)) {
    return Promise.all(path)
  }

  throw new TypeError('Path must be defined')
}

/**
 * @param string path
 * @param object options
 *   - int page (0) - the current page
 *   - int maxDepth (10) - max depth for directory size computation
 *   - int limit (100)
 *   - int concurrency (100)
 *   @see prepareTree
 * @return Promise
 * @catchable
 */
var tree = function(path, options) {

  if(!options) { options = {} }

  options.page = parseInt(options.page) || 1
  options.limit = parseInt(options.limit) || 100
  options.maxDepth = parseInt(options.maxDepth) || 10
  options.concurrency = parseInt(options.concurrency) || 100
  options.cacheTTL = options.cacheTTL || 86400 //24 hours

  options.skip = options.skip ? options.skip : function(v) { return true; }
  options.filter = utils.noDotFiles

  debug('Tree for path %s and options %o', path, options)

  var pages = 0
  var concurrency = {concurrency: options.concurrency}
  //this is used only for nativeSearch
  var root = util.isArray(path) ? options.root : path
  var num = 0
  var totalSize = 0

  var cache = options.cache || false

  var calcDirectorySize = function(f) {

    if(f.ext !== 'app' && f.directory !== true) {
      return f
    }

    var hash = utils.sha1Hash(f.path)

    var resolver = function() {
      if(cache) {
        return Promise.all([
          cache.time.put(hash, ''+f.mtime, options.cacheTTL),
          cache.size.put(hash, ''+f.size, options.cacheTTL)
        ])
        .then(function() {
          return f
        })
      }

      return f
    }

    if(cache) {
      return cache.time.get(hash)
      .then(function(cached) {
        if(cached == f.mtime) {
          return cache.size.get(hash)
          .then(function(size) {
            f.size = parseInt(size)
            return f
          })
        }

        return directorySize('', f, options)
        .then(resolver)
      })
    }

    return directorySize('', f, options)
    .then(resolver)
  }

  return paths(path, options)
  .filter(options.filter, concurrency)
  .filter(options.skip, concurrency)
  .map(function(f) {

    var v = p.join(root, f)

    // debug('Map stat', f)

    return Promise.join(fs.statAsync(v), utils.pathInfo(v), function(stat, info) {
      info.size = stat.size
      info.mtime = stat.mtime.getTime()
      info.lastModified = moment(stat.mtime).format('llll')

      // info.atime = stat.atime.getTime()
      // info.lastAccessed = moment(stat.atime).format('llll')

      if(stat.isDirectory()) {
        info.directory = true
        info.type = 'directory'
        info.depth = 0
      }

      // debug('info', info)

      return info
    })
    .catch(function(err) {
      if(err.code === 'ENOENT') {
        console.error('Stat ENOENT', err)
      }
    })
  }, concurrency)
  .filter(options.searchFilter ? options.searchFilter : function(e) { return e })
  .map(options.sort == 'size' ? calcDirectorySize : function(e) { return e })
  .call('sort', options.sortMethod || function() { return; })
  .filter(function(value, index, length) {

    if(!num)
      num = length

    if(!pages)
      pages = Math.ceil(length / options.limit)

    if(options.page == 1) {
      return index < (options.limit * options.page);
    }

    return index >= (options.page - 1) * options.limit && index < options.page * options.limit
  }, concurrency)
  .map(options.sort !== 'size' ? calcDirectorySize : function(e) { return e })
  .map(function(f) {
    totalSize += f.size
    f.humanSize = prettyBytes(f.size)

    return f
  }, concurrency)
  .then(function(tree) {
    var breadcrumb = buildBreadcrumb(options.root, options.path)

    return util._extend({
      tree: tree,
      pages: pages,
      size: prettyBytes(totalSize),
      num: num,
      breadcrumb: breadcrumb
    }, options)
  })
}

module.exports = tree
<commit_msg>fix(tree): Recursive tree resolve paths with correct root style(tree): Move calcDirectorySize<commit_after>"use strict";
var p = require('path')
var Promise = require('bluebird')
var prettyBytes = require('pretty-bytes')
var util = require('util')
var moment = require('moment')
var utils = require('./utils.js')

var debug = require('debug')('explorer:tree')
var fs = Promise.promisifyAll(require('fs'))

/**
 * Build Breadcrumb from paths
 * @param string root
 * @param string path
 * @return array of objects {path, name} where name will be linked to path
 */
var buildBreadcrumb = function(root, path) {
  var breadcrumbs = [{path: root, name: root}]

  if(!path) {
    return breadcrumbs
  }

  let paths = path.replace(root, '')
    .split('/')
    .filter(function(v) { return v != '' })

  for(let i in paths) {
    breadcrumbs[parseInt(i)+1] = {
      path: p.join(breadcrumbs[i].path, paths[i]),
      name: paths[i]
    }
  }

  return breadcrumbs
}

/**
 * @param string file
 * @param object root (@see pathInfo)
 * @param object options:
 *   - int maxDepth (10)
 *   - function filter (@see noDotFiles)
 */
var directorySize = function(file, root, options) {

  var filePath = p.resolve(root.path, file)
  var depth = file.split(p.sep).length

  if(root.depth < depth) {
    root.depth = depth
  }

  if(root.depth >= options.maxDepth) {
    root.depth = Infinity
    return Promise.resolve(root)
  }

  return fs.statAsync(filePath)
  .then(function(stat) {
    if(stat.isDirectory()) {
      return fs.readdirAsync(filePath)
      .filter(options.filter, {concurrency: options.concurrency})
      .filter(options.skip, {concurrency: options.concurrency})
      .each(v => directorySize(p.join(file, v), root, options))
    } else {
      root.size += stat.size
      return Promise.resolve(root)
    }
  })
  .catch(function(err) {

    if(err.code != 'EACCES' && err.code != 'ENOENT') {
      return Promise.reject(err)
    }

    if(err.code == 'EACCES')
      console.error('No file access (check rights)', filePath)
    else
      console.error('No file entry (file does not exist ?!)', filePath)

    return Promise.resolve(root)
  })
}

var recursiveReaddir = function(root, options) {
  if(!options.depth)
    options.depth = 0

  return fs.readdirAsync(root)
  .filter(options.filter, {concurrency: options.concurrency})
  .filter(options.skip, {concurrency: options.concurrency})
  .map(function(f) {
    var path = p.join(root, f)

    return fs.statAsync(path)
    .then(function(stat) {

      if(options.depth > options.maxDepth)
        return path

      if(stat.isDirectory()) {
        options.depth++
        return recursiveReaddir(path, options)
      }

      return path
    })
  }).then(function(paths) {
    paths.push(root)
    return [].concat.apply([], paths)
  })
}

/**
 * Get directory size through cache
 * @param object options
 * @return function
 */
var getDirectorySize = function(options) {
  var cache = options.cache || false

  /**
   * @param object file (see below)
   * @return Promise
   */
  return function calcDirectorySize(f) {

    if(f.ext !== 'app' && f.directory !== true) {
      return f
    }

    var hash = utils.sha1Hash(f.path)

    var resolver = function() {
      if(cache) {
        return Promise.all([
          cache.time.put(hash, ''+f.mtime, options.cacheTTL),
          cache.size.put(hash, ''+f.size, options.cacheTTL)
        ])
        .then(function() {
          return f
        })
      }

      return f
    }

    if(cache) {
      return cache.time.get(hash)
      .then(function(cached) {
        if(cached == f.mtime) {
          return cache.size.get(hash)
          .then(function(size) {
            f.size = parseInt(size)
            return f
          })
        }

        return directorySize('', f, options)
        .then(resolver)
      })
    }

    return directorySize('', f, options)
    .then(resolver)
  }
}

/**
 * Handles path argument, readdir if it's a string or return Promise.all
 * @param mixed path
 * @return array Promises
 */
var paths = function(path, options) {
  if(typeof path === 'string') {
    if(options.recursive === true) {
      return recursiveReaddir(path, options)
      .map(function(e) {
        return p.relative(path, e)
      })
    }

    return fs.readdirAsync(path)
  } else if(util.isArray(path)) {
    return Promise.all(path)
  }

  throw new TypeError('Path must be defined')
}

/**
 * @param string path
 * @param object options
 *   - int page (0) - the current page
 *   - int maxDepth (10) - max depth for directory size computation
 *   - int limit (100)
 *   - int concurrency (100)
 *   @see prepareTree
 * @return Promise
 * @catchable
 */
var tree = function(path, options) {

  if(!options) { options = {} }

  options.page = parseInt(options.page) || 1
  options.limit = parseInt(options.limit) || 100
  options.maxDepth = parseInt(options.maxDepth) || 10
  options.concurrency = parseInt(options.concurrency) || 100
  options.cacheTTL = options.cacheTTL || 86400 //24 hours

  options.skip = options.skip ? options.skip : function(v) { return true; }
  options.filter = utils.noDotFiles

  debug('Tree for path %s and options %o', path, options)

  var pages = 0
  var concurrency = {concurrency: options.concurrency}
  var num = 0
  var totalSize = 0
  var directorySize = getDirectorySize(options)

  return paths(path, options)
  .filter(options.filter, concurrency)
  .filter(options.skip, concurrency)
  .map(function(f) {
    var v = p.join(path, f)
    // debug('Map stat', f)

    return Promise.join(fs.statAsync(v), utils.pathInfo(v), function(stat, info) {
      info.size = stat.size
      info.mtime = stat.mtime.getTime()
      info.lastModified = moment(stat.mtime).format('llll')

      // info.atime = stat.atime.getTime()
      // info.lastAccessed = moment(stat.atime).format('llll')

      if(stat.isDirectory()) {
        info.directory = true
        info.type = 'directory'
        info.depth = 0
      }

      // debug('info', info)

      return info
    })
    .catch(function(err) {
      if(err.code === 'ENOENT') {
        console.error('Stat ENOENT', err)
      }
    })
  }, concurrency)
  .filter(options.searchFilter ? options.searchFilter : function(e) { return e })
  .map(options.sort == 'size' ? directorySize : function(e) { return e })
  .call('sort', options.sortMethod || function() { return; })
  .filter(function(value, index, length) {

    if(!num)
      num = length

    if(!pages)
      pages = Math.ceil(length / options.limit)

    if(options.page == 1) {
      return index < (options.limit * options.page);
    }

    return index >= (options.page - 1) * options.limit && index < options.page * options.limit
  }, concurrency)
  .map(options.sort !== 'size' ? directorySize : function(e) { return e })
  .map(function(f) {
    totalSize += f.size
    f.humanSize = prettyBytes(f.size)

    return f
  }, concurrency)
  .then(function(tree) {
    var breadcrumb = buildBreadcrumb(options.root, options.path)

    return util._extend({
      tree: tree,
      pages: pages,
      size: prettyBytes(totalSize),
      num: num,
      breadcrumb: breadcrumb
    }, options)
  })
}

module.exports = tree
<|endoftext|>


<commit_before>import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import { connect } from 'react-redux';
import { Link } from 'react-router-dom';
import { getOfficeDetail } from '../actions';
import { bindActionCreators } from 'redux';

class OfficeAvailability extends Component {
  constructor(props) {
    super(props);
    this.state = { id:'' };
  }
  componentDidMount() {
    const { id } = this.props.match.params;
    this.setState({ id });
    console.log('OfficeAvailability component mounted - id:', id);
    //this.props.change('office_id', id);
    this.props.getOfficeDetail(id);
  }
  render() {
    if (!this.props.office) {
      return (

Office Availability

            No office selected

      )
    } else {
      return (

          Availability for { this.props.office.name }

Address: { this.props.office.address }

      )
    }
  }
}

function mapStateToProps(state) {
  return {
    office: state.activeOffice
  }
}

function mapDispatchToProps(dispatch) {
  return bindActionCreators({ getOfficeDetail }, dispatch);
}

export default connect(mapStateToProps, mapDispatchToProps)(OfficeAvailability);
<commit_msg>add calendar in office availability component<commit_after>import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import { connect } from 'react-redux';
import { Link } from 'react-router-dom';
import { getOfficeDetail } from '../actions';
import { bindActionCreators } from 'redux';
import Calendar from './calendar';

class OfficeAvailability extends Component {
  constructor(props) {
    super(props);
    this.state = { id:'' };
  }
  componentDidMount() {
    const { id } = this.props.match.params;
    this.setState({ id });
    console.log('OfficeAvailability component mounted - id:', id);
    //this.props.change('office_id', id);
    this.props.getOfficeDetail(id);
  }
  render() {
    if (!this.props.office) {
      return (

Office Availability

            No office selected

      )
    } else {
      return (

          Availability for { this.props.office.name }

            <Calendar />

      )
    }
  }
}

function mapStateToProps(state) {
  return {
    office: state.activeOffice
  }
}

function mapDispatchToProps(dispatch) {
  return bindActionCreators({ getOfficeDetail }, dispatch);
}

export default connect(mapStateToProps, mapDispatchToProps)(OfficeAvailability);
<|endoftext|>


<commit_before>jQuery(document).ready(function() {
    jQuery('.mobile-nav').click(function() {
        jQuery('body').toggleClass('show');
    });
	jQuery('.menu-open').click(function() {
        jQuery('body').removeClass('show');
   	});
});<commit_msg>added js to make twitter block exceed 520 width<commit_after>jQuery(document).ready(function() {
    jQuery('.mobile-nav').click(function() {
        jQuery('body').toggleClass('show');
    });
	jQuery('.menu-open').click(function() {
        jQuery('body').removeClass('show');
   	});
});

twttr.events.bind('rendered',function(){
  [].slice.call(document.querySelectorAll('iframe.twitter-timeline')).forEach(function(e,i,a){
    var fE = e.contentDocument.getElementsByClassName('timeline');
    if(fE.length){
      fE[0].style.maxWidth='720px';
    }
  });
});<|endoftext|>


<commit_before>'use strict';

var extend = require('extend');
var path = require('path');
var fs = require('fs');
var convertSourceMap = require('convert-source-map');

function Tree(nodes) {
    this.nodes = nodes;
}

Tree.prototype.cloneNode = function (node) {
    var clone = mixIn({}, node);
    clone.metadata = mixIn({}, node.metadata);
    clone.siblings = node.siblings.slice();
    return clone;
};

Tree.prototype.cloneSibling = function (sibling, node) {
    return mixIn({}, sibling);
};

Tree.prototype.findNodeByName = function (name) {
    return this.findNode(function (n) {
        return n === name;
    });
};

Tree.prototype.findNodeByPath = function (absPath) {
    return this.findNode(function (name, base) {
        return path.join(base, name) === absPath;
    });
};

Tree.prototype.findNode = function (predicate) {
    for (var nodeIndex = 0; nodeIndex !== this.nodes.length; nodeIndex++) {
        var node = this.nodes[nodeIndex];
        if (predicate(node.name, node.base)) {
            return node;
        }
        for (var siblingIndex = 0; siblingIndex !== node.siblings.length; siblingIndex++) {
            var sibling = node.siblings[siblingIndex];
            if (predicate(sibling.name, node.base)) {
                return {
                    name: sibling.name,
                    base: node.base,
                    data: sibling.data,
                    stat: sibling.stat,
                    metadata: {},
                    siblings: []
                };
            }
        }
    }
    return null;
};

var SourceMap = {};

SourceMap.get = function (node) {
    if (node.metadata.sourceMap !== undefined) {
        var data = node.siblings[node.metadata.sourceMap].data.toString('utf-8');
        return JSON.parse(data);
    } else {
        return undefined;
    }
};

SourceMap.name = function (node) {
    return path.basename(node.name) + '.map';
};

SourceMap.set = function (node, sourceMap, options) {
    options.annotate = options.annotate || false;
    options.sourceBase = options.sourceBase || false;

    var basedir = path.dirname(node.name);

    if (options.sourceBase) {
        sourceMap.sources = sourceMap.sources.map(function (source) {
            return path.relative(options.sourceBase, source);
        });
    }

    if (sourceMap.sourcesContent === undefined || sourceMap.sourcesContent.length === 0) {
        sourceMap.sourcesContent = sourceMap.sources.map(function (source) {
            return fs.readFileSync(path.join(node.base, basedir, source), {encoding: 'utf-8'});
        });
    }

    if (node.metadata.sourceMap !== undefined) {
        var sibling = Tree.prototype.cloneSibling(node.siblings[node.metadata.sourceMap], node);
        sibling.data = new Buffer(JSON.stringify(sourceMap), 'utf-8');
        node.siblings[node.metadata.sourceMap] = sibling;
    } else {
        node.metadata.sourceMap = node.siblings.length;
        node.siblings.push({
            name: path.join(basedir, SourceMap.name(node)),
            data: new Buffer(JSON.stringify(sourceMap), 'utf-8')
        });

        options.annotate = true;
    }

    if (options.annotate) {
        var data = node.data.toString('utf-8');
        var commentFreeData = convertSourceMap.removeMapFileComments(data);

        var annotation;
        if (node.metadata.mime === 'application/javascript') {
            annotation = '\n//# sourceMappingURL=' + SourceMap.name(node);
        } else if (node.metadata.mime === 'text/css') {
            annotation = '\n/*# sourceMappingURL=' + SourceMap.name(node) +'*/';
        }

        node.data = new Buffer(data + annotation, 'utf-8');
    }
};

SourceMap.unset = function (node) {
    if (node.metadata.sourceMap !== undefined) {
        node.siblings.splice(node.metadata.sourceMap, 1);
        delete node.metadata.sourceMap;

        var data = node.data.toString('utf-8');
        var commentFreeData = convertSourceMap.removeMapFileComments(data);
        node.data = new Buffer(commentFreeData, 'utf-8');

        return true;
    }

    return false;
};

Tree.create = function (nodes) {
    return new Tree(nodes);
};

Tree.merge = function () {
    var result = new Tree([]);
    for (var i = 0; i !== arguments.length; i++) {
        var tree = arguments[i];
        result.nodes.push.apply(result.nodes, tree.nodes);
    }
    return result;
};

module.exports = Tree.create;
module.exports.merge = Tree.merge;
module.exports.sourceMap = SourceMap;
<commit_msg>improve tree api, moar immutability<commit_after>'use strict';

var extend = require('extend');
var path = require('path');
var fs = require('fs');
var convertSourceMap = require('convert-source-map');

function Tree(nodes) {
    this.nodes = nodes;
}

Tree.prototype.findNodeByName = function (name) {
    return this.findNode(function (n) {
        return n === name;
    });
};

Tree.prototype.findNodeByPath = function (absPath) {
    return this.findNode(function (name, base) {
        return path.join(base, name) === absPath;
    });
};

Tree.prototype.findNode = function (predicate) {
    for (var nodeIndex = 0; nodeIndex !== this.nodes.length; nodeIndex++) {
        var node = this.nodes[nodeIndex];
        if (predicate(node.name, node.base)) {
            return node;
        }
        for (var siblingIndex = 0; siblingIndex !== node.siblings.length; siblingIndex++) {
            var sibling = node.siblings[siblingIndex];
            if (predicate(sibling.name, node.base)) {
                return {
                    name: sibling.name,
                    base: node.base,
                    data: sibling.data,
                    stat: sibling.stat,
                    metadata: {},
                    siblings: []
                };
            }
        }
    }
    return null;
};

Tree.create = function (nodes) {
    return new Tree(nodes);
};

Tree.merge = function () {
    var result = new Tree([]);
    for (var i = 0; i !== arguments.length; i++) {
        var tree = arguments[i];
        result.nodes.push.apply(result.nodes, tree.nodes);
    }
    return result;
};

Tree.cloneNode = function (node) {
    var clone = extend({}, node);
    clone.metadata = extend({}, node.metadata);
    clone.siblings = node.siblings.slice();
    return clone;
};

Tree.cloneSibling = function (sibling, node) {
    return extend({}, sibling);
};

var SourceMap = {};

SourceMap.get = function (node) {
    if (node.metadata.sourceMap !== undefined) {
        var data = node.siblings[node.metadata.sourceMap].data.toString('utf-8');
        return JSON.parse(data);
    } else {
        return undefined;
    }
};

SourceMap.name = function (node) {
    return path.basename(node.name) + '.map';
};

SourceMap.set = function (node, sourceMap, options) {
    options.annotate = options.annotate || false;
    options.sourceBase = options.sourceBase || false;

    var basedir = path.dirname(node.name);

    if (options.sourceBase) {
        sourceMap.sources = sourceMap.sources.map(function (source) {
            return path.relative(options.sourceBase, source);
        });
    }

    if (sourceMap.sourcesContent === undefined || sourceMap.sourcesContent.length === 0) {
        sourceMap.sourcesContent = sourceMap.sources.map(function (source) {
            return fs.readFileSync(path.join(node.base, basedir, source), {encoding: 'utf-8'});
        });
    }

    var outputNode = Tree.cloneNode(node);

    if (node.metadata.sourceMap !== undefined) {
        var sibling = Tree.cloneSibling(outputNode.siblings[outputNode.metadata.sourceMap], outputNode);
        sibling.data = new Buffer(JSON.stringify(sourceMap), 'utf-8');
        outputNode.siblings[outputNode.metadata.sourceMap] = sibling;
    } else {
        outputNode.metadata.sourceMap = outputNode.siblings.length;
        outputNode.siblings.push({
            name: path.join(basedir, SourceMap.name(node)),
            data: new Buffer(JSON.stringify(sourceMap), 'utf-8')
        });

        options.annotate = true;
    }

    if (options.annotate) {
        var data = outputNode.data.toString('utf-8');
        var commentFreeData = convertSourceMap.removeMapFileComments(data);

        var annotation;
        if (outputNode.metadata.mime === 'application/javascript') {
            annotation = '\n//# sourceMappingURL=' + SourceMap.name(node);
        } else if (outputNode.metadata.mime === 'text/css') {
            annotation = '\n/*# sourceMappingURL=' + SourceMap.name(node) +'*/';
        }

        outputNode.data = new Buffer(data + annotation, 'utf-8');
    }

    return outputNode;
};

SourceMap.unset = function (node) {
    if (node.metadata.sourceMap !== undefined) {
        var outputNode = Tree.cloneNode(node);
        outputNode.siblings.splice(node.metadata.sourceMap, 1);
        delete outputNode.metadata.sourceMap;

        var data = outputNode.data.toString('utf-8');
        var commentFreeData = convertSourceMap.removeMapFileComments(data);
        outputNode.data = new Buffer(commentFreeData, 'utf-8');

        return outputNode;
    }

    return node;
};

module.exports = extend(Tree.create, {
    merge: Tree.merge,
    cloneNode: Tree.cloneNode,
    cloneSibling: Tree.cloneSibling,
    sourceMap: SourceMap
});
<|endoftext|>