<commit_before>import RBush from 'rbush';

import { dispatch as d3_dispatch } from 'd3-dispatch';
import { json as d3_json } from 'd3-fetch';

import marked from 'marked';

import { fileFetcher } from '../core/file_fetcher';
import { localizer } from '../core/localizer';
import { geoExtent, geoVecAdd } from '../geo';
import { QAItem } from '../osm';
import { utilRebind, utilTiler, utilQsString } from '../util';

const tiler = utilTiler();
const dispatch = d3_dispatch('loaded');
const _tileZoom = 14;
const _osmoseUrlRoot = 'https://osmose.openstreetmap.fr/api/0.3beta';
let _osmoseData = { icons: {}, items: [] };

// This gets reassigned if reset
let _cache;

function abortRequest(controller) {
  if (controller) {
    controller.abort();
  }
}

function abortUnwantedRequests(cache, tiles) {
  Object.keys(cache.inflightTile).forEach(k => {
    let wanted = tiles.find(tile => k === tile.id);
    if (!wanted) {
      abortRequest(cache.inflightTile[k]);
      delete cache.inflightTile[k];
    }
  });
}

function encodeIssueRtree(d) {
  return { minX: d.loc[0], minY: d.loc[1], maxX: d.loc[0], maxY: d.loc[1], data: d };
}

// Replace or remove QAItem from rtree
function updateRtree(item, replace) {
  _cache.rtree.remove(item, (a, b) => a.data.id === b.data.id);

  if (replace) {
    _cache.rtree.insert(item);
  }
}

// Issues shouldn't obscure eachother
function preventCoincident(loc) {
  let coincident = false;
  do {
    // first time, move marker up. after that, move marker right.
    let delta = coincident ? [0.00001, 0] : [0, 0.00001];
    loc = geoVecAdd(loc, delta);
    let bbox = geoExtent(loc).bbox();
    coincident = _cache.rtree.search(bbox).length;
  } while (coincident);

  return loc;
}

export default {
  title: 'osmose',

  init() {
    fileFetcher.get('qa_data')
      .then(d => {
        _osmoseData = d.osmose;
        _osmoseData.items = Object.keys(d.osmose.icons)
          .map(s => s.split('-')[0])
          .reduce((unique, item) => unique.indexOf(item) !== -1 ? unique : [...unique, item], []);
      });

    if (!_cache) {
      this.reset();
    }

    this.event = utilRebind(this, dispatch, 'on');
  },

  reset() {
    if (_cache) {
      Object.values(_cache.inflightTile).forEach(abortRequest);
    }
    _cache = {
      data: {},
      loadedTile: {},
      inflightTile: {},
      inflightPost: {},
      closed: {},
      rtree: new RBush(),
      strings: {},
      colors: {}
    };
  },

  loadIssues(projection) {
    let params = {
      // Tiles return a maximum # of issues
      // So we want to filter our request for only types iD supports
      item: _osmoseData.items
    };

    // determine the needed tiles to cover the view
    let tiles = tiler
      .zoomExtent([_tileZoom, _tileZoom])
      .getTiles(projection);

    // abort inflight requests that are no longer needed
    abortUnwantedRequests(_cache, tiles);

    // issue new requests..
    tiles.forEach(tile => {
      if (_cache.loadedTile[tile.id] || _cache.inflightTile[tile.id]) return;

      let [ x, y, z ] = tile.xyz;
      let url = `${_osmoseUrlRoot}/issues/${z}/${x}/${y}.json?` + utilQsString(params);

      let controller = new AbortController();
      _cache.inflightTile[tile.id] = controller;

      d3_json(url, { signal: controller.signal })
        .then(data => {
          delete _cache.inflightTile[tile.id];
          _cache.loadedTile[tile.id] = true;

          if (data.features) {
            data.features.forEach(issue => {
              const { item, class: cl, uuid: id } = issue.properties;
              /* Osmose issues are uniquely identified by a unique
                `item` and `class` combination (both integer values) */
              const itemType = `${item}-${cl}`;

              // Filter out unsupported issue types (some are too specific or advanced)
              if (itemType in _osmoseData.icons) {
                let loc = issue.geometry.coordinates; // lon, lat
                loc = preventCoincident(loc);

                let d = new QAItem(loc, this, itemType, id, { item });

                // Setting elems here prevents UI detail requests
                if (item === 8300 || item === 8360) {
                  d.elems = [];
                }

                _cache.data[d.id] = d;
                _cache.rtree.insert(encodeIssueRtree(d));
              }
            });
          }

          dispatch.call('loaded');
        })
        .catch(() => {
          delete _cache.inflightTile[tile.id];
          _cache.loadedTile[tile.id] = true;
        });
    });
  },

  loadIssueDetail(issue) {
    // Issue details only need to be fetched once
    if (issue.elems !== undefined) {
      return Promise.resolve(issue);
    }

    const url = `${_osmoseUrlRoot}/issue/${issue.id}?langs=${localizer.localeCode()}`;
    const cacheDetails = data => {
      // Associated elements used for highlighting
      // Assign directly for immediate use in the callback
      issue.elems = data.elems.map(e => e.type.substring(0,1) + e.id);

      // Some issues have instance specific detail in a subtitle
      issue.detail = data.subtitle ? marked(data.subtitle.auto) : '';

      this.replaceItem(issue);
    };

    return d3_json(url).then(cacheDetails).then(() => issue);
  },

  loadStrings(locale=localizer.localeCode()) {
    const items = Object.keys(_osmoseData.icons);

    if (
      locale in _cache.strings
      && Object.keys(_cache.strings[locale]).length === items.length
    ) {
        return Promise.resolve(_cache.strings[locale]);
    }

    // May be partially populated already if some requests were successful
    if (!(locale in _cache.strings)) {
      _cache.strings[locale] = {};
    }

    // Only need to cache strings for supported issue types
    // Using multiple individual item + class requests to reduce fetched data size
    const allRequests = items.map(itemType => {
      // No need to request data we already have
      if (itemType in _cache.strings[locale]) return;

      const cacheData = data => {
        // Bunch of nested single value arrays of objects
        const [ cat = {items:[]} ] = data.categories;
        const [ item = {class:[]} ] = cat.items;
        const [ cl = null ] = item.class;

        // If null default value is reached, data wasn't as expected (or was empty)
        if (!cl) {
          /* eslint-disable no-console */
          console.log(`Osmose strings request (${itemType}) had unexpected data`);
          /* eslint-enable no-console */
          return;
        }

        // Cache served item colors to automatically style issue markers later
        const { item: itemInt, color } = item;
        if (/^#[A-Fa-f0-9]{6}|[A-Fa-f0-9]{3}/.test(color)) {
          _cache.colors[itemInt] = color;
        }

        // Value of root key will be null if no string exists
        // If string exists, value is an object with key 'auto' for string
        const { title, detail, fix, trap } = cl;

        // Osmose titles shouldn't contain markdown
        let issueStrings = {};
        if (title) issueStrings.title = title.auto;
        if (detail) issueStrings.detail = marked(detail.auto);
        if (trap) issueStrings.trap = marked(trap.auto);
        if (fix) issueStrings.fix = marked(fix.auto);

        _cache.strings[locale][itemType] = issueStrings;
      };

      const [ item, cl ] = itemType.split('-');

      // Osmose API falls back to English strings where untranslated or if locale doesn't exist
      const url = `${_osmoseUrlRoot}/items/${item}/class/${cl}?langs=${locale}`;

      return d3_json(url).then(cacheData);
    });

    return Promise.all(allRequests).then(() => _cache.strings[locale]);
  },

  getStrings(itemType, locale=localizer.localeCode()) {
    // No need to fallback to English, Osmose API handles this for us
    return (locale in _cache.strings) ? _cache.strings[locale][itemType] : {};
  },

  getColor(itemType) {
    return (itemType in _cache.colors) ? _cache.colors[itemType] : '#FFFFFF';
  },

  postUpdate(issue, callback) {
    if (_cache.inflightPost[issue.id]) {
      return callback({ message: 'Issue update already inflight', status: -2 }, issue);
    }

    // UI sets the status to either 'done' or 'false'
    const url = `${_osmoseUrlRoot}/issue/${issue.id}/${issue.newStatus}`;
    const controller = new AbortController();
    const after = () => {
      delete _cache.inflightPost[issue.id];

      this.removeItem(issue);
      if (issue.newStatus === 'done') {
        // Keep track of the number of issues closed per `item` to tag the changeset
        if (!(issue.item in _cache.closed)) {
          _cache.closed[issue.item] = 0;
        }
        _cache.closed[issue.item] += 1;
      }
      if (callback) callback(null, issue);
    };

    _cache.inflightPost[issue.id] = controller;

    fetch(url, { signal: controller.signal })
      .then(after)
      .catch(err => {
        delete _cache.inflightPost[issue.id];
        if (callback) callback(err.message);
      });
  },

  // Get all cached QAItems covering the viewport
  getItems(projection) {
    const viewport = projection.clipExtent();
    const min = [viewport[0][0], viewport[1][1]];
    const max = [viewport[1][0], viewport[0][1]];
    const bbox = geoExtent(projection.invert(min), projection.invert(max)).bbox();

    return _cache.rtree.search(bbox).map(d => d.data);
  },

  // Get a QAItem from cache
  // NOTE: Don't change method name until UI v3 is merged
  getError(id) {
    return _cache.data[id];
  },

  // get the name of the icon to display for this item
  getIcon(itemType) {
    return _osmoseData.icons[itemType];
  },

  // Replace a single QAItem in the cache
  replaceItem(item) {
    if (!(item instanceof QAItem) || !item.id) return;

    _cache.data[item.id] = item;
    updateRtree(encodeIssueRtree(item), true); // true = replace
    return item;
  },

  // Remove a single QAItem from the cache
  removeItem(item) {
    if (!(item instanceof QAItem) || !item.id) return;

    delete _cache.data[item.id];
    updateRtree(encodeIssueRtree(item), false); // false = remove
  },

  // Used to populate `closed:osmose:*` changeset tags
  getClosedCounts() {
    return _cache.closed;
  },

  itemURL(item) {
    return `https://osmose.openstreetmap.fr/error/${item.id}`;
  }
};
<commit_msg>Fix white Osmose icons after changes upload<commit_after>import RBush from 'rbush';

import { dispatch as d3_dispatch } from 'd3-dispatch';
import { json as d3_json } from 'd3-fetch';

import marked from 'marked';

import { fileFetcher } from '../core/file_fetcher';
import { localizer } from '../core/localizer';
import { geoExtent, geoVecAdd } from '../geo';
import { QAItem } from '../osm';
import { utilRebind, utilTiler, utilQsString } from '../util';

const tiler = utilTiler();
const dispatch = d3_dispatch('loaded');
const _tileZoom = 14;
const _osmoseUrlRoot = 'https://osmose.openstreetmap.fr/api/0.3beta';
let _osmoseData = { icons: {}, items: [] };

// This gets reassigned if reset
let _cache;

function abortRequest(controller) {
  if (controller) {
    controller.abort();
  }
}

function abortUnwantedRequests(cache, tiles) {
  Object.keys(cache.inflightTile).forEach(k => {
    let wanted = tiles.find(tile => k === tile.id);
    if (!wanted) {
      abortRequest(cache.inflightTile[k]);
      delete cache.inflightTile[k];
    }
  });
}

function encodeIssueRtree(d) {
  return { minX: d.loc[0], minY: d.loc[1], maxX: d.loc[0], maxY: d.loc[1], data: d };
}

// Replace or remove QAItem from rtree
function updateRtree(item, replace) {
  _cache.rtree.remove(item, (a, b) => a.data.id === b.data.id);

  if (replace) {
    _cache.rtree.insert(item);
  }
}

// Issues shouldn't obscure eachother
function preventCoincident(loc) {
  let coincident = false;
  do {
    // first time, move marker up. after that, move marker right.
    let delta = coincident ? [0.00001, 0] : [0, 0.00001];
    loc = geoVecAdd(loc, delta);
    let bbox = geoExtent(loc).bbox();
    coincident = _cache.rtree.search(bbox).length;
  } while (coincident);

  return loc;
}

export default {
  title: 'osmose',

  init() {
    fileFetcher.get('qa_data')
      .then(d => {
        _osmoseData = d.osmose;
        _osmoseData.items = Object.keys(d.osmose.icons)
          .map(s => s.split('-')[0])
          .reduce((unique, item) => unique.indexOf(item) !== -1 ? unique : [...unique, item], []);
      });

    if (!_cache) {
      this.reset();
    }

    this.event = utilRebind(this, dispatch, 'on');
  },

  reset() {
    let _strings = {};
    let _colors = {};
    if (_cache) {
      Object.values(_cache.inflightTile).forEach(abortRequest);
      // Strings and colors are static and should not be re-populated
      _strings = _cache.strings;
      _colors = _cache.colors;
    }
    _cache = {
      data: {},
      loadedTile: {},
      inflightTile: {},
      inflightPost: {},
      closed: {},
      rtree: new RBush(),
      strings: _strings,
      colors: _colors
    };
  },

  loadIssues(projection) {
    let params = {
      // Tiles return a maximum # of issues
      // So we want to filter our request for only types iD supports
      item: _osmoseData.items
    };

    // determine the needed tiles to cover the view
    let tiles = tiler
      .zoomExtent([_tileZoom, _tileZoom])
      .getTiles(projection);

    // abort inflight requests that are no longer needed
    abortUnwantedRequests(_cache, tiles);

    // issue new requests..
    tiles.forEach(tile => {
      if (_cache.loadedTile[tile.id] || _cache.inflightTile[tile.id]) return;

      let [ x, y, z ] = tile.xyz;
      let url = `${_osmoseUrlRoot}/issues/${z}/${x}/${y}.json?` + utilQsString(params);

      let controller = new AbortController();
      _cache.inflightTile[tile.id] = controller;

      d3_json(url, { signal: controller.signal })
        .then(data => {
          delete _cache.inflightTile[tile.id];
          _cache.loadedTile[tile.id] = true;

          if (data.features) {
            data.features.forEach(issue => {
              const { item, class: cl, uuid: id } = issue.properties;
              /* Osmose issues are uniquely identified by a unique
                `item` and `class` combination (both integer values) */
              const itemType = `${item}-${cl}`;

              // Filter out unsupported issue types (some are too specific or advanced)
              if (itemType in _osmoseData.icons) {
                let loc = issue.geometry.coordinates; // lon, lat
                loc = preventCoincident(loc);

                let d = new QAItem(loc, this, itemType, id, { item });

                // Setting elems here prevents UI detail requests
                if (item === 8300 || item === 8360) {
                  d.elems = [];
                }

                _cache.data[d.id] = d;
                _cache.rtree.insert(encodeIssueRtree(d));
              }
            });
          }

          dispatch.call('loaded');
        })
        .catch(() => {
          delete _cache.inflightTile[tile.id];
          _cache.loadedTile[tile.id] = true;
        });
    });
  },

  loadIssueDetail(issue) {
    // Issue details only need to be fetched once
    if (issue.elems !== undefined) {
      return Promise.resolve(issue);
    }

    const url = `${_osmoseUrlRoot}/issue/${issue.id}?langs=${localizer.localeCode()}`;
    const cacheDetails = data => {
      // Associated elements used for highlighting
      // Assign directly for immediate use in the callback
      issue.elems = data.elems.map(e => e.type.substring(0,1) + e.id);

      // Some issues have instance specific detail in a subtitle
      issue.detail = data.subtitle ? marked(data.subtitle.auto) : '';

      this.replaceItem(issue);
    };

    return d3_json(url).then(cacheDetails).then(() => issue);
  },

  loadStrings(locale=localizer.localeCode()) {
    const items = Object.keys(_osmoseData.icons);

    if (
      locale in _cache.strings
      && Object.keys(_cache.strings[locale]).length === items.length
    ) {
        return Promise.resolve(_cache.strings[locale]);
    }

    // May be partially populated already if some requests were successful
    if (!(locale in _cache.strings)) {
      _cache.strings[locale] = {};
    }

    // Only need to cache strings for supported issue types
    // Using multiple individual item + class requests to reduce fetched data size
    const allRequests = items.map(itemType => {
      // No need to request data we already have
      if (itemType in _cache.strings[locale]) return;

      const cacheData = data => {
        // Bunch of nested single value arrays of objects
        const [ cat = {items:[]} ] = data.categories;
        const [ item = {class:[]} ] = cat.items;
        const [ cl = null ] = item.class;

        // If null default value is reached, data wasn't as expected (or was empty)
        if (!cl) {
          /* eslint-disable no-console */
          console.log(`Osmose strings request (${itemType}) had unexpected data`);
          /* eslint-enable no-console */
          return;
        }

        // Cache served item colors to automatically style issue markers later
        const { item: itemInt, color } = item;
        if (/^#[A-Fa-f0-9]{6}|[A-Fa-f0-9]{3}/.test(color)) {
          _cache.colors[itemInt] = color;
        }

        // Value of root key will be null if no string exists
        // If string exists, value is an object with key 'auto' for string
        const { title, detail, fix, trap } = cl;

        // Osmose titles shouldn't contain markdown
        let issueStrings = {};
        if (title) issueStrings.title = title.auto;
        if (detail) issueStrings.detail = marked(detail.auto);
        if (trap) issueStrings.trap = marked(trap.auto);
        if (fix) issueStrings.fix = marked(fix.auto);

        _cache.strings[locale][itemType] = issueStrings;
      };

      const [ item, cl ] = itemType.split('-');

      // Osmose API falls back to English strings where untranslated or if locale doesn't exist
      const url = `${_osmoseUrlRoot}/items/${item}/class/${cl}?langs=${locale}`;

      return d3_json(url).then(cacheData);
    });

    return Promise.all(allRequests).then(() => _cache.strings[locale]);
  },

  getStrings(itemType, locale=localizer.localeCode()) {
    // No need to fallback to English, Osmose API handles this for us
    return (locale in _cache.strings) ? _cache.strings[locale][itemType] : {};
  },

  getColor(itemType) {
    return (itemType in _cache.colors) ? _cache.colors[itemType] : '#FFFFFF';
  },

  postUpdate(issue, callback) {
    if (_cache.inflightPost[issue.id]) {
      return callback({ message: 'Issue update already inflight', status: -2 }, issue);
    }

    // UI sets the status to either 'done' or 'false'
    const url = `${_osmoseUrlRoot}/issue/${issue.id}/${issue.newStatus}`;
    const controller = new AbortController();
    const after = () => {
      delete _cache.inflightPost[issue.id];

      this.removeItem(issue);
      if (issue.newStatus === 'done') {
        // Keep track of the number of issues closed per `item` to tag the changeset
        if (!(issue.item in _cache.closed)) {
          _cache.closed[issue.item] = 0;
        }
        _cache.closed[issue.item] += 1;
      }
      if (callback) callback(null, issue);
    };

    _cache.inflightPost[issue.id] = controller;

    fetch(url, { signal: controller.signal })
      .then(after)
      .catch(err => {
        delete _cache.inflightPost[issue.id];
        if (callback) callback(err.message);
      });
  },

  // Get all cached QAItems covering the viewport
  getItems(projection) {
    const viewport = projection.clipExtent();
    const min = [viewport[0][0], viewport[1][1]];
    const max = [viewport[1][0], viewport[0][1]];
    const bbox = geoExtent(projection.invert(min), projection.invert(max)).bbox();

    return _cache.rtree.search(bbox).map(d => d.data);
  },

  // Get a QAItem from cache
  // NOTE: Don't change method name until UI v3 is merged
  getError(id) {
    return _cache.data[id];
  },

  // get the name of the icon to display for this item
  getIcon(itemType) {
    return _osmoseData.icons[itemType];
  },

  // Replace a single QAItem in the cache
  replaceItem(item) {
    if (!(item instanceof QAItem) || !item.id) return;

    _cache.data[item.id] = item;
    updateRtree(encodeIssueRtree(item), true); // true = replace
    return item;
  },

  // Remove a single QAItem from the cache
  removeItem(item) {
    if (!(item instanceof QAItem) || !item.id) return;

    delete _cache.data[item.id];
    updateRtree(encodeIssueRtree(item), false); // false = remove
  },

  // Used to populate `closed:osmose:*` changeset tags
  getClosedCounts() {
    return _cache.closed;
  },

  itemURL(item) {
    return `https://osmose.openstreetmap.fr/error/${item.id}`;
  }
};
<|endoftext|>


<commit_before>exports.question = function(req, res) {
  res.render('quizes/question', {pregunta: 'Capital de Italia'});
};

exports.answer = function(req, res) {
  if (req.query.respuesta === 'Roma') {
    res.render('quizes/answer', {respuesta: 'Correcto'});
  } else {
    res.render('quizes/answer', {respuesta: 'Incorrecto'});
  }
};

exports.author = function(req, res) {
	res.render('quizes/author', {autor: 'Qin Jian'})
}

<commit_msg>corregir el controller<commit_after>exports.question = function(req, res) {
  res.render('quizes/question', {pregunta: 'Capital de Italia'});
};

exports.answer = function(req, res) {
  if (req.query.respuesta === 'Roma') {
    res.render('quizes/answer', {respuesta: 'Correcto'});
  } else {
    res.render('quizes/answer', {respuesta: 'Incorrecto'});
  }
};

exports.author = function(req, res) {
	res.render('author', {autor: 'Qin Jian'})
}

<|endoftext|>


<commit_before>/*
 * grunt-compile-revealjs
 * https://github.com/Schepp/grunt-compile-revealjs
 *
 * Copyright (c) 2014 Christian Schaefer
 * Licensed under the MIT license.
 */

'use strict';

module.exports = function(grunt) {

  // Please see the Grunt documentation for more information regarding task
  // creation: http://gruntjs.com/creating-tasks

  grunt.registerMultiTask('compile_revealjs', 'Compiles reveal.js slidedecks', function() {
    // Iterate over all specified file groups.
    this.files.forEach(function(f) {
      var path = require('path'),
          sources = grunt.file.expand({}, f.src),
          setConfig = function(html, config) {
            var globalAttributes = [
                  'accesskey',
                  'class',
                  'contenteditable',
                  'contextmenu',
                  'dir',
                  'draggable',
                  'dropzone',
                  'hidden',
                  'id',
                  'lang',
                  'spellcheck',
                  'style',
                  'tabindex',
                  'title',
                  'translate'
                ],
                attribute,
                sectionHTML,
                presentAttributesString,
                presentAttributesArray,
                presentAttributes = {};

            sectionHTML = html.replace(/[\s\S]*(
]*>)[\s\S]*/, '$1');
            presentAttributesString = sectionHTML.replace(/
/, '$1');
            presentAttributesString = presentAttributesString.replace(/([^'"])\s/g, '$1§');
            presentAttributesArray = presentAttributesString.split(' ');
            if (presentAttributesArray.length) {
              presentAttributesArray.forEach(function(attribute) {
                var keyvalueArray = attribute.split('='),
                    key = keyvalueArray[ 0 ].replace(/^data\-/, '').toLowerCase(),
                    value;

                if (keyvalueArray.length > 1) {
                  value = keyvalueArray[ 1 ]
                    .replace(/§/g, ' ')
                    .replace(/^["']/, '')
                    .replace(/["']$/, '');

                  if (key === 'id') {
                    value = value
                      .replace(/[^_a-z0-9]/g, "-");
                  }

                  presentAttributes[ key ] = value;
                }
              });
            }

            for (attribute in config) {
              if (config.hasOwnProperty(attribute)) {
                if (attribute === 'id') {
                  config[ attribute ] = config[ attribute ].replace(/[^_a-z0-9]/g, "-");
                }

                presentAttributes[ attribute.toLowerCase() ] = config[ attribute ];
              }
            }

            sectionHTML = '<section';
            for (attribute in presentAttributes) {
              if (presentAttributes.hasOwnProperty(attribute)) {
                sectionHTML += ' ';
                if (globalAttributes.indexOf(attribute) !== -1) {
                  sectionHTML += attribute;
                } else {
                  sectionHTML += 'data-' + attribute;
                }
                sectionHTML += '="';
                sectionHTML += presentAttributes[ attribute ];
                sectionHTML += '"';
              }
            }
            sectionHTML += '>';
            html = html.replace(/
]*>/, sectionHTML);

            return html;
          },
          renderCurrentSlide = function(slide) {
            var html = grunt.file.read(slide.template),
                entry,
                regex;

            html = setConfig(html, slide.config);

            for (entry in slide.data) {
              if (slide.data.hasOwnProperty(entry)) {
                regex = new RegExp('{{ ' + entry + ' }}', 'g');
                html = html.replace(regex, slide.data[ entry ]);
              }
            }

            return html;
          },
          renderSlide = function(slide) {
            var html = "";

            if (slide.slides) {
              html += '
';
              html += renderSubslides(slide.slides);
              html += '
';
              html = setConfig(html, slide.config);
            } else {
              html += renderCurrentSlide(slide);
            }

            return html;
          },
          renderVariant = function(variant, slides) {
            var html = "";

            slides.forEach(function(slide) {
              if (
                slide.exclude &&
                slide.exclude.forEach &&
                slide.exclude.indexOf(variant) !== -1
              ) {
                return;
              }
              html += renderSlide(slide);
            });

            html = html.replace(/@@variant/g, variant);

            return html;
          };

      sources = sources.filter(function(filepath) {
        // Warn on and remove invalid source files (if nonull was set).
        if (!grunt.file.exists(filepath)) {
          grunt.log.warn('Source file "' + filepath + '" not found.');
          return false;
        } else {
          grunt.log.writeln('Source file "' + filepath + '" found.');
          return true;
        }
      });

      sources.forEach(function(filepath) {
        var master = path.basename(filepath).split('.')[ 0 ],
            config = JSON.parse(grunt.file.read(filepath)),
            regex = new RegExp('{{ slides }}'),
            layoutHtml = grunt.file.read(config.layout);

        grunt.log.writeln(filepath);
        grunt.log.writeln(path.basename(filepath));

        config.variants.push(master);
        config.variants.forEach(function(variant) {
          var html = renderVariant(variant, config.slides),
              destination;

          html = layoutHtml.replace(regex, html);
          destination = filepath
            .replace(master, variant)
            .replace('.json', '.html');
          destination = path.basename(destination);
          destination = f.dest ? f.dest + '/' + destination : destination;

          grunt.file.write(destination, html);
        });
      });
    });
  });

};
<commit_msg>- Added logic that clears all unresolved variables and also attributes containing unresolved attributes<commit_after>/*
 * grunt-compile-revealjs
 * https://github.com/Schepp/grunt-compile-revealjs
 *
 * Copyright (c) 2014 Christian Schaefer
 * Licensed under the MIT license.
 */

'use strict';

module.exports = function(grunt) {

  // Please see the Grunt documentation for more information regarding task
  // creation: http://gruntjs.com/creating-tasks

  grunt.registerMultiTask('compile_revealjs', 'Compiles reveal.js slidedecks', function() {
    // Iterate over all specified file groups.
    this.files.forEach(function(f) {
      var path = require('path'),
          sources = grunt.file.expand({}, f.src),
          setConfig = function(html, config) {
            var globalAttributes = [
                  'accesskey',
                  'class',
                  'contenteditable',
                  'contextmenu',
                  'dir',
                  'draggable',
                  'dropzone',
                  'hidden',
                  'id',
                  'lang',
                  'spellcheck',
                  'style',
                  'tabindex',
                  'title',
                  'translate'
                ],
                attribute,
                sectionHTML,
                presentAttributesString,
                presentAttributesArray,
                presentAttributes = {};

            sectionHTML = html.replace(/[\s\S]*(
]*>)[\s\S]*/, '$1');
            presentAttributesString = sectionHTML.replace(/
/, '$1');
            presentAttributesString = presentAttributesString.replace(/([^'"])\s/g, '$1§');
            presentAttributesArray = presentAttributesString.split(' ');
            if (presentAttributesArray.length) {
              presentAttributesArray.forEach(function(attribute) {
                var keyvalueArray = attribute.split('='),
                    key = keyvalueArray[ 0 ].replace(/^data\-/, '').toLowerCase(),
                    value;

                if (keyvalueArray.length > 1) {
                  value = keyvalueArray[ 1 ]
                    .replace(/§/g, ' ')
                    .replace(/^["']/, '')
                    .replace(/["']$/, '');

                  if (key === 'id') {
                    value = value
                      .replace(/[^_a-z0-9]/g, "-");
                  }

                  presentAttributes[ key ] = value;
                }
              });
            }

            for (attribute in config) {
              if (config.hasOwnProperty(attribute)) {
                if (attribute === 'id') {
                  config[ attribute ] = config[ attribute ].replace(/[^_a-z0-9]/g, "-");
                }

                presentAttributes[ attribute.toLowerCase() ] = config[ attribute ];
              }
            }

            sectionHTML = '<section';
            for (attribute in presentAttributes) {
              if (presentAttributes.hasOwnProperty(attribute)) {
                sectionHTML += ' ';
                if (globalAttributes.indexOf(attribute) !== -1) {
                  sectionHTML += attribute;
                } else {
                  sectionHTML += 'data-' + attribute;
                }
                sectionHTML += '="';
                sectionHTML += presentAttributes[ attribute ];
                sectionHTML += '"';
              }
            }
            sectionHTML += '>';
            html = html.replace(/
]*>/, sectionHTML);

            return html;
          },
          renderCurrentSlide = function(slide) {
            var html = grunt.file.read(slide.template),
                entry,
                regex;

            html = setConfig(html, slide.config);

            for (entry in slide.data) {
              if (slide.data.hasOwnProperty(entry)) {
                regex = new RegExp('{{ ' + entry + ' }}', 'g');
                html = html.replace(regex, slide.data[ entry ]);
              }
            }

            return html;
          },
          renderSlide = function(slide) {
            var html = "";

            if (slide.slides) {
              html += '
';
              html += renderSubslides(slide.slides);
              html += '
';
              html = setConfig(html, slide.config);
            } else {
              html += renderCurrentSlide(slide);
            }

            return html;
          },
          renderVariant = function(variant, slides) {
            var html = '',
                regex = new RegExp('@@variant', 'g');

            slides.forEach(function(slide) {
              if (
                slide.exclude &&
                slide.exclude.forEach &&
                slide.exclude.indexOf(variant) !== -1
              ) {
                return;
              }
              html += renderSlide(slide);
            });

            html = html.replace(regex, variant);

            return html;
          };

      sources = sources.filter(function(filepath) {
        // Warn on and remove invalid source files (if nonull was set).
        if (!grunt.file.exists(filepath)) {
          grunt.log.warn('Source file "' + filepath + '" not found.');
          return false;
        } else {
          grunt.log.writeln('Source file "' + filepath + '" found.');
          return true;
        }
      });

      sources.forEach(function(filepath) {
        var master = path.basename(filepath).split('.')[ 0 ],
            config = JSON.parse(grunt.file.read(filepath)),
            layoutHtml = grunt.file.read(config.layout);

        grunt.log.writeln(filepath);
        grunt.log.writeln(path.basename(filepath));

        config.variants.push(master);
        config.variants.forEach(function(variant) {
          var html = renderVariant(variant, config.slides),
              regexEmptyAttribute = new RegExp('\\s[^\\s]+=[\'"]{0,1}{{ [^\\s]+ }}[\'"]{0,1}', 'g'),
              regexEmpty = new RegExp('{{ [^\\s]+ }}', 'g'),
              regexSlides = new RegExp('{{ slides }}'),
              destination;

          html = html.replace(regexEmptyAttribute, '');
          html = html.replace(regexEmpty, '');
          html = layoutHtml.replace(regexSlides, html);
          destination = filepath
            .replace(master, variant)
            .replace('.json', '.html');
          destination = path.basename(destination);
          destination = f.dest ? f.dest + '/' + destination : destination;

          grunt.file.write(destination, html);
        });
      });
    });
  });

};
<|endoftext|>


<commit_before>"use strict";

const LS  = 0;
const RS  = 1;
const MLS = 2;
const MRS = 3;

const LEFT  = 0;
const UP    = 1;
const RIGHT = 2;
const DOWN  = 3;

const RightSlash = "\u2571";
const LeftSlash  = "\u2572";
const MarkedESC  = "\x1b[32m";
const BLINK	 = "\x1b[7m";
const RESET      = "\x1b[0m";

const maxTries 	= 100000;
var LastMaze 	= null;

function nextChar(){
	let a = [RS, LS];
	return a[Math.round(Math.random(a.length-1))];
}

function TenPrint(width, height, walk){
  let maze = buildMaze(width, height);
  let count = 0;

  if(walk){
	while(!walkMaze(maze) && count < maxTries){
		count++;
		maze = buildMaze(width, height);
		process.stdout.write("0x" + count.toString(16));
		process.stdout.clearLine();
		process.stdout.cursorTo(0);
	}

	if(count != maxTries){
		console.log("\nFound Solution After " + count + " Tries");

	}else{
		console.log("\nTimedout After " + count + " Tries");
	}
  }

  printMaze(maze);

  // Save the last maze for REPL
  LastMaze = maze;
}

function printMaze(maze){
  let height = maze.length;
  let width  = maze[0].length;

  for(let y=0;y<height;y++){
   let line = "";
   let cChr = "";
   for(let x=0; x<width;x++){
           switch(maze[y][x]){
               case(LS):
                 cChr = LeftSlash;
                 break;
               case(RS):
                 cChr = RightSlash;
                 break;
               case(MLS):
                 cChr = MarkedESC + LeftSlash;
                 break;
               case(MRS):
                 cChr = MarkedESC + RightSlash;
                 break;
               default:
           }
           line = line + cChr + RESET;
        }
        console.log(line);
   }
}

function buildMaze(width, height){
        let maze = new Array(height);
	maze.fill(0);
 	maze.forEach(function(v, i, a){
		a[i] = new Array(width);
		a[i].fill(0);
	});

	for(let y=0;y<height;y++){
        	for(let x=0; x<width;x++){
                	maze[y][x] = nextChar();
        	}
   	}

	return maze;
}

function walkMaze(maze){
	let currentDir = RIGHT;
	let currentRow = -1;
	let currentCol =  0;
	let entrance = -1;

        // LEFT	UP  RIGHT DOWN
	// '\'	'\'  '\'  '\'
	//' /'	'/'  '/'  '/'
	let lookup = [	[ UP,LEFT,DOWN,RIGHT ],
			[ DOWN,RIGHT,UP,LEFT ] ];

	// Start Walking //
	// 1. Start in upper lefthand side and look for two matching slashes
	//    a. if LS then RS or RS then LS then move down 1
	//    b. if RS and RS then move RIGHT starting with second RS
      	//    c. if LS and LS then move RIGHT starting with first LS
	// 2. Lookup in table which direction to move for DIRECTION + SLASH
	// 3. Mark each visited box with MarkedESC
	// 3. Repeat until X | Y  < 0, Y > HEIGHT, or  X > WIDTH
	//    a. IF X > WIDTH then the puzzle is solved
	//    b. IF X | Y < O, or Y > HEIGHT then the puzzle has no solution

	while((entrance = findEntrance(maze, entrance+1)) != -1){

	// What happens if we don't find an entrance?
	if(entrance < 0 || entrance >= maze.length){
		return false;
	}else{
		currentRow = entrance;
		currentDir = RIGHT;
		currentCol = 0;
	}

	do{
		//set the wall at currentRow,currentCol to visited	//
		//set the currentRow, currentCol and currentDir   	//
 		//based on the current character type and currentDir	//
		let cChr = maze[currentRow][currentCol];
		maze[currentRow][currentCol] =
				(cChr == LS || cChr == MLS) ? MLS : MRS;
	        // cChr%2 so that Marked versions and normal versions
		// will both map to the correct row in the lookup
		currentDir = lookup[(cChr%2)][currentDir];

		switch(currentDir){
			case(UP):
			  currentRow = currentRow - 1;
			  break;
			case(DOWN):
			  currentRow = currentRow + 1;
			  break;
			case(LEFT):
			  currentCol = currentCol - 1;
			  break;
			case(RIGHT):
			  currentCol = currentCol + 1;
			  break;
			default:
		}

	}while( (currentRow != entrance  || currentCol == 0)
		&& currentRow >= 0 && currentRow < maze.length
                && currentCol >= 0 && currentCol < maze[0].length);

	if(currentCol == maze[0].length){
               return true;
        }

	// undo any items that we've marked before we try next entrance //
	resetMaze(maze);
     }

     //we did not find a solution!
     return false;
}

function findEntrance(maze, currentRow){
   if(currentRow >= maze.length-1){ return -1 };

   if(maze[currentRow][0] == maze[currentRow+1][0]){
       return (maze[currentRow][0] == RS
		|| maze[currentRow][0] == MRS) ? currentRow+1 : currentRow;
   }else{
	return findEntrance(maze, currentRow+1);
   }
}

function resetMaze(maze){
	maze.forEach(function(v,i,a){
		a[i].forEach(function(v1,i1,a1){
			if(a1[i1] == MLS){
				a1[i1] = LS;
			}else if(a1[i1] == MRS){
				a1[i1] = RS;
			}
		});
	});
}
<commit_msg>Added comment to make it clear what the LastMaze variable was for.<commit_after>"use strict";

const LS  = 0;
const RS  = 1;
const MLS = 2;
const MRS = 3;

const LEFT  = 0;
const UP    = 1;
const RIGHT = 2;
const DOWN  = 3;

const RightSlash = "\u2571";
const LeftSlash  = "\u2572";
const MarkedESC  = "\x1b[32m";
const BLINK	 = "\x1b[7m";
const RESET      = "\x1b[0m";

const maxTries 	= 100000;

// Save the last maze for REPL
var LastMaze 	= null;

function nextChar(){
	let a = [RS, LS];
	return a[Math.round(Math.random(a.length-1))];
}

function TenPrint(width, height, walk){
  let maze = buildMaze(width, height);
  let count = 0;

  if(walk){
	while(!walkMaze(maze) && count < maxTries){
		count++;
		maze = buildMaze(width, height);
		process.stdout.write("0x" + count.toString(16));
		process.stdout.clearLine();
		process.stdout.cursorTo(0);
	}

	if(count != maxTries){
		console.log("\nFound Solution After " + count + " Tries");

	}else{
		console.log("\nTimedout After " + count + " Tries");
	}
  }

  printMaze(maze);

  // Save the last maze for REPL
  LastMaze = maze;
}

function printMaze(maze){
  let height = maze.length;
  let width  = maze[0].length;

  for(let y=0;y<height;y++){
   let line = "";
   let cChr = "";
   for(let x=0; x<width;x++){
           switch(maze[y][x]){
               case(LS):
                 cChr = LeftSlash;
                 break;
               case(RS):
                 cChr = RightSlash;
                 break;
               case(MLS):
                 cChr = MarkedESC + LeftSlash;
                 break;
               case(MRS):
                 cChr = MarkedESC + RightSlash;
                 break;
               default:
           }
           line = line + cChr + RESET;
        }
        console.log(line);
   }
}

function buildMaze(width, height){
        let maze = new Array(height);
	maze.fill(0);
 	maze.forEach(function(v, i, a){
		a[i] = new Array(width);
		a[i].fill(0);
	});

	for(let y=0;y<height;y++){
        	for(let x=0; x<width;x++){
                	maze[y][x] = nextChar();
        	}
   	}

	return maze;
}

function walkMaze(maze){
	let currentDir = RIGHT;
	let currentRow = -1;
	let currentCol =  0;
	let entrance = -1;

        // LEFT	UP  RIGHT DOWN
	// '\'	'\'  '\'  '\'
	//' /'	'/'  '/'  '/'
	let lookup = [	[ UP,LEFT,DOWN,RIGHT ],
			[ DOWN,RIGHT,UP,LEFT ] ];

	// Start Walking //
	// 1. Start in upper lefthand side and look for two matching slashes
	//    a. if LS then RS or RS then LS then move down 1
	//    b. if RS and RS then move RIGHT starting with second RS
      	//    c. if LS and LS then move RIGHT starting with first LS
	// 2. Lookup in table which direction to move for DIRECTION + SLASH
	// 3. Mark each visited box with MarkedESC
	// 3. Repeat until X | Y  < 0, Y > HEIGHT, or  X > WIDTH
	//    a. IF X > WIDTH then the puzzle is solved
	//    b. IF X | Y < O, or Y > HEIGHT then the puzzle has no solution

	while((entrance = findEntrance(maze, entrance+1)) != -1){

	// What happens if we don't find an entrance?
	if(entrance < 0 || entrance >= maze.length){
		return false;
	}else{
		currentRow = entrance;
		currentDir = RIGHT;
		currentCol = 0;
	}

	do{
		//set the wall at currentRow,currentCol to visited	//
		//set the currentRow, currentCol and currentDir   	//
 		//based on the current character type and currentDir	//
		let cChr = maze[currentRow][currentCol];
		maze[currentRow][currentCol] =
				(cChr == LS || cChr == MLS) ? MLS : MRS;
	        // cChr%2 so that Marked versions and normal versions
		// will both map to the correct row in the lookup
		currentDir = lookup[(cChr%2)][currentDir];

		switch(currentDir){
			case(UP):
			  currentRow = currentRow - 1;
			  break;
			case(DOWN):
			  currentRow = currentRow + 1;
			  break;
			case(LEFT):
			  currentCol = currentCol - 1;
			  break;
			case(RIGHT):
			  currentCol = currentCol + 1;
			  break;
			default:
		}

	}while( (currentRow != entrance  || currentCol == 0)
		&& currentRow >= 0 && currentRow < maze.length
                && currentCol >= 0 && currentCol < maze[0].length);

	if(currentCol == maze[0].length){
               return true;
        }

	// undo any items that we've marked before we try next entrance //
	resetMaze(maze);
     }

     //we did not find a solution!
     return false;
}

function findEntrance(maze, currentRow){
   if(currentRow >= maze.length-1){ return -1 };

   if(maze[currentRow][0] == maze[currentRow+1][0]){
       return (maze[currentRow][0] == RS
		|| maze[currentRow][0] == MRS) ? currentRow+1 : currentRow;
   }else{
	return findEntrance(maze, currentRow+1);
   }
}

function resetMaze(maze){
	maze.forEach(function(v,i,a){
		a[i].forEach(function(v1,i1,a1){
			if(a1[i1] == MLS){
				a1[i1] = LS;
			}else if(a1[i1] == MRS){
				a1[i1] = RS;
			}
		});
	});
}
<|endoftext|>


<commit_before>//TODO: resolve data/@src and script/@src. either here, or in a separate module.
//TODO: remove nodejs dependencies
//TODO: decide on a friendly, portable interface to this module. streaming is possible, but maybe not very portable.

var sax = require("sax"),
    strict = true, // set to false for html-mode
    parser;

function merge(o1,o2){
    Object.keys(o2).forEach(function(k){
        o1[k] = o2[k];
    });
    return o1;
}

function getNormalizedAttributeName(attr){
  return attr.uri ? '{' + attr.uri + '}' + attr.local : attr.local;
}

function copyNsAttrObj(o){
    var r = {};
    Object.keys(o).forEach(function(k){
        var attr = o[k];
        r[getNormalizedAttributeName(attr)] = attr.value;
    });
    return r;
}

function transform(xmlString){
    parser = sax.parser(strict,{trim : true, xmlns : true});

    var rootJson,
        currentJson,
        expressionAttributeCache,      //we cache them because in sax-js attributes get processed before the nodes they're attached to,
                                            //and this is the only way we can capture their row/col numbers.
                                            //so when we finally find one, it gets popped off the stack.
        jsonStack = [],
        allTransitions = [];                 //we keep a reference to these so we can clean up the onTransition property later

    function createActionJson(node){
        var action = merge(
                    {
                        $line : parser.line,
                        $column : parser.column,
                        $type: node.local
                    },
                    copyNsAttrObj(node.attributes));

        //console.log('action node',node);

        var actionContainer;
        if(Array.isArray(currentJson)){
            //this will be onExit and onEntry
            currentJson.push(action);
        }else if(currentJson.$type === 'scxml' && action.$type === 'script'){
            //top-level script
            currentJson.rootScripts = currentJson.rootScripts || [];
            currentJson.rootScripts.push(action);
        }else{
            //if it's any other action
            currentJson.actions = currentJson.actions || [];
            currentJson.actions.push(action);
        }

        return currentJson = action;
    }

    function createDataJson(node){
        currentJson =
            merge({
                $line : parser.line,
                $column : parser.column,
                $type : 'data'
            },
            copyNsAttrObj(node.attributes));
        return currentJson;
    }

    function createStateJson(node){
        var state = merge({
                $line : parser.line,
                $column : parser.column
              }, copyNsAttrObj(node.attributes));

        if(state.type){
            state.isDeep = state.type === 'deep' ? true : false;
        }

        //"state" is the default, so you don't need to explicitly write it
        if(node.local !== 'state' && node.local !== 'schema') state.$type = node.local;

        if(currentJson){
            if(!currentJson.states){
                currentJson.states = [];
            }

            currentJson.states.push(state);
        }

        return currentJson = state;
    }

    function createTransitionJson(node){

        var transition = copyNsAttrObj(node.attributes);

        //target can either be a string, an array (for multiple targets, e.g. targeting, or undefined
        if(transition.target){
            //console.log('transition',transition);
            transition.target = transition.target.trim().split(/\s+/);
            if(transition.target.length === 1){
                transition.target = transition.target[0];
            }
        }

        if(currentJson){
            if(!currentJson.transitions){
                currentJson.transitions = [];
            }

            currentJson.transitions.push(transition);
        }

        allTransitions.push(transition);

        return currentJson = transition;
    }

    function createExpression(value){
        return {
            $line : parser.line,
            $column : parser.column,
            expr : value
        };
    }

    var tagActions = {
        "scxml": function(node){
            return rootJson = createStateJson(node);
        },
        "initial": createStateJson,
        "history":createStateJson,
        "state":createStateJson,
        "parallel":createStateJson,
        "final":createStateJson,

        //transitions/action containers
        "transition" : createTransitionJson,

        "onentry":function(node){
            currentJson = currentJson.onEntry = currentJson.onEntry || [];
        },
        "onexit":function(node){
            currentJson = currentJson.onExit = currentJson.onExit || [];
        },

        //actions
        "foreach" : createActionJson,
        "raise" : createActionJson,
        "log": createActionJson,
        "assign": createActionJson,
        "validate":createActionJson,
        "script":createActionJson,
        "cancel":createActionJson,
        //TODO: deal with namelist
        //TODO: decide how to deal with location expressions, as opposed to regular expressions
        "send":createActionJson,

        //children of send
        "param": function(node){
            //TODO: figure out how to deal with param and param/@expr and param/@location
            currentJson.params = currentJson.params || [];
            var attr = copyNsAttrObj(node.attributes);
            currentJson.params.push(attr);
            currentJson = attr;
        },
        "content":function(){
            if (expressionAttributeCache.expr) {
                currentJson.contentexpr = merge({}, expressionAttributeCache.expr);
            }
        },

        //these are treated a bit special - TODO: normalize/decide on a representation
        "if" : createActionJson,
        "elseif" : createActionJson,
        "else" : createActionJson,

        //data
        "datamodel":function(node){
            //console.log('datamodel currentJson',currentJson);
            currentJson = currentJson.datamodel = [];
        },
        "data":function(node){
            //console.log('data currentJson',currentJson);
            currentJson.push(createDataJson(node));
        }

        //TODO: these
        //"invoke":,
        //"finalize":,
        //"donedata":
    };

    expressionAttributeCache = {};  //TODO: put in onstart or something like that

    parser.onopentag = function (node) {
        //console.log("open tag",node.local);

        if(tagActions[node.local]){
            tagActions[node.local](node);

            jsonStack.push(currentJson);
            //console.log('current json now',currentJson,jsonStack.length);

            //merge in the current expression attribute cache
            merge(currentJson,expressionAttributeCache);

            expressionAttributeCache = {};  //clear the expression attribute cache
        } else {
            createActionJson(node);
            jsonStack.push(currentJson);
            merge(currentJson,expressionAttributeCache);
            expressionAttributeCache = {};
        }

    };

    var EXPRESSION_ATTRS = [ 'cond',
                            'array',
                            'location',
                            'namelist',
                            'idlocation'];

    parser.onclosetag = function(tag){
        //console.log("close tag",tag);
        var localName = tag.split(':').pop();
        // if(tagActions[localName]){
            jsonStack.pop();
            currentJson = jsonStack[jsonStack.length - 1];
            //console.log('current json now',currentJson,jsonStack.length);
        // }
    };

    parser.onattribute = function (attr) {
        //if attribute name ends with 'expr' or is one of the other ones enumerated above
        //then cache him and his position
        if( attr.name.match(/^.*expr$/) ||
            EXPRESSION_ATTRS.indexOf(attr.name) > -1){
            expressionAttributeCache[getNormalizedAttributeName(attr)] = createExpression(attr.value);
        }
    };

    parser.onerror = function (e) {
        // an error happened.
        throw e;
    };

    parser.ontext = function (t) {
        //the only text we care about is that inside of <script> and <content>
        if(currentJson && currentJson.$type){
            if(currentJson.$type === 'script'){
                currentJson.content = t;        //I don't think we need a separate expression for this w/ line/col mapping
            }else if(currentJson.$type === 'send'){
                currentJson.content = t;
            }else if(currentJson.$type === 'data'){
                currentJson.content = {
                  $line : currentJson.$line,
                  $column : currentJson.$column,
                  expr : t
                };
            } else {
                currentJson.content = t;
            }
        }
    };

    parser.oncdata = function (t) {
        currentJson.content = t;
    }

    parser.onend = function () {
        //do some scrubbing of root attributes
        delete rootJson.xmlns;
        //delete rootJson.type;     //it can be useful to leave in 'type' === 'scxml'
        delete rootJson.version;

        if(typeof rootJson.datamodel === 'string') delete rootJson.datamodel;       //this would happen if we have, e.g. state.datamodel === 'ecmascript'

        //change the property name of transition event to something nicer
        allTransitions.forEach(function(transition){
            transition.onTransition = transition.actions;
            delete transition.actions;
        });
    };

    parser.write(xmlString).close();

    return rootJson;

}

module.exports = transform;

//for executing diretly under node.js
if(require.main === module){
    //TODO: allow reading from stdin directly
    //TODO: use saxjs's support for streaming API.
    console.log(JSON.stringify(transform(require('fs').readFileSync(process.argv[2],'utf8')),4,4));
}
<commit_msg>Testing.<commit_after>//TODO: resolve data/@src and script/@src. either here, or in a separate module.
//TODO: remove nodejs dependencies
//TODO: decide on a friendly, portable interface to this module. streaming is possible, but maybe not very portable.

var sax = require("sax"),
    strict = true, // set to false for html-mode
    parser;

function merge(o1,o2){
    Object.keys(o2).forEach(function(k){
        o1[k] = o2[k];
    });
    return o1;
}

function getNormalizedAttributeName(attr){
  return attr.uri ? '{' + attr.uri + '}' + attr.local : attr.local;
}

function copyNsAttrObj(o){
    var r = {};
    Object.keys(o).forEach(function(k){
        var attr = o[k];
        r[getNormalizedAttributeName(attr)] = attr.value;
    });
    return r;
}

function transform(xmlString){
    parser = sax.parser(strict,{trim : true, xmlns : true});

    var rootJson,
        currentJson,
        expressionAttributeCache,      //we cache them because in sax-js attributes get processed before the nodes they're attached to,
                                            //and this is the only way we can capture their row/col numbers.
                                            //so when we finally find one, it gets popped off the stack.
        jsonStack = [],
        allTransitions = [];                 //we keep a reference to these so we can clean up the onTransition property later

    function createActionJson(node){
        var action = merge(
                    {
                        $line : parser.line,
                        $column : parser.column,
                        $type: node.local
                    },
                    copyNsAttrObj(node.attributes));

        //console.log('action node',node);

        var actionContainer;
        if(Array.isArray(currentJson)){
            //this will be onExit and onEntry
            currentJson.push(action);
        }else if(currentJson.$type === 'scxml' && action.$type === 'script'){
            //top-level script
            currentJson.rootScripts = currentJson.rootScripts || [];
            currentJson.rootScripts.push(action);
        }else{
            //if it's any other action
            currentJson.actions = currentJson.actions || [];
            currentJson.actions.push(action);
        }

        return currentJson = action;
    }

    function createDataJson(node){
        currentJson =
            merge({
                $line : parser.line,
                $column : parser.column,
                $type : 'data'
            },
            copyNsAttrObj(node.attributes));
        return currentJson;
    }

    function createStateJson(node){
        var state = merge({
                $line : parser.line,
                $column : parser.column
              }, copyNsAttrObj(node.attributes));

        if(state.type){
            state.isDeep = state.type === 'deep' ? true : false;
        }

        //"state" is the default, so you don't need to explicitly write it
        if(node.local !== 'state' && node.local !== 'schema') state.$type = node.local;

        if(currentJson){
            if(!currentJson.states){
                currentJson.states = [];
            }

            currentJson.states.push(state);
        }

        return currentJson = state;
    }

    function createTransitionJson(node){

        var transition = copyNsAttrObj(node.attributes);

        //target can either be a string, an array (for multiple targets, e.g. targeting, or undefined
        if(transition.target){
            //console.log('transition',transition);
            transition.target = transition.target.trim().split(/\s+/);
            if(transition.target.length === 1){
                transition.target = transition.target[0];
            }
        }

        if(currentJson){
            if(!currentJson.transitions){
                currentJson.transitions = [];
            }

            currentJson.transitions.push(transition);
        }

        allTransitions.push(transition);

        return currentJson = transition;
    }

    function createExpression(value){
        return {
            $line : parser.line,
            $column : parser.column,
            expr : value
        };
    }

    var tagActions = {
        "scxml": function(node){
            return rootJson = createStateJson(node);
        },
        "initial": createStateJson,
        "history":createStateJson,
        "state":createStateJson,
        "parallel":createStateJson,
        "final":createStateJson,

        //transitions/action containers
        "transition" : createTransitionJson,

        "onentry":function(node){
            currentJson = currentJson.onEntry = currentJson.onEntry || [];
        },
        "onexit":function(node){
            currentJson = currentJson.onExit = currentJson.onExit || [];
        },

        //actions
        "foreach" : createActionJson,
        "raise" : createActionJson,
        "log": createActionJson,
        "assign": createActionJson,
        "validate":createActionJson,
        "script":createActionJson,
        "cancel":createActionJson,
        //TODO: deal with namelist
        //TODO: decide how to deal with location expressions, as opposed to regular expressions
        "send":createActionJson,

        //children of send
        "param": function(node){
            //TODO: figure out how to deal with param and param/@expr and param/@location
            currentJson.params = currentJson.params || [];
            var attr = copyNsAttrObj(node.attributes);
            currentJson.params.push(attr);
            currentJson = attr;
        },
        "content":function(){
            if (expressionAttributeCache.expr) {
                currentJson.contentexpr = merge({}, expressionAttributeCache.expr);
            }
        },

        //these are treated a bit special - TODO: normalize/decide on a representation
        "if" : createActionJson,
        "elseif" : createActionJson,
        "else" : createActionJson,

        //data
        "datamodel":function(node){
            //console.log('datamodel currentJson',currentJson);
            currentJson = currentJson.datamodel = [];
        },
        "data":function(node){
            //console.log('data currentJson',currentJson);
            currentJson.push(createDataJson(node));
        }

        //TODO: these
        //"invoke":,
        //"finalize":,
        //"donedata":
    };

    expressionAttributeCache = {};  //TODO: put in onstart or something like that

    parser.onopentag = function (node) {
        //console.log("open tag",node.local);

        if(tagActions[node.local]){
            tagActions[node.local](node);

            jsonStack.push(currentJson);
            //console.log('current json now',currentJson,jsonStack.length);

            //merge in the current expression attribute cache
            merge(currentJson,expressionAttributeCache);

            expressionAttributeCache = {};  //clear the expression attribute cache
        } else {
            createActionJson(node);
            jsonStack.push(currentJson);
            merge(currentJson,expressionAttributeCache);
            expressionAttributeCache = {};
        }

    };

    var EXPRESSION_ATTRS = [ 'cond',
                            'array',
                            'location',
                            'namelist',
                            'idlocation'];

    parser.onclosetag = function(tag){
        //console.log("close tag",tag);
        var localName = tag.split(':').pop();
        // if(tagActions[localName]){
            jsonStack.pop();
            currentJson = jsonStack[jsonStack.length - 1];
            if(currentJson){
              currentJson.$endLine = tag.$line;
              currentJson.$endColumn = tag.$column;
            }
            //console.log('current json now',currentJson,jsonStack.length);
        // }
    };

    parser.onattribute = function (attr) {
        //if attribute name ends with 'expr' or is one of the other ones enumerated above
        //then cache him and his position
        if( attr.name.match(/^.*expr$/) ||
            EXPRESSION_ATTRS.indexOf(attr.name) > -1){
            expressionAttributeCache[getNormalizedAttributeName(attr)] = createExpression(attr.value);
        }
    };

    parser.onerror = function (e) {
        // an error happened.
        throw e;
    };

    parser.ontext = function (t) {
        //the only text we care about is that inside of <script> and <content>
        if(currentJson && currentJson.$type){
            if(currentJson.$type === 'script'){
                currentJson.content = t;        //I don't think we need a separate expression for this w/ line/col mapping
            }else if(currentJson.$type === 'send'){
                currentJson.content = t;
            }else if(currentJson.$type === 'data'){
                currentJson.content = {
                  $line : currentJson.$line,
                  $column : currentJson.$column,
                  expr : t
                };
            } else {
                currentJson.content = t;
            }
        }
    };

    parser.oncdata = function (t) {
        currentJson.content = t;
    }

    parser.onend = function () {
        //do some scrubbing of root attributes
        delete rootJson.xmlns;
        //delete rootJson.type;     //it can be useful to leave in 'type' === 'scxml'
        delete rootJson.version;

        if(typeof rootJson.datamodel === 'string') delete rootJson.datamodel;       //this would happen if we have, e.g. state.datamodel === 'ecmascript'

        //change the property name of transition event to something nicer
        allTransitions.forEach(function(transition){
            transition.onTransition = transition.actions;
            delete transition.actions;
        });
    };

    parser.write(xmlString).close();

    return rootJson;

}

module.exports = transform;

//for executing diretly under node.js
if(require.main === module){
    //TODO: allow reading from stdin directly
    //TODO: use saxjs's support for streaming API.
    console.log(JSON.stringify(transform(require('fs').readFileSync(process.argv[2],'utf8')),4,4));
}
<|endoftext|>


<commit_before>$(document).ready(function() {
  var messages = [];
  var socket = io.connect('/');
  var field = document.getElementById("field");
  var sendButton = document.getElementById("send");
  var content = document.getElementById("content");
  var usersDiv = document.getElementById("users");
  var name = document.getElementById("name");

  function toInt(str) {
    str = str || '';
    var int = 0;
    for (var i = 0; i < str.length; i++) {
      int += str.charCodeAt(i);
    }
    return int;
  };

  function nameColor(name) {
    var colors = ["red", "green", "blue", "pink", "purple", "brown", "DarkGreen", "DarkOrange", "Fuchsia", "Orange"];
    var l = colors.length;
    return colors[(toInt(name) % l)]
  }

  socket.on('message', function(data) {
    if (data.message) {
      messages.push(data);
      var html = '';
      for (var i = 0; i < messages.length; i++) {
        if (messages[i].username) {
          html += '' + messages[i].username + ': ';
        }
        html += messages[i].message + '
';
      }
      content.innerHTML = html;
      content.scrollTop = content.scrollHeight;
    } else {
      console.log("There is a problem:", data);
    }
  });

  socket.on("users_updated", function(data) {
    if (data.users) {
      var users = []
      data.users.forEach(function(user) {
        if (user.username) {
          users.push(""+user.username+"");
        } else {
          users.push("Guest")
        }
      })
      var html = users.join(", ")
      usersDiv.innerHTML = html;
    }
  })

  sendButton.onclick = sendMessage = function() {
    if (name.value == "") {
      alert("Please type your name!");
    } else {
      var text = field.value;
      socket.emit('send', { message: text, username: name.value });
      field.value = "";
    }
  };

  $("#field").keyup(function(e) {
    if (e.keyCode == 13) {
      sendMessage();
    }
  });
});
<commit_msg>link it<commit_after>jQuery.fn.autolink = function (target) {
  if (target == null) target = '_parent';
  return this.each( function(){
    var re = /((http|https|ftp):\/\/[\w?=&.\/-;#~%-]+(?![\w\s?&.\/;#~%"=-]*>))/g;
    $(this).html( $(this).html().replace(re, '$1 ') );
  });
}

$(document).ready(function() {
  var messages = [];
  var socket = io.connect('/');
  var field = document.getElementById("field");
  var sendButton = document.getElementById("send");
  var content = document.getElementById("content");
  var usersDiv = document.getElementById("users");
  var name = document.getElementById("name");

  function toInt(str) {
    str = str || '';
    var int = 0;
    for (var i = 0; i < str.length; i++) {
      int += str.charCodeAt(i);
    }
    return int;
  };

  function nameColor(name) {
    var colors = ["red", "green", "blue", "pink", "purple", "brown", "DarkGreen", "DarkOrange", "Fuchsia", "Orange"];
    var l = colors.length;
    return colors[(toInt(name) % l)]
  }

  socket.on('message', function(data) {
    if (data.message) {
      messages.push(data);
      var html = '';
      for (var i = 0; i < messages.length; i++) {
        if (messages[i].username) {
          html += '' + messages[i].username + ': ';
        }
        html += messages[i].message + '
';
      }
      content.innerHTML = html;
      $(content).autolink();
      content.scrollTop = content.scrollHeight;
    } else {
      console.log("There is a problem:", data);
    }
  });

  socket.on("users_updated", function(data) {
    if (data.users) {
      var users = []
      data.users.forEach(function(user) {
        if (user.username) {
          users.push(""+user.username+"");
        } else {
          users.push("Guest")
        }
      })
      var html = users.join(", ")
      usersDiv.innerHTML = html;
    }
  })

  sendButton.onclick = sendMessage = function() {
    if (name.value == "") {
      alert("Please type your name!");
    } else {
      var text = field.value;
      socket.emit('send', { message: text, username: name.value });
      field.value = "";
    }
  };

  $("#field").keyup(function(e) {
    if (e.keyCode == 13) {
      sendMessage();
    }
  });
});
<|endoftext|>


<commit_before>'use strict';

var mongoose = require('mongoose'),
    User = mongoose.model('User'),
    Project = mongoose.model('Project'),
    _ = require('lodash');

/**
 * Does a full text search over the compound index of the user.
 *
 * @param req the request.
 * @param res the response.
 * @param next the next callback.
 */
exports.search = function (req, res, next) {
    var queryString = req.query.q;

    User.find({
            $text: {$search: queryString}
        },
        {
            score: {$meta: 'textScore'}
        })
        .sort(
        {
            score: {$meta: 'textScore'}
        })
        .execAsync()
        .then(function (users) {
            res.json(users);
        })
        .catch(function (err) {
            err.message('Could not search for user');
            next(err);
        });
};

/**
 * Returns all projects for the given user.
 *
 * @param req the request.
 * @param res the response.
 * @param next the next callback.
 */
exports.listProjectsByUser = function (req, res, next) {
    var user = req.user;

    if ((req.principal.role === 'user') && (req.user._id != req.principal._id)) {
        next(new Error('Not authorized'));
    }

    Project.find({users: user._id})
        .then(function (projects) {
            res.json(projects);
        })
        .catch(function (err) {
            err.message = 'Could not load projects for user';
            err.status = 400;
            next(err);
        });
};

/**
 * User middleware to load a user by id.
 *
 * @param req the request.
 * @param res the response.
 * @param next the next callback.
 * @param id the user id.
 */
exports.loadUserByID = function (req, res, next, id) {
    var errorMessage = 'Failed to load user by id: ' + id;

    User.findById(id)
        .then(function (user) {
            if (user) {
                req.user = user;
                next();
            } else {
                next(new Error(errorMessage));
            }
        })
        .catch(function (err) {
            req.log.error(errorMessage);
            next(err);
        });
};<commit_msg>changed error handling for search<commit_after>'use strict';

var mongoose = require('mongoose'),
    User = mongoose.model('User'),
    Project = mongoose.model('Project'),
    _ = require('lodash');

/**
 * Does a full text search over the compound index of the user.
 *
 * @param req the request.
 * @param res the response.
 * @param next the next callback.
 */
exports.search = function (req, res, next) {
    var queryString = req.query.q;

    User.find({
            $text: {$search: queryString}
        },
        {
            score: {$meta: 'textScore'}
        })
        .sort(
        {
            score: {$meta: 'textScore'}
        })
        .execAsync()
        .then(function (users) {
            res.json(users);
        })
        .catch(function (err) {
            next(err);
        });
};

/**
 * Returns all projects for the given user.
 *
 * @param req the request.
 * @param res the response.
 * @param next the next callback.
 */
exports.listProjectsByUser = function (req, res, next) {
    var user = req.user;

    if ((req.principal.role === 'user') && (req.user._id != req.principal._id)) {
        next(new Error('Not authorized'));
    }

    Project.find({users: user._id})
        .then(function (projects) {
            res.json(projects);
        })
        .catch(function (err) {
            err.message = 'Could not load projects for user';
            err.status = 400;
            next(err);
        });
};

/**
 * User middleware to load a user by id.
 *
 * @param req the request.
 * @param res the response.
 * @param next the next callback.
 * @param id the user id.
 */
exports.loadUserByID = function (req, res, next, id) {
    var errorMessage = 'Failed to load user by id: ' + id;

    User.findById(id)
        .then(function (user) {
            if (user) {
                req.user = user;
                next();
            } else {
                next(new Error(errorMessage));
            }
        })
        .catch(function (err) {
            req.log.error(errorMessage);
            next(err);
        });
};<|endoftext|>


<commit_before>moment.lang('ko', {
    months : "1월_2월_3월_4월_5월_6월_7월_8월_9월_10월_11월_12월".split("_"),
    monthsShort : "1월_2월_3월_4월_5월_6월_7월_8월_9월_10월_11월_12월".split("_"),
    weekdays : "일요일_월요일_화요일_수요일_목요일_금요일_토요일".split("_"),
    weekdaysShort : "일_월_화_수_목_금_토".split("_"),
    weekdaysMin : "일_월_화_수_목_금_토".split("_"),
    longDateFormat : {
        LT : "A h시 mm분",
        L : "YYYY.MM.DD",
        LL : "YYYY년 MMMM D일",
        LLL : "YYYY년 MMMM D일 LT",
        LLLL : "YYYY년 MMMM D일 dddd LT"
    },
    meridiem : function (hour, minute, isUpper) {
        return hour < 12 ? '오전' : '오후';
    },
    calendar : {
        sameDay : '오늘 LT',
        nextDay : '내일 LT',
        nextWeek : 'dddd LT',
        lastDay : '어제 LT',
        lastWeek : '지난주 dddd LT',
        sameElse : 'L'
    },
    relativeTime : {
        future : "%s 후",
        past : "%s 전",
        s : "몇초",
        ss : "%d초",
        m : "일분",
        mm : "%d분",
        h : "한시간",
        hh : "%d시간",
        d : "하루",
        dd : "%d일",
        M : "한달",
        MM : "%d달",
        y : "일년",
        yy : "%d년"
    },
    ordinal : '%d일'
});

$(document).ready(function(){
      $(window).bind('hashchange', function(e){
        //대기 표시 한다.
        //여기서 요청을 보내고
        var path = getHash().replace(/^#/, "");
        var branch = encodeURIComponent($("#selected-branch").text());

        $.ajax("code/" + branch + "/!" + path, {
          datatype : "json",
          success : function(data, textStatus, jqXHR){
            updateBreadcrumbs(path);
            switch(data.type){
              case "file" :
                  handleFile(data);
                break;
              case "folder" :
                  handleFolder(data);
                break;
            }
          },
          error : function(){
            $("#codeError").show();
          }
        });
        function handleFile(data){
            //파일을 표시한다.
            $("#commiter").text(data.author);
            if( data.hasOwnProperty("msg") ) $("#commitMessage").text(data.msg);
            if( data.hasOwnProperty("revisionNo") ) $("#revisionNo").text("Revision#: " + data.revisionNo);
            $("#commitDate").text(moment(new Date(data.date)).fromNow());
            $("code").text(data.data);
            $("#rawCode").attr("href", "rawcode"+path);//TODO 현재 동작하지 않음.

            $("#folderView").hide();
            $("#codeView").show();
            $("code").highlight();
        }
        function handleFolder(data){
            //폴더내용을 리스팅 한다.
            $("#commiter").text(data.author);
            if( data.hasOwnProperty("msg") ) $("#commitMessage").text(data.msg);
            if( data.hasOwnProperty("revisionNo") ) $("#revisionNo").text("Revision #: " + data.revisionNo);
            $("#commitDate").text(data.date);

            $(".contents").children().remove();

            for(var name in data.data){
              var info = data.data[name];
              var tablerow = makeTableRow(name, info.msg, info.date, info.author);
              $(".contents").append(tablerow);
            }

            $("#folderView").show();
            $("#codeView").hide();
        }
        function makeTableRow(name, message, date, author){
          if (message.length > 70){
            message = message.substr(0, 70) + "...";
          }
          return $("
")
              .append(
                  $("").append(
                      $("").text(name).attr("href", "#" + (path !== "/" ? path : "") + "/" +name)
                    ).addClass("filename")
                  )
              .append($("").text(message).addClass("message"))
              .append($("").text(moment(new Date(date)).fromNow()).addClass("date"))
              //.append($("").text(author).addClass("author"))
              .append($('	'));
        }
        function updateBreadcrumbs(path){
          var $breadcrumbs = $("#breadcrumbs");
          $($breadcrumbs).html('/');

          var names = path.split("/");
          var str = "#"
          for(var i = 1; i < names.length; i++){
            var name = names[i];
            str += "/" + name;
            $breadcrumbs.append(" > ");
            $("").text(name).attr("href", str).appendTo($breadcrumbs);
          }
        }
      });

      if (!$("#selected-branch").text()) $("#selected-branch").text('HEAD');
      $(window).trigger('hashchange');
  });

  function getHash(){
      //혹시 있을지도 모를 호완성을 위해.
      return location.hash;
  }
  function setHash(hash){
      return location.hash = hash;
  }
  function standizePath(path){
    return "/" + path.split("/").filter(isEmpty).join("/");
    function isEmpty(data){
      if(data == ""){
        return false;
      } else {
        return true;
      }
    }
  }

  $(".branch-item").click(function(ev){
    $("#selected-branch").text($(this).text());
    $(window).trigger('hashchange');
  });

// adaptorForDynatree adaptor function is used for existed function
// Also, it pass the below tests
//
// it("file & folder combine", function() {
//     //Given
//     var target = {
//         "attachment": {
//             "type": "folder",
//             "msg": "add folders",
//             "author": "doortts",
//             "date": "Mon Jan 28 14:21:07 KST 2013"
//         },
//         "favicon.ico": {
//             "type": "file",
//             "msg": "add folders",
//             "author": "doortts",
//             "date": "Mon Jan 28 14:21:07 KST 2013"
//         }
//     };
//     var expected = [
//         {title: "attachment", isFolder: true, isLazy: true},
//         {title: "favicon.ico"}
//     ];
//     //When
//     var result = adaptorForDynatree(target);
//     //Then
//     assert.deepEqual(result, expected);
// });

var result = [];
function adaptorForDynatree(target){
    result = [];
    _.each(target, function(value, key, list){
        if(value.type === "folder") {
            result.push({ title: key, isFolder: true, isLazy: true});
        } else {
            result.push({ title: key});
        }
    })
    return _.sortBy(result, function(elm){
        return -elm.hasOwnProperty("isFolder");
    })
}

function findTreeNode(path){
    var root = $("#folderNav").dynatree("getRoot");
    var nodes = path.split("/");  // "a/b/c" => a, b, c
    var currentNode = root;
    for(var idx in nodes){
        var searchTarget = currentNode.getChildren()
        for( var jdx in  searchTarget){
            if ( searchTarget[jdx].data.title === nodes[idx] ) {
                currentNode = searchTarget[jdx];
                currentNode.expand();
                break;
            }
        }
    }
}

// Traverse the path of selected tree item
function getTreePath(node){
    var path = "";
    if( node.getParent() && node.getParent().data.title !== null ){
        path = getTreePath(node.getParent()) + "/" + node.data.title;
    } else {
        path = node.data.title;
    }
    return path;
}

// initial path loading
var rootPath = "";
var treeSelectorId = "#folderNav";
$(function(){
    var path = getHash().replace(/^#/, "");
    var branch = encodeURIComponent($("#selected-branch").text());
    rootPath = "code/" + branch + "/!/";
    $.ajax({
        url: rootPath,
        success: function(result, textStatus){
            treeInit(adaptorForDynatree(result.data));
            findTreeNode(path.substr(1));  // path.substr(1) "/a/b/c" => "a/b/c"
        }
    });
});

// DynaTree Init function
// see: http://wwwendt.de/tech/dynatree/doc/dynatree-doc.html

function treeInit(initData){
    $(treeSelectorId).dynatree({
        fx: {  height: "toggle", duration: 200 },
        autoFocus: false,
        isLazy: true,
        onLazyRead: function(node){

            $.ajax({
                url: rootPath + getTreePath(node),
                success: function(result, textStatus) {
                    // Called after nodes have been created and the waiting icon was removed.
                    // 'this' is the options for this Ajax request
                    if(result){
                        node.setLazyNodeStatus(DTNodeStatus_Ok);
                        node.addChild(adaptorForDynatree(result.data));
                    }else{
                        // Server returned an error condition: set node status accordingly
                        node.setLazyNodeStatus(DTNodeStatus_Error, {
                            tooltip: "Loading failed",
                            info: result
                        });
                    }
                },
                error: function(node, XMLHttpRequest, textStatus, errorThrown) {
                    // Called on error, after error icon was created.
                    console.log(node);
                },
                cache: false // Append random '_' argument to url to prevent caching.
            });
        },
        onActivate: function(node) {
            // A DynaTreeNode object is passed to the activation handler
            // Note: we also get this event, if persistence is on, and the page is reloaded.
            window.location = "#/" + getTreePath(node);
        },
        title: "/",
        children: initData,
        debugLevel: 0
    });
}

<commit_msg>change breadcrumb home marker  -  / to project_name<commit_after>moment.lang('ko', {
    months : "1월_2월_3월_4월_5월_6월_7월_8월_9월_10월_11월_12월".split("_"),
    monthsShort : "1월_2월_3월_4월_5월_6월_7월_8월_9월_10월_11월_12월".split("_"),
    weekdays : "일요일_월요일_화요일_수요일_목요일_금요일_토요일".split("_"),
    weekdaysShort : "일_월_화_수_목_금_토".split("_"),
    weekdaysMin : "일_월_화_수_목_금_토".split("_"),
    longDateFormat : {
        LT : "A h시 mm분",
        L : "YYYY.MM.DD",
        LL : "YYYY년 MMMM D일",
        LLL : "YYYY년 MMMM D일 LT",
        LLLL : "YYYY년 MMMM D일 dddd LT"
    },
    meridiem : function (hour, minute, isUpper) {
        return hour < 12 ? '오전' : '오후';
    },
    calendar : {
        sameDay : '오늘 LT',
        nextDay : '내일 LT',
        nextWeek : 'dddd LT',
        lastDay : '어제 LT',
        lastWeek : '지난주 dddd LT',
        sameElse : 'L'
    },
    relativeTime : {
        future : "%s 후",
        past : "%s 전",
        s : "몇초",
        ss : "%d초",
        m : "일분",
        mm : "%d분",
        h : "한시간",
        hh : "%d시간",
        d : "하루",
        dd : "%d일",
        M : "한달",
        MM : "%d달",
        y : "일년",
        yy : "%d년"
    },
    ordinal : '%d일'
});

$(document).ready(function(){
      $(window).bind('hashchange', function(e){
        //대기 표시 한다.
        //여기서 요청을 보내고
        var path = getHash().replace(/^#/, "");
        var branch = encodeURIComponent($("#selected-branch").text());

        $.ajax("code/" + branch + "/!" + path, {
          datatype : "json",
          success : function(data, textStatus, jqXHR){
            updateBreadcrumbs(path);
            switch(data.type){
              case "file" :
                  handleFile(data);
                break;
              case "folder" :
                  handleFolder(data);
                break;
            }
          },
          error : function(){
            $("#codeError").show();
          }
        });
        function handleFile(data){
            //파일을 표시한다.
            $("#commiter").text(data.author);
            if( data.hasOwnProperty("msg") ) $("#commitMessage").text(data.msg);
            if( data.hasOwnProperty("revisionNo") ) $("#revisionNo").text("Revision#: " + data.revisionNo);
            $("#commitDate").text(moment(new Date(data.date)).fromNow());
            $("code").text(data.data);
            $("#rawCode").attr("href", "rawcode"+path);//TODO 현재 동작하지 않음.

            $("#folderView").hide();
            $("#codeView").show();
            $("code").highlight();
        }
        function handleFolder(data){
            //폴더내용을 리스팅 한다.
            $("#commiter").text(data.author);
            if( data.hasOwnProperty("msg") ) $("#commitMessage").text(data.msg);
            if( data.hasOwnProperty("revisionNo") ) $("#revisionNo").text("Revision #: " + data.revisionNo);
            $("#commitDate").text(data.date);

            $(".contents").children().remove();

            for(var name in data.data){
              var info = data.data[name];
              var tablerow = makeTableRow(name, info.msg, info.date, info.author);
              $(".contents").append(tablerow);
            }

            $("#folderView").show();
            $("#codeView").hide();
        }
        function makeTableRow(name, message, date, author){
          if (message.length > 70){
            message = message.substr(0, 70) + "...";
          }
          return $("
")
              .append(
                  $("").append(
                      $("").text(name).attr("href", "#" + (path !== "/" ? path : "") + "/" +name)
                    ).addClass("filename")
                  )
              .append($("").text(message).addClass("message"))
              .append($("").text(moment(new Date(date)).fromNow()).addClass("date"))
              //.append($("").text(author).addClass("author"))
              .append($('	'));
        }
        function updateBreadcrumbs(path){
          var $breadcrumbs = $("#breadcrumbs");
          $($breadcrumbs).html(''+project_name+'');

          var names = path.split("/");
          var str = "#"
          for(var i = 1; i < names.length; i++){
            var name = names[i];
            str += "/" + name;
            $breadcrumbs.append("/");
            $("").text(name).attr("href", str).appendTo($breadcrumbs);
          }
        }
      });

      if (!$("#selected-branch").text()) $("#selected-branch").text('HEAD');
      $(window).trigger('hashchange');
  });

  function getHash(){
      //혹시 있을지도 모를 호완성을 위해.
      return location.hash;
  }
  function setHash(hash){
      return location.hash = hash;
  }
  function standizePath(path){
    return "/" + path.split("/").filter(isEmpty).join("/");
    function isEmpty(data){
      if(data == ""){
        return false;
      } else {
        return true;
      }
    }
  }

  $(".branch-item").click(function(ev){
    $("#selected-branch").text($(this).text());
    $(window).trigger('hashchange');
  });

// adaptorForDynatree adaptor function is used for existed function
// Also, it pass the below tests
//
// it("file & folder combine", function() {
//     //Given
//     var target = {
//         "attachment": {
//             "type": "folder",
//             "msg": "add folders",
//             "author": "doortts",
//             "date": "Mon Jan 28 14:21:07 KST 2013"
//         },
//         "favicon.ico": {
//             "type": "file",
//             "msg": "add folders",
//             "author": "doortts",
//             "date": "Mon Jan 28 14:21:07 KST 2013"
//         }
//     };
//     var expected = [
//         {title: "attachment", isFolder: true, isLazy: true},
//         {title: "favicon.ico"}
//     ];
//     //When
//     var result = adaptorForDynatree(target);
//     //Then
//     assert.deepEqual(result, expected);
// });

var result = [];
function adaptorForDynatree(target){
    result = [];
    _.each(target, function(value, key, list){
        if(value.type === "folder") {
            result.push({ title: key, isFolder: true, isLazy: true});
        } else {
            result.push({ title: key});
        }
    })
    return _.sortBy(result, function(elm){
        return -elm.hasOwnProperty("isFolder");
    })
}

function findTreeNode(path){
    var root = $("#folderNav").dynatree("getRoot");
    var nodes = path.split("/");  // "a/b/c" => a, b, c
    var currentNode = root;
    for(var idx in nodes){
        var searchTarget = currentNode.getChildren()
        for( var jdx in  searchTarget){
            if ( searchTarget[jdx].data.title === nodes[idx] ) {
                currentNode = searchTarget[jdx];
                currentNode.expand();
                break;
            }
        }
    }
}

// Traverse the path of selected tree item
function getTreePath(node){
    var path = "";
    if( node.getParent() && node.getParent().data.title !== null ){
        path = getTreePath(node.getParent()) + "/" + node.data.title;
    } else {
        path = node.data.title;
    }
    return path;
}

// initial path loading
var rootPath = "";
var treeSelectorId = "#folderNav";
$(function(){
    var path = getHash().replace(/^#/, "");
    var branch = encodeURIComponent($("#selected-branch").text());
    rootPath = "code/" + branch + "/!/";
    $.ajax({
        url: rootPath,
        success: function(result, textStatus){
            treeInit(adaptorForDynatree(result.data));
            findTreeNode(path.substr(1));  // path.substr(1) "/a/b/c" => "a/b/c"
        }
    });
});

// DynaTree Init function
// see: http://wwwendt.de/tech/dynatree/doc/dynatree-doc.html

function treeInit(initData){
    $(treeSelectorId).dynatree({
        fx: {  height: "toggle", duration: 200 },
        autoFocus: false,
        isLazy: true,
        onLazyRead: function(node){

            $.ajax({
                url: rootPath + getTreePath(node),
                success: function(result, textStatus) {
                    // Called after nodes have been created and the waiting icon was removed.
                    // 'this' is the options for this Ajax request
                    if(result){
                        node.setLazyNodeStatus(DTNodeStatus_Ok);
                        node.addChild(adaptorForDynatree(result.data));
                    }else{
                        // Server returned an error condition: set node status accordingly
                        node.setLazyNodeStatus(DTNodeStatus_Error, {
                            tooltip: "Loading failed",
                            info: result
                        });
                    }
                },
                error: function(node, XMLHttpRequest, textStatus, errorThrown) {
                    // Called on error, after error icon was created.
                    console.log(node);
                },
                cache: false // Append random '_' argument to url to prevent caching.
            });
        },
        onActivate: function(node) {
            // A DynaTreeNode object is passed to the activation handler
            // Note: we also get this event, if persistence is on, and the page is reloaded.
            window.location = "#/" + getTreePath(node);
        },
        title: "/",
        children: initData,
        debugLevel: 0
    });
}

<|endoftext|>


<commit_before>'use strict';

angular.module('app').controller('inlineEditorCtrl', [
  '$scope',
  'queryRunnerService', ($scope, queryRunnerService) => {
    $scope.show = false;
    $scope.doc = _.extend({}, $scope.inlineEditorDoc);
    $scope.newValue = $scope.doc[$scope.inlineEditorKey];

    $scope.$watch('show', (val) => {
      if (val === true) {
        $scope.$emit('inline-editor-show', $scope.doc.id, $scope.inlineEditorKey);
      } else {
        $scope.newValue = $scope.doc[$scope.inlineEditorKey];
      }
    });

    $scope.$on('inline-editor-hide', (docId, propName) => {
      if (docId === $scope.doc.id && $scope.inlineEditorKey !== propName) {
        $scope.show = false;
      }
    });

    $scope.keydown = function($event) {
      if ($event && $event.keyCode === 27) {
        $scope.show = false;
      }
    };

    $scope.saveChanges = () => {
      alert('save changes');

      queryRunnerService.runQuery('db.' + $scope.collection.name + 'updateOne({ id : ' + $scope.doc.id + ' })', $scope.collections)
        .then(() => {

        })
        .catch((err) => {
          alert(err);
        });
    };

    $scope.cancel = function() {
      $scope.show = false;
    };
  }
]);
<commit_msg>use tab cache to get the current collection and run the updateOne query<commit_after>'use strict';

angular.module('app').controller('inlineEditorCtrl', [
  '$scope',
  'queryRunnerService',
  'tabCache',
  'notificationService', ($scope, queryRunnerService, tabCache, notificationService) => {
    $scope.show = false;
    $scope.doc = _.extend({}, $scope.inlineEditorDoc);
    $scope.newValue = $scope.doc[$scope.inlineEditorKey];

    $scope.$watch('show', (val) => {
      if (val === true) {
        $scope.$emit('inline-editor-show', $scope.doc.id, $scope.inlineEditorKey);
      } else {
        $scope.newValue = $scope.doc[$scope.inlineEditorKey];
      }
    });

    $scope.$on('inline-editor-hide', (docId, propName) => {
      if (docId === $scope.doc.id && $scope.inlineEditorKey !== propName) {
        $scope.show = false;
      }
    });

    $scope.keydown = function($event) {
      if ($event && $event.keyCode === 27) {
        $scope.show = false;
      }
    };

    $scope.saveChanges = () => {
      let activeTab = tabCache.getActive();

      if (!activeTab) return;

      let fullQuery = _getFullQuery(activeTab.collection.name);

      queryRunnerService.runQuery(fullQuery, activeTab.database.collections)
        .then(() => {
          notificationService.success('yay!!');
        })
        .catch((err) => {
          notificationService.error(err);
        });
    };

    $scope.cancel = function() {
      $scope.show = false;
    };

    function _getFullQuery(collectionName) {
      return 'db.' + collectionName + '.updateOne({ id : ' + $scope.doc._id.toString() + ' }, { $set : { ' + $scope.inlineEditorKey + ' : \'' + $scope.newValue + '\' } })';
    }
  }
]);
<|endoftext|>