<commit_before>// Copyright (c) 2017 Google Inc. All rights reserved.
// This code may only be used under the BSD style license found at
// http://polymer.github.io/LICENSE.txt
// Code distributed by Google as part of this project is also
// subject to an additional IP rights grant found at
// http://polymer.github.io/PATENTS.txt

var assert = require('assert');
var util = require('./util.js');
var TypeChecker = require('./type-checker.js');

class View {
  constructor(recipe) {
    assert(recipe);
    this._recipe = recipe;
    this._id = undefined;
    this._localName = undefined;
    this._tags = [];
    this._type = undefined;
    this._create = false;
    this._connections = [];
    this._mappedType = undefined;
  }

  clone(recipe) {
    var view = new View(recipe);
    view._id = this._id;
    view._tags = [...this._tags];
    view._type = this._type;
    view._create = this._create;
    view._mappedType = this._mappedType;

    // the connections are re-established when Particles clone their
    // attached ViewConnection objects.
    view._connections = [];
    return view;
  }

  _startNormalize() {
    this._localName = null;
    this._tags.sort();
    // TODO: type?
  }

  _finishNormalize() {
    for (let connection of this._connections) {
      assert(Object.isFrozen(connection));
    }
    this._connections.sort(util.compareComparables);
    Object.freeze(this);
  }

  _compareTo(other) {
    let cmp;
    if ((cmp = util.compareStrings(this._id, other._id)) != 0) return cmp;
    if ((cmp = util.compareStrings(this._localName, other._localName)) != 0) return cmp;
    if ((cmp = util.compareArrays(this._tags, other._tags, util.compareStrings)) != 0) return cmp;
    // TODO: type?
    if ((cmp = util.compareNumbers(this._create, other._create)) != 0) return cmp;
    return 0;
  }

  // a resolved View has either an id or create=true
  get recipe() { return this._recipe; }
  get tags() { return this._tags; } // only tags owned by the view
  set tags(tags) { this._tags = tags; }
  get type() { return this._type; } // nullable
  get id() { return this._id; }
  mapToView(view) { this._id = view.id; this._type = undefined; this._mappedType = view.type;}
  get localName() { return this._localName; }
  set localName(name) { this._localName = name; }
  get create() { return this._create; }
  set create(create) { this._create = create; }
  get connections() { return this._connections } // ViewConnection*

  _isValid() {
    var typeSet = [];
    if (this._mappedType)
      typeSet.push({type: this._mappedType});
    var tags = new Set();
    for (var connection of this._connections) {
      if (connection.type)
        typeSet.push({type: connection.type, direction: connection.direction, connection});
      connection.tags.forEach(tag => tags.add(tag));
    }
    var {type, valid} = TypeChecker.processTypeList(typeSet);
    if (valid) {
      this._type = type.type;
      this._tags.forEach(tag => tags.add(tag));
      this._tags = [...tags];
    }
    return valid;
  }

  isResolved() {
    assert(Object.isFrozen(this));
    return (this._id || this._create) && this._type;
  }

  toString(nameMap) {
    // TODO: type? maybe output in a comment
    let result = [];
    result.push(this.create ? 'create' : 'map');
    if (this.id) {
      result.push(`'${this.id}'`);
    }
    result.push(...this.tags);
    result.push(`as ${(nameMap && nameMap.get(this)) || this.localName}`);
    if (this.type) {
      result.push('#');
      result.push(JSON.stringify(this.type.key));
    }
    return result.join(' ');
  }
}

module.exports = View;
<commit_msg>Smaller type comments<commit_after>// Copyright (c) 2017 Google Inc. All rights reserved.
// This code may only be used under the BSD style license found at
// http://polymer.github.io/LICENSE.txt
// Code distributed by Google as part of this project is also
// subject to an additional IP rights grant found at
// http://polymer.github.io/PATENTS.txt

var assert = require('assert');
var util = require('./util.js');
var TypeChecker = require('./type-checker.js');

class View {
  constructor(recipe) {
    assert(recipe);
    this._recipe = recipe;
    this._id = undefined;
    this._localName = undefined;
    this._tags = [];
    this._type = undefined;
    this._create = false;
    this._connections = [];
    this._mappedType = undefined;
  }

  clone(recipe) {
    var view = new View(recipe);
    view._id = this._id;
    view._tags = [...this._tags];
    view._type = this._type;
    view._create = this._create;
    view._mappedType = this._mappedType;

    // the connections are re-established when Particles clone their
    // attached ViewConnection objects.
    view._connections = [];
    return view;
  }

  _startNormalize() {
    this._localName = null;
    this._tags.sort();
    // TODO: type?
  }

  _finishNormalize() {
    for (let connection of this._connections) {
      assert(Object.isFrozen(connection));
    }
    this._connections.sort(util.compareComparables);
    Object.freeze(this);
  }

  _compareTo(other) {
    let cmp;
    if ((cmp = util.compareStrings(this._id, other._id)) != 0) return cmp;
    if ((cmp = util.compareStrings(this._localName, other._localName)) != 0) return cmp;
    if ((cmp = util.compareArrays(this._tags, other._tags, util.compareStrings)) != 0) return cmp;
    // TODO: type?
    if ((cmp = util.compareNumbers(this._create, other._create)) != 0) return cmp;
    return 0;
  }

  // a resolved View has either an id or create=true
  get recipe() { return this._recipe; }
  get tags() { return this._tags; } // only tags owned by the view
  set tags(tags) { this._tags = tags; }
  get type() { return this._type; } // nullable
  get id() { return this._id; }
  mapToView(view) { this._id = view.id; this._type = undefined; this._mappedType = view.type;}
  get localName() { return this._localName; }
  set localName(name) { this._localName = name; }
  get create() { return this._create; }
  set create(create) { this._create = create; }
  get connections() { return this._connections } // ViewConnection*

  _isValid() {
    var typeSet = [];
    if (this._mappedType)
      typeSet.push({type: this._mappedType});
    var tags = new Set();
    for (var connection of this._connections) {
      if (connection.type)
        typeSet.push({type: connection.type, direction: connection.direction, connection});
      connection.tags.forEach(tag => tags.add(tag));
    }
    var {type, valid} = TypeChecker.processTypeList(typeSet);
    if (valid) {
      this._type = type.type;
      this._tags.forEach(tag => tags.add(tag));
      this._tags = [...tags];
    }
    return valid;
  }

  isResolved() {
    assert(Object.isFrozen(this));
    return (this._id || this._create) && this._type;
  }

  toString(nameMap) {
    // TODO: type? maybe output in a comment
    let result = [];
    result.push(this.create ? 'create' : 'map');
    if (this.id) {
      result.push(`'${this.id}'`);
    }
    result.push(...this.tags);
    result.push(`as ${(nameMap && nameMap.get(this)) || this.localName}`);
    if (this.type) {
      result.push('#');
      result.push(this.type.toString());
    }
    return result.join(' ');
  }
}

module.exports = View;
<|endoftext|>


<commit_before>/*
Copyright 2015 OpenMarket Ltd

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

'use strict';

var React = require('react');

var ImageViewController = require("../../../../src/controllers/atoms/ImageView");

module.exports = React.createClass({
    displayName: 'ImageView',
    mixins: [ImageViewController],

    render: function() {

        // XXX: can't we just do max-width: 80%, max-height: 80% on the CSS?

        var width = this.props.width || 500;
        var height = this.props.height || 500;

        var maxWidth = document.documentElement.clientWidth * 0.8;
        var maxHeight = document.documentElement.clientHeight * 0.8;

        var widthFrac = width / maxWidth;
        var heightFrac = height / maxHeight;

        var displayWidth;
        var displayHeight;
        if (widthFrac > heightFrac) {
            displayWidth = Math.min(width, maxWidth);
            displayHeight = (displayWidth / width) * height;
        } else {
            displayHeight = Math.min(height, maxHeight);
            displayWidth = (displayHeight / height) * width;
        }

        var style = {
            width: displayWidth,
            height: displayHeight
        };

        return (

        );
    }
});
<commit_msg>escape key to cancel imageview popup<commit_after>/*
Copyright 2015 OpenMarket Ltd

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

'use strict';

var React = require('react');

var ImageViewController = require("../../../../src/controllers/atoms/ImageView");

module.exports = React.createClass({
    displayName: 'ImageView',
    mixins: [ImageViewController],

    // XXX: keyboard shortcuts for managing dialogs should be done by the modal dialog base class omehow, surely...
    componentDidMount: function() {
        document.addEventListener("keydown", this.onKeyDown);
    },

    componentWillUnmount: function() {
        document.removeEventListener("keydown", this.onKeyDown);
    },

    onKeyDown: function(ev) {
        if (ev.keyCode == 27) { // escape
            ev.stopPropagation();
            ev.preventDefault();
            this.props.onFinished();
        }
    },

    render: function() {

        // XXX: can't we just do max-width: 80%, max-height: 80% on the CSS?

        var width = this.props.width || 500;
        var height = this.props.height || 500;

        var maxWidth = document.documentElement.clientWidth * 0.8;
        var maxHeight = document.documentElement.clientHeight * 0.8;

        var widthFrac = width / maxWidth;
        var heightFrac = height / maxHeight;

        var displayWidth;
        var displayHeight;
        if (widthFrac > heightFrac) {
            displayWidth = Math.min(width, maxWidth);
            displayHeight = (displayWidth / width) * height;
        } else {
            displayHeight = Math.min(height, maxHeight);
            displayWidth = (displayHeight / height) * width;
        }

        var style = {
            width: displayWidth,
            height: displayHeight
        };

        return (

        );
    }
});
<|endoftext|>


<commit_before>/**
 * Using Rails-like standard naming convention for endpoints.
 * GET     /api/submissions              ->  index
 * POST    /api/submissions              ->  create
 * GET     /api/submissions/:id          ->  show
 * PUT     /api/submissions/:id          ->  update
 * DELETE  /api/submissions/:id          ->  destroy
 */

'use strict';

var _ = require('lodash');
var Submission = require('./submission.model');
var SectionEvent = require('../sectionevent/sectionevent.model');
var Section = require('../section/section.model');
var multiparty = require('multiparty');
var User = require('../user/user.model');
var fs = require('fs');
var mkdirp = require('mkdirp');
var config = require('../../config/environment');
var mongoose = require('bluebird').promisifyAll(require('mongoose'));
var path = require('path');
import async from 'async';

function handleError(res, statusCode) {
  statusCode = statusCode || 500;
  return function(err) {
    res.status(statusCode).send(err);
  };
}

function responseWithResult(res, statusCode) {
  statusCode = statusCode || 200;
  return function(entity) {
    if (entity) {
      res.status(statusCode).json(entity);
    }
  };
}

function handleEntityNotFound(res) {
  return function(entity) {
    if (!entity) {
      res.status(404).end();
      return null;
    }
    return entity;
  };
}

function saveUpdates(updates) {
  return function(entity) {
    var updated = _.merge(entity, updates);
    return updated.saveAsync()
      .spread(function(updated) {
        return updated;
      });
  };
}

function removeEntity(res) {
  return function(entity) {
    if (entity) {
      return entity.removeAsync()
        .then(function() {
          res.status(204).end();
        });
    }
  };
}

function saveSubmissionImage(files, fields){
  var imagePaths = [];
  if (!files){return imagePaths;}
  files.files.forEach(function(file) {
    var name = Date.now().toString()+ '_' + file.path.substring(file.path.lastIndexOf('/')).substring(1);
    var path = config.imageUploadPath  + fields.userId + '/' + fields.eventId;
    var destPath = path + '/' + name;
    imagePaths.push("/api/submissions/image?imgPath=" + destPath);
    if(!fs.existsSync(path)){
      mkdirp.sync(path);
    }
    var is = fs.createReadStream(file.path);
    var os = fs.createWriteStream(destPath);
    is.pipe(os);
    is.on('end', function() {
        fs.unlinkSync(file.path);
    });
  });
  return imagePaths;
}

function withDefault(queryString, defaultValue){
  if (queryString === undefined) return defaultValue;
  else return queryString=="true";
}

// Gets a list of Submissions
exports.index = function(req, res) {
  var withStudents = withDefault(req.query.withStudents, true);
  var withSectionEvent = withDefault(req.query.withSectionEvent, true);
  var withEventInfo = withDefault(req.query.withEventInfo, withSectionEvent);

  function respond(query){
    if (withStudents){
      query.populate("authors");
      query.populate("submitter");
    }
    if (withSectionEvent){
      if (withEventInfo){
        query.populate({
          path: 'sectionEvent',
          model: 'SectionEvent',
          populate: {
            path: 'info',
            model: 'EventInfo'
          }
        });
      }else query.populate("sectionEvent");
    }

    query.execAsync()
      .then(responseWithResult(res))
      .catch(handleError(res));
  }

  if (req.query.onlyInstructor){
    var instructorId = req.query.onlyInstructor == "me" ? req.user._id : req.query.onlyInstructor;
    Section.findAsync({instructors: instructorId})
      .then((instructorSections) => {
        var instructorSectionIds = instructorSections.map((sec) => sec._id);
        return SectionEvent.findAsync({section: {$in: instructorSectionIds}});
      })
      .then((sectionEvents) => {
        var sectionEventIds = sectionEvents.map((evnt) => evnt._id);
        respond(Submission.find({sectionEvent: {$in: sectionEventIds}}))
      });

  }else if (req.query.onlySection){
    SectionEvent.findAsync({section: req.query.onlySection})
      .then((sectionEvents) => {
        var sectionEventIds = sectionEvents.map((evnt) => evnt._id);
        respond(Submission.find({sectionEvent: {$in: sectionEventIds}}));
      });

  }else if (req.query.onlyStudent){
    var studentId = req.query.onlyStudent == 'me' ? req.user._id : req.query.onlyStudent;
    var search = { $or: [{ submitter: studentId}, { authors: {$in: [studentId]} } ]};
    if (req.query.onlySectionEvent) search.sectionEvent = req.query.onlySectionEvent;
    respond(Submission.find(search));

  }else if (req.query.onlySectionEvent){
    respond(Submission.find({sectionEvent: req.query.onlySectionEvent}));

  }else{
    respond(Submission.find());
  }
};

exports.image = function(req, res){
  var imgPath = path.join(__dirname, "../../../", req.query.imgPath);
  res.sendFile(imgPath);
};

// Gets a single Submission from the DB
exports.show = function(req, res) {
  Submission.findByIdAsync(req.params.id)
    .then(handleEntityNotFound(res))
    .then(responseWithResult(res))
    .catch(handleError(res));
};

// Creates a new Submission in the DB
exports.create = function(req, res) {
  // TODO: add back after auth
  // if (req.user._id)
  //   {
  //     req.body.userId = req.user._id;
  //   }
  var imagePaths = saveSubmissionImage(req.files, req.body);
  var submit = {
    images : imagePaths,
    submitter : req.body.userId,
    authors : req.body.authors,
    sectionEvent : req.body.eventId,
    location : {
      geo: {
        coordinates : req.body.coordinates
      }
    },
    time: Date.now(),
    content: req.body.content
  };

  Submission.create(submit, (err, submission) => {
    if (err) return handleError(res);
    return res.json(submission);
  })
};

// Updates an existing Submission in the DB
exports.update = function(req, res) {
  if (req.body._id) {
    delete req.body._id;
  }
  Submission.findByIdAsync(req.params.id)
    .then(handleEntityNotFound(res))
    .then(saveUpdates(req.body))
    .then(responseWithResult(res))
    .catch(handleError(res));
};

// Deletes a Submission from the DB
exports.destroy = function(req, res) {
  Submission.findByIdAsync(req.params.id)
    .then(handleEntityNotFound(res))
    .then(removeEntity(res))
    .catch(handleError(res));
};
<commit_msg>submissions are now sorted by submission date<commit_after>/**
 * Using Rails-like standard naming convention for endpoints.
 * GET     /api/submissions              ->  index
 * POST    /api/submissions              ->  create
 * GET     /api/submissions/:id          ->  show
 * PUT     /api/submissions/:id          ->  update
 * DELETE  /api/submissions/:id          ->  destroy
 */

'use strict';

var _ = require('lodash');
var Submission = require('./submission.model');
var SectionEvent = require('../sectionevent/sectionevent.model');
var Section = require('../section/section.model');
var multiparty = require('multiparty');
var User = require('../user/user.model');
var fs = require('fs');
var mkdirp = require('mkdirp');
var config = require('../../config/environment');
var mongoose = require('bluebird').promisifyAll(require('mongoose'));
var path = require('path');
import async from 'async';

function handleError(res, statusCode) {
  statusCode = statusCode || 500;
  return function(err) {
    res.status(statusCode).send(err);
  };
}

function responseWithResult(res, statusCode) {
  statusCode = statusCode || 200;
  return function(entity) {
    if (entity) {
      res.status(statusCode).json(entity);
    }
  };
}

function handleEntityNotFound(res) {
  return function(entity) {
    if (!entity) {
      res.status(404).end();
      return null;
    }
    return entity;
  };
}

function saveUpdates(updates) {
  return function(entity) {
    var updated = _.merge(entity, updates);
    return updated.saveAsync()
      .spread(function(updated) {
        return updated;
      });
  };
}

function removeEntity(res) {
  return function(entity) {
    if (entity) {
      return entity.removeAsync()
        .then(function() {
          res.status(204).end();
        });
    }
  };
}

function saveSubmissionImage(files, fields){
  var imagePaths = [];
  if (!files){return imagePaths;}
  files.files.forEach(function(file) {
    var name = Date.now().toString()+ '_' + file.path.substring(file.path.lastIndexOf('/')).substring(1);
    var path = config.imageUploadPath  + fields.userId + '/' + fields.eventId;
    var destPath = path + '/' + name;
    imagePaths.push("/api/submissions/image?imgPath=" + destPath);
    if(!fs.existsSync(path)){
      mkdirp.sync(path);
    }
    var is = fs.createReadStream(file.path);
    var os = fs.createWriteStream(destPath);
    is.pipe(os);
    is.on('end', function() {
        fs.unlinkSync(file.path);
    });
  });
  return imagePaths;
}

function withDefault(queryString, defaultValue){
  if (queryString === undefined) return defaultValue;
  else return queryString=="true";
}

// Gets a list of Submissions
exports.index = function(req, res) {
  var withStudents = withDefault(req.query.withStudents, true);
  var withSectionEvent = withDefault(req.query.withSectionEvent, true);
  var withEventInfo = withDefault(req.query.withEventInfo, withSectionEvent);

  function respond(query){
    if (withStudents){
      query.populate("authors");
      query.populate("submitter");
    }
    if (withSectionEvent){
      if (withEventInfo){
        query.populate({
          path: 'sectionEvent',
          model: 'SectionEvent',
          populate: {
            path: 'info',
            model: 'EventInfo'
          }
        });
      }else query.populate("sectionEvent");
    }

    query.sort({time: -1});

    query.execAsync()
      .then(responseWithResult(res))
      .catch(handleError(res));
  }

  if (req.query.onlyInstructor){
    var instructorId = req.query.onlyInstructor == "me" ? req.user._id : req.query.onlyInstructor;
    Section.findAsync({instructors: instructorId})
      .then((instructorSections) => {
        var instructorSectionIds = instructorSections.map((sec) => sec._id);
        return SectionEvent.findAsync({section: {$in: instructorSectionIds}});
      })
      .then((sectionEvents) => {
        var sectionEventIds = sectionEvents.map((evnt) => evnt._id);
        respond(Submission.find({sectionEvent: {$in: sectionEventIds}}))
      });

  }else if (req.query.onlySection){
    SectionEvent.findAsync({section: req.query.onlySection})
      .then((sectionEvents) => {
        var sectionEventIds = sectionEvents.map((evnt) => evnt._id);
        respond(Submission.find({sectionEvent: {$in: sectionEventIds}}));
      });

  }else if (req.query.onlyStudent){
    var studentId = req.query.onlyStudent == 'me' ? req.user._id : req.query.onlyStudent;
    var search = { $or: [{ submitter: studentId}, { authors: {$in: [studentId]} } ]};
    if (req.query.onlySectionEvent) search.sectionEvent = req.query.onlySectionEvent;
    respond(Submission.find(search));

  }else if (req.query.onlySectionEvent){
    respond(Submission.find({sectionEvent: req.query.onlySectionEvent}));

  }else{
    respond(Submission.find());
  }
};

exports.image = function(req, res){
  var imgPath = path.join(__dirname, "../../../", req.query.imgPath);
  res.sendFile(imgPath);
};

// Gets a single Submission from the DB
exports.show = function(req, res) {
  Submission.findByIdAsync(req.params.id)
    .then(handleEntityNotFound(res))
    .then(responseWithResult(res))
    .catch(handleError(res));
};

// Creates a new Submission in the DB
exports.create = function(req, res) {
  // TODO: add back after auth
  // if (req.user._id)
  //   {
  //     req.body.userId = req.user._id;
  //   }
  var imagePaths = saveSubmissionImage(req.files, req.body);
  var submit = {
    images : imagePaths,
    submitter : req.body.userId,
    authors : req.body.authors,
    sectionEvent : req.body.eventId,
    location : {
      geo: {
        coordinates : req.body.coordinates
      }
    },
    time: Date.now(),
    content: req.body.content
  };

  Submission.create(submit, (err, submission) => {
    if (err) return handleError(res);
    return res.json(submission);
  })
};

// Updates an existing Submission in the DB
exports.update = function(req, res) {
  if (req.body._id) {
    delete req.body._id;
  }
  Submission.findByIdAsync(req.params.id)
    .then(handleEntityNotFound(res))
    .then(saveUpdates(req.body))
    .then(responseWithResult(res))
    .catch(handleError(res));
};

// Deletes a Submission from the DB
exports.destroy = function(req, res) {
  Submission.findByIdAsync(req.params.id)
    .then(handleEntityNotFound(res))
    .then(removeEntity(res))
    .catch(handleError(res));
};
<|endoftext|>


<commit_before>$(window).on('load', documentReady);

var menuTree = {};
var topMenuItemTemplate;
var sideMenuItemTemplate;
var thePath;

//Side menu template
sideMenuItemTemplate = '';
sideMenuItemTemplate += '  ';
sideMenuItemTemplate += '
';
sideMenuItemTemplate += '    ';
sideMenuItemTemplate += '   <\/div>';
sideMenuItemTemplate += '';
sideMenuItemTemplate += '
';
sideMenuItemTemplate += '
{{text}}<\/p>';
sideMenuItemTemplate += '   <\/div>';
sideMenuItemTemplate += '';
sideMenuItemTemplate += '
';
sideMenuItemTemplate += '     ';
sideMenuItemTemplate += '   <\/div>';
sideMenuItemTemplate += '  <\/a>';

//Top menu template
topMenuItemTemplate = '';
topMenuItemTemplate += '{{text}}<\/a>';

function documentReady() {
  thePath = window.location.href.substring(theBaseUrl.length, window.location.href.length);
  thePath = thePath.substring(1, thePath.length - 1);

  getMenuFile(menuFileLoaded);

  // Top menu color change
  $(window).on("scroll", function () {
    setColorMenu();
  });

  setColorMenu();
  function setColorMenu() {
    if ($(window).scrollTop() > 0 || thePath !== '/') {
      $("#header").addClass("active");
    } else {
      //remove the background property so it comes transparent again (defined in your css)
      $("#header").removeClass("active");
    }
  }

  //Set smoothscrolling
  $('a[href^="#"]').on('click', function (e) {
    e.preventDefault();

    var target = this.hash;
    var $target = $(target);

    $('html, body').stop().animate({
      'scrollTop': $target.offset().top - 100
    }, 350, 'swing', function () {
      // window.location.hash = target;
    });
  });

  // Set mobile menu
  $('#menu-toggle').on('click', function (e) {
    $('.top-menu-items').toggleClass('open');
  });

  $('top-menu-item').on('click', function(e) {
    $('.top-menu-items').removeClass('open');
  });

  $('pre code').each(function(i, block) {
    var codeText = $(this).text();
    hljs.highlightBlock(block);

    $(this).append('<button class="copy-button" data-clipboard-text=\''+codeText+'\'>
  Copy

  Copied to clipboard
</button>');
  });

  var client = new ZeroClipboard( $('.copy-button') );

  $('.copy-button').click(function() {
    var self = this;
    $(self).addClass('clicked');

    window.setTimeout(function () {
      $(self).removeClass('clicked');
    }, 5000);

  });

}

function getMenuFile(callback) {
  $.getJSON(theBaseUrl + '/menu.json', function (data) {
    callback(data);
  });

  //unsticky menu
  $(window).scroll(function () {
    var offsetTop = $('.footer').offset().top;
    var scrollBottom = $(window).scrollTop() + $(window).height();

    if(scrollBottom >= offsetTop) {
      $('.menu').css('position', 'absolute');
      $('.menu').css('bottom', '0');

    } else {
      $('.menu').css('position', 'fixed');
      $('.menu').css('bottom', '');
    }
  });

  // Set target="_blank" on external links
  $(document.links).filter(function() {
    return this.hostname != window.location.hostname;
  }).attr('target', '_blank');

  //Initi slicknav
  var slickNavConfig = {
    label: '',
    allowParentLinks: false,
    closedSymbol: '',
    openedSymbol: '',
    brand: ''
  }

  $(function(){
    $('#mobile-menu').slicknav(slickNavConfig);
  });

  //check if emails are correct
  var inputfields = $('#mc-embedded-subscribe-form input');

  $('#mc-embedded-subscribe-form input').on('change paste keyup', function () {
    var emailValue = $(this).val();

    //
    if(isEmail(emailValue) && (emailValue !== '')) {
      $(this).parent().find('.button').removeClass('not-active');
    } else {
      $(this).parent().find('.button').addClass('not-active');
    }

  });

  function isEmail(email) {
    var regex = /^([a-zA-Z0-9_.+-])+\@(([a-zA-Z0-9-])+\.)+([a-zA-Z0-9]{2,4})+$/;
    return regex.test(email);
  }

}

function menuFileLoaded(data) {
  //parents are set
  setParents([], data.children);

  // Find path
  findPath(data.children, thePath, function(allActive){
    allActive.forEach(function(oneActive) {
      oneActive.active = true;
    });
  });

  data.children.forEach(function (topMenuItem) {
      var html = Mustache.render(topMenuItemTemplate, topMenuItem);
      var renderedTopMenuItem = $.parseHTML(html);
      if(topMenuItem.active) {
        $(renderedTopMenuItem).addClass('active');
        topMenuItem.children && buildSideMenu(topMenuItem);
      }
      topMenuItem.visible && $('#top-menu-items').append(renderedTopMenuItem);
  });
  //build mobile menu
  buildMobileMenu(data);
}

function buildMobileMenu(data) {
  data.children.forEach(function(menuItem) {
    var mobileListItemTop = createMobileListItem(menuItem.path, menuItem.text);

    var secondLevelUl = $.parseHTML('

');
    menuItem.children && menuItem.children.forEach(function(menuItemSecond) {
      if(menuItemSecond.visible) {
        var mobileSecondListItem = createMobileListItem(menuItemSecond.path, menuItemSecond.text);
        $(secondLevelUl).append(mobileSecondListItem);

        var thirdLevelUl = $.parseHTML('

');
        menuItemSecond.children && menuItemSecond.children.forEach(function (menuItemThird) {
          if(menuItemThird.visible) {
            var mobileThirdListItem = createMobileListItem(menuItemThird.path, menuItemThird.text);
            $(thirdLevelUl).append(mobileThirdListItem);
          }
        });
        menuItemSecond.children && $(mobileSecondListItem).append(thirdLevelUl);
        }
    });
    menuItem.children && $(mobileListItemTop).append(secondLevelUl);
    menuItem.visible && $('#mobile-menu').append(mobileListItemTop);
  });
}

function createMobileListItem(href, text) {
  var html = '
'+text+'
';
  return $.parseHTML(html);
}

function setParents(parents, data) {
  //
  //

  data.forEach(function(dataPoint) {
    dataPoint.parents = parents.slice();
    //
    if(dataPoint.children) {
      var withSelfParents = dataPoint.parents.slice();
      withSelfParents.push(dataPoint);
      //
      setParents(withSelfParents, dataPoint.children);
    }
  });
}

function findPath(data, path, callback) {
  data.forEach(function(dataPoint) {
    if(dataPoint.path === path){
      var copiedArray = dataPoint.parents.slice();
      copiedArray.push(dataPoint);
      callback(copiedArray);
    }

    if(dataPoint.children) {
      findPath(dataPoint.children, path, callback);
    }
  });
}

function buildSideMenu(data) {
  data.children.forEach(function (sideMenuItem) {
      var html = Mustache.render(sideMenuItemTemplate, sideMenuItem);
      var renderedSideMenuItem = $.parseHTML(html);
      sideMenuItem.visible && $('#side-menu').append(renderedSideMenuItem);

      if(sideMenuItem.active) {
        $(renderedSideMenuItem).addClass('active');
        sideMenuItem.children && buildSubSideMenu(sideMenuItem);
      }
  })
}

function buildSubSideMenu(data) {
  data.children.forEach(function (subSideMenuItem) {
      var html = Mustache.render('
{{text}}
', subSideMenuItem);
      var renderedSubSideMenuItem = $.parseHTML(html);

      if (subSideMenuItem.active) {
        $(renderedSubSideMenuItem).addClass('active');
      }

      subSideMenuItem.visible && $('#side-menu').append(renderedSubSideMenuItem);
  })
}

<commit_msg>Slash fix 04<commit_after>$(window).on('load', documentReady);

var menuTree = {};
var topMenuItemTemplate;
var sideMenuItemTemplate;
var thePath;

//Side menu template
sideMenuItemTemplate = '';
sideMenuItemTemplate += '  ';
sideMenuItemTemplate += '
';
sideMenuItemTemplate += '    ';
sideMenuItemTemplate += '   <\/div>';
sideMenuItemTemplate += '';
sideMenuItemTemplate += '
';
sideMenuItemTemplate += '
{{text}}<\/p>';
sideMenuItemTemplate += '   <\/div>';
sideMenuItemTemplate += '';
sideMenuItemTemplate += '
';
sideMenuItemTemplate += '     ';
sideMenuItemTemplate += '   <\/div>';
sideMenuItemTemplate += '  <\/a>';

//Top menu template
topMenuItemTemplate = '';
topMenuItemTemplate += '{{text}}<\/a>';

function documentReady() {
  thePath = window.location.href.substring(theBaseUrl.length, window.location.href.length);
  console.log(thePath);
  thePath = thePath.substring(1, thePath.length - 1);
  console.log(thePath);
  getMenuFile(menuFileLoaded);

  // Top menu color change
  $(window).on("scroll", function () {
    setColorMenu();
  });

  setColorMenu();
  function setColorMenu() {
    if ($(window).scrollTop() > 0 || thePath !== '/') {
      $("#header").addClass("active");
    } else {
      //remove the background property so it comes transparent again (defined in your css)
      $("#header").removeClass("active");
    }
  }

  //Set smoothscrolling
  $('a[href^="#"]').on('click', function (e) {
    e.preventDefault();

    var target = this.hash;
    var $target = $(target);

    $('html, body').stop().animate({
      'scrollTop': $target.offset().top - 100
    }, 350, 'swing', function () {
      // window.location.hash = target;
    });
  });

  // Set mobile menu
  $('#menu-toggle').on('click', function (e) {
    $('.top-menu-items').toggleClass('open');
  });

  $('top-menu-item').on('click', function(e) {
    $('.top-menu-items').removeClass('open');
  });

  $('pre code').each(function(i, block) {
    var codeText = $(this).text();
    hljs.highlightBlock(block);

    $(this).append('<button class="copy-button" data-clipboard-text=\''+codeText+'\'>
  Copy

  Copied to clipboard
</button>');
  });

  var client = new ZeroClipboard( $('.copy-button') );

  $('.copy-button').click(function() {
    var self = this;
    $(self).addClass('clicked');

    window.setTimeout(function () {
      $(self).removeClass('clicked');
    }, 5000);

  });

}

function getMenuFile(callback) {
  $.getJSON(theBaseUrl + '/menu.json', function (data) {
    callback(data);
  });

  //unsticky menu
  $(window).scroll(function () {
    var offsetTop = $('.footer').offset().top;
    var scrollBottom = $(window).scrollTop() + $(window).height();

    if(scrollBottom >= offsetTop) {
      $('.menu').css('position', 'absolute');
      $('.menu').css('bottom', '0');

    } else {
      $('.menu').css('position', 'fixed');
      $('.menu').css('bottom', '');
    }
  });

  // Set target="_blank" on external links
  $(document.links).filter(function() {
    return this.hostname != window.location.hostname;
  }).attr('target', '_blank');

  //Initi slicknav
  var slickNavConfig = {
    label: '',
    allowParentLinks: false,
    closedSymbol: '',
    openedSymbol: '',
    brand: ''
  }

  $(function(){
    $('#mobile-menu').slicknav(slickNavConfig);
  });

  //check if emails are correct
  var inputfields = $('#mc-embedded-subscribe-form input');

  $('#mc-embedded-subscribe-form input').on('change paste keyup', function () {
    var emailValue = $(this).val();

    //
    if(isEmail(emailValue) && (emailValue !== '')) {
      $(this).parent().find('.button').removeClass('not-active');
    } else {
      $(this).parent().find('.button').addClass('not-active');
    }

  });

  function isEmail(email) {
    var regex = /^([a-zA-Z0-9_.+-])+\@(([a-zA-Z0-9-])+\.)+([a-zA-Z0-9]{2,4})+$/;
    return regex.test(email);
  }

}

function menuFileLoaded(data) {
  //parents are set
  setParents([], data.children);

  // Find path
  findPath(data.children, thePath, function(allActive){
    allActive.forEach(function(oneActive) {
      oneActive.active = true;
    });
  });

  data.children.forEach(function (topMenuItem) {
      var html = Mustache.render(topMenuItemTemplate, topMenuItem);
      var renderedTopMenuItem = $.parseHTML(html);
      if(topMenuItem.active) {
        $(renderedTopMenuItem).addClass('active');
        topMenuItem.children && buildSideMenu(topMenuItem);
      }
      topMenuItem.visible && $('#top-menu-items').append(renderedTopMenuItem);
  });
  //build mobile menu
  buildMobileMenu(data);
}

function buildMobileMenu(data) {
  data.children.forEach(function(menuItem) {
    var mobileListItemTop = createMobileListItem(menuItem.path, menuItem.text);

    var secondLevelUl = $.parseHTML('

');
    menuItem.children && menuItem.children.forEach(function(menuItemSecond) {
      if(menuItemSecond.visible) {
        var mobileSecondListItem = createMobileListItem(menuItemSecond.path, menuItemSecond.text);
        $(secondLevelUl).append(mobileSecondListItem);

        var thirdLevelUl = $.parseHTML('

');
        menuItemSecond.children && menuItemSecond.children.forEach(function (menuItemThird) {
          if(menuItemThird.visible) {
            var mobileThirdListItem = createMobileListItem(menuItemThird.path, menuItemThird.text);
            $(thirdLevelUl).append(mobileThirdListItem);
          }
        });
        menuItemSecond.children && $(mobileSecondListItem).append(thirdLevelUl);
        }
    });
    menuItem.children && $(mobileListItemTop).append(secondLevelUl);
    menuItem.visible && $('#mobile-menu').append(mobileListItemTop);
  });
}

function createMobileListItem(href, text) {
  var html = '
'+text+'
';
  return $.parseHTML(html);
}

function setParents(parents, data) {
  //
  //

  data.forEach(function(dataPoint) {
    dataPoint.parents = parents.slice();
    //
    if(dataPoint.children) {
      var withSelfParents = dataPoint.parents.slice();
      withSelfParents.push(dataPoint);
      //
      setParents(withSelfParents, dataPoint.children);
    }
  });
}

function findPath(data, path, callback) {
  data.forEach(function(dataPoint) {
    if(dataPoint.path === path){
      var copiedArray = dataPoint.parents.slice();
      copiedArray.push(dataPoint);
      callback(copiedArray);
    }

    if(dataPoint.children) {
      findPath(dataPoint.children, path, callback);
    }
  });
}

function buildSideMenu(data) {
  data.children.forEach(function (sideMenuItem) {
      var html = Mustache.render(sideMenuItemTemplate, sideMenuItem);
      var renderedSideMenuItem = $.parseHTML(html);
      sideMenuItem.visible && $('#side-menu').append(renderedSideMenuItem);

      if(sideMenuItem.active) {
        $(renderedSideMenuItem).addClass('active');
        sideMenuItem.children && buildSubSideMenu(sideMenuItem);
      }
  })
}

function buildSubSideMenu(data) {
  data.children.forEach(function (subSideMenuItem) {
      var html = Mustache.render('
{{text}}
', subSideMenuItem);
      var renderedSubSideMenuItem = $.parseHTML(html);

      if (subSideMenuItem.active) {
        $(renderedSubSideMenuItem).addClass('active');
      }

      subSideMenuItem.visible && $('#side-menu').append(renderedSubSideMenuItem);
  })
}

<|endoftext|>


<commit_before>var themeData = [
  {
    name: 'Creative Tim',

    description: `[Creative Tim](https://creative-tim.com?affiliate_id=116187) のパートナーによって作られた以下の例を見ると、実世界のアプリケーションがどのように構築されるのか、その背後にある技術スタック、ここまで学んだ主要なコンセプトが実世界のアプリケーションにどう適用されるのか、を知ることができます。`,
    seeMoreUrl: 'https://www.creative-tim.com/bootstrap-themes/vuejs-themes?affiliate_id=116187',
    products: [
      {
        name: 'Vue Argon Design System',
        price: 0,
        description: 'Free Vue.js Design System',
        url: 'https://www.creative-tim.com/product/vue-argon-design-system?affiliate_id=116187',
        image: 'https://raw.githubusercontent.com/creativetimofficial/public-assets/master/vue-argon-design-system/vue-argon-design-system.jpg'
      },
      {
        name: 'Vue Black Dashboard Pro',
        price: 59,
        description: 'Premium Vue.js Admin Template',
        url: 'https://www.creative-tim.com/product/vue-black-dashboard-pro?affiliate_id=116187',
        image: 'https://raw.githubusercontent.com/creativetimofficial/public-assets/master/vue-black-dashboard-pro/vue-black-dashboard-pro.jpg'
      },
      {
        name: 'Vue Paper Dashboard 2 Pro',
        price: 59,
        description: 'Premium Vue.js Admin Template',
        url: 'https://www.creative-tim.com/product/vue-paper-dashboard-2-pro?affiliate_id=116187',
        image: 'https://raw.githubusercontent.com/creativetimofficial/public-assets/master/vue-black-dashboard-pro/vue-black-dashboard-pro.jpg'
      },
      {
        name: 'Vue Material Kit',
        price: 0,
        description: 'Free Vue.js UI Kit',
        url: 'https://www.creative-tim.com/product/vue-material-kit?affiliate_id=116187',
        image: 'https://raw.githubusercontent.com/creativetimofficial/public-assets/master/vue-material-kit/vue-material-kit.jpg'
      },
      {
        name: 'Vue Black Dashboard',
        price: 0,
        description: 'Free Vue.js Admin Template',
        url: 'https://www.creative-tim.com/product/vue-black-dashboard?affiliate_id=116187',
        image: 'https://raw.githubusercontent.com/creativetimofficial/public-assets/master/vue-black-dashboard/vue-black-dashboard.jpg'
      },
      {
        name: 'Vue Now UI Kit Pro',
        price: 79,
        description: 'Premium Vue.js UI Kit',
        url: 'https://www.creative-tim.com/product/vue-now-ui-kit-pro?affiliate_id=116187',
        image: 'https://raw.githubusercontent.com/creativetimofficial/public-assets/master/vue-now-ui-kit-pro/vue-now-ui-kit-pro.jpg'
      },
      {
        name: 'Vue Now UI Dashboard Pro',
        price: 59,
        description: 'Premium Vue.js Admin Template',
        url: 'https://www.creative-tim.com/product/vue-now-ui-dashboard-pro?affiliate_id=116187',
        image: 'https://raw.githubusercontent.com/creativetimofficial/public-assets/master/vue-now-ui-dashboard-pro/vue-now-ui-dashboard-pro.jpg'
      },
      {
        name: 'Vue Now UI Kit',
        price: 0,
        description: 'Free Vue.js UI Kit',
        url: 'https://www.creative-tim.com/product/vue-now-ui-kit?affiliate_id=116187',
        image: 'https://raw.githubusercontent.com/creativetimofficial/public-assets/master/vue-now-ui-kit/vue-now-ui-kit.jpg'
      },
      {
        name: 'Vue Light Bootstrap Dashboard Pro',
        price: 49,
        description: 'Premium Vue.js Admin Template',
        url: 'https://www.creative-tim.com/product/vue-light-bootstrap-dashboard-pro?affiliate_id=116187',
        image: 'https://raw.githubusercontent.com/creativetimofficial/public-assets/master/vue-light-bootstrap-dashboard-pro/vue-light-bootstrap-dashboard-pro.jpg'
      },
      {
        name: 'Vue Material Dashboard Pro',
        price: 59,
        description: 'Premium Vue.js Admin Template',
        url: 'https://www.creative-tim.com/product/vue-material-dashboard-pro?affiliate_id=116187',
        image: 'https://raw.githubusercontent.com/creativetimofficial/public-assets/master/vue-material-dashboard-pro/vue-material-dashboard-pro.jpg'
      },
      {
        name: 'Vue Material Kit Pro',
        price: 89,
        description: 'Premium Vue.js UI Kit',
        url: 'https://www.creative-tim.com/product/vue-material-kit-pro?affiliate_id=116187',
        image: 'https://raw.githubusercontent.com/creativetimofficial/public-assets/master/vue-material-kit-pro/vue-material-kit-pro.jpg'
      },
      {
        name: 'Vue Light Bootstrap Dashboard',
        price: 0,
        description: 'Free Vue.js Admin Template',
        url: 'https://www.creative-tim.com/product/vue-light-bootstrap-dashboard?affiliate_id=116187',
        image: 'https://raw.githubusercontent.com/creativetimofficial/public-assets/master/vue-light-bootstrap-dashboard/vue-light-bootstrap-dashboard.jpg'
      }
    ]
  },
  {
    name: "MDBootstrap",
    description: `Check out the themes, UI Kits and plugins below built by our partners from [MDBootstrap](https://mdbootstrap.com/?utm_ref_id=82665). Learn how to use Vue in professional projects along such technologies as Bootstrap. Templates & ready components make your development faster and more efficient.`,
    seeMoreUrl: "https://mdbootstrap.com/docs/vue?utm_ref_id=82665",
    products: [
      {
        url: "https://mdbootstrap.com/products/vue-ui-kit?utm_ref_id=82665",
        name: "Vue Material Bootstrap Pro",
        description: "Premium Vue.js UI Kit",
        image: "https://mdbootstrap.com/img/Marketing/mdb-press-pack/vuejs/ui-kit.jpg",
        price: 99
      },
      {
        url: "https://mdbootstrap.com/docs/vue?utm_ref_id=82665",
        name: "Vue Material Bootstrap Kit",
        description: "Free Vue.js UI Kit",
        image: "https://mdbootstrap.com/img/Marketing/mdb-press-pack/vuejs/ui-kit.jpg",
        price: 0
      },
      {
        url: "https://mdbootstrap.com/products/vue-admin-dashboard?utm_ref_id=82665",
        name: " Vue Admin Bootstrap Pro",
        description: "Premium Vue.js Templates Pack",
        image: "https://mdbootstrap.com/img/Marketing/mdb-press-pack/vuejs/admin-pack.jpg",
        price: 79
      },
      {
        url: "https://mdbootstrap.com/freebies/vue/admin-dashboard?utm_ref_id=82665",
        name: "Vue Admin Bootstrap",
        description: "Free Vue.js Templates Pack",
        image: "https://mdbootstrap.com/img/Marketing/mdb-press-pack/vuejs/admin-pack.jpg",
        price: 0
      }
    ]
  }

]<commit_msg>Add PrimeVue Themes (#2458) (#2006)<commit_after>var themeData = [
  {
    name: 'Creative Tim',

    description: `[Creative Tim](https://creative-tim.com?affiliate_id=116187) のパートナーによって作られた以下の例を見ると、実世界のアプリケーションがどのように構築されるのか、その背後にある技術スタック、ここまで学んだ主要なコンセプトが実世界のアプリケーションにどう適用されるのか、を知ることができます。`,
    seeMoreUrl: 'https://www.creative-tim.com/bootstrap-themes/vuejs-themes?affiliate_id=116187',
    products: [
      {
        name: 'Vue Argon Design System',
        price: 0,
        description: 'Free Vue.js Design System',
        url: 'https://www.creative-tim.com/product/vue-argon-design-system?affiliate_id=116187',
        image: 'https://raw.githubusercontent.com/creativetimofficial/public-assets/master/vue-argon-design-system/vue-argon-design-system.jpg'
      },
      {
        name: 'Vue Black Dashboard Pro',
        price: 59,
        description: 'Premium Vue.js Admin Template',
        url: 'https://www.creative-tim.com/product/vue-black-dashboard-pro?affiliate_id=116187',
        image: 'https://raw.githubusercontent.com/creativetimofficial/public-assets/master/vue-black-dashboard-pro/vue-black-dashboard-pro.jpg'
      },
      {
        name: 'Vue Paper Dashboard 2 Pro',
        price: 59,
        description: 'Premium Vue.js Admin Template',
        url: 'https://www.creative-tim.com/product/vue-paper-dashboard-2-pro?affiliate_id=116187',
        image: 'https://raw.githubusercontent.com/creativetimofficial/public-assets/master/vue-black-dashboard-pro/vue-black-dashboard-pro.jpg'
      },
      {
        name: 'Vue Material Kit',
        price: 0,
        description: 'Free Vue.js UI Kit',
        url: 'https://www.creative-tim.com/product/vue-material-kit?affiliate_id=116187',
        image: 'https://raw.githubusercontent.com/creativetimofficial/public-assets/master/vue-material-kit/vue-material-kit.jpg'
      },
      {
        name: 'Vue Black Dashboard',
        price: 0,
        description: 'Free Vue.js Admin Template',
        url: 'https://www.creative-tim.com/product/vue-black-dashboard?affiliate_id=116187',
        image: 'https://raw.githubusercontent.com/creativetimofficial/public-assets/master/vue-black-dashboard/vue-black-dashboard.jpg'
      },
      {
        name: 'Vue Now UI Kit Pro',
        price: 79,
        description: 'Premium Vue.js UI Kit',
        url: 'https://www.creative-tim.com/product/vue-now-ui-kit-pro?affiliate_id=116187',
        image: 'https://raw.githubusercontent.com/creativetimofficial/public-assets/master/vue-now-ui-kit-pro/vue-now-ui-kit-pro.jpg'
      },
      {
        name: 'Vue Now UI Dashboard Pro',
        price: 59,
        description: 'Premium Vue.js Admin Template',
        url: 'https://www.creative-tim.com/product/vue-now-ui-dashboard-pro?affiliate_id=116187',
        image: 'https://raw.githubusercontent.com/creativetimofficial/public-assets/master/vue-now-ui-dashboard-pro/vue-now-ui-dashboard-pro.jpg'
      },
      {
        name: 'Vue Now UI Kit',
        price: 0,
        description: 'Free Vue.js UI Kit',
        url: 'https://www.creative-tim.com/product/vue-now-ui-kit?affiliate_id=116187',
        image: 'https://raw.githubusercontent.com/creativetimofficial/public-assets/master/vue-now-ui-kit/vue-now-ui-kit.jpg'
      },
      {
        name: 'Vue Light Bootstrap Dashboard Pro',
        price: 49,
        description: 'Premium Vue.js Admin Template',
        url: 'https://www.creative-tim.com/product/vue-light-bootstrap-dashboard-pro?affiliate_id=116187',
        image: 'https://raw.githubusercontent.com/creativetimofficial/public-assets/master/vue-light-bootstrap-dashboard-pro/vue-light-bootstrap-dashboard-pro.jpg'
      },
      {
        name: 'Vue Material Dashboard Pro',
        price: 59,
        description: 'Premium Vue.js Admin Template',
        url: 'https://www.creative-tim.com/product/vue-material-dashboard-pro?affiliate_id=116187',
        image: 'https://raw.githubusercontent.com/creativetimofficial/public-assets/master/vue-material-dashboard-pro/vue-material-dashboard-pro.jpg'
      },
      {
        name: 'Vue Material Kit Pro',
        price: 89,
        description: 'Premium Vue.js UI Kit',
        url: 'https://www.creative-tim.com/product/vue-material-kit-pro?affiliate_id=116187',
        image: 'https://raw.githubusercontent.com/creativetimofficial/public-assets/master/vue-material-kit-pro/vue-material-kit-pro.jpg'
      },
      {
        name: 'Vue Light Bootstrap Dashboard',
        price: 0,
        description: 'Free Vue.js Admin Template',
        url: 'https://www.creative-tim.com/product/vue-light-bootstrap-dashboard?affiliate_id=116187',
        image: 'https://raw.githubusercontent.com/creativetimofficial/public-assets/master/vue-light-bootstrap-dashboard/vue-light-bootstrap-dashboard.jpg'
      }
    ]
  },
  {
    name: "MDBootstrap",
    description: `Check out the themes, UI Kits and plugins below built by our partners from [MDBootstrap](https://mdbootstrap.com/?utm_ref_id=82665). Learn how to use Vue in professional projects along such technologies as Bootstrap. Templates & ready components make your development faster and more efficient.`,
    seeMoreUrl: "https://mdbootstrap.com/docs/vue?utm_ref_id=82665",
    products: [
      {
        url: "https://mdbootstrap.com/products/vue-ui-kit?utm_ref_id=82665",
        name: "Vue Material Bootstrap Pro",
        description: "Premium Vue.js UI Kit",
        image: "https://mdbootstrap.com/img/Marketing/mdb-press-pack/vuejs/ui-kit.jpg",
        price: 99
      },
      {
        url: "https://mdbootstrap.com/docs/vue?utm_ref_id=82665",
        name: "Vue Material Bootstrap Kit",
        description: "Free Vue.js UI Kit",
        image: "https://mdbootstrap.com/img/Marketing/mdb-press-pack/vuejs/ui-kit.jpg",
        price: 0
      },
      {
        url: "https://mdbootstrap.com/products/vue-admin-dashboard?utm_ref_id=82665",
        name: " Vue Admin Bootstrap Pro",
        description: "Premium Vue.js Templates Pack",
        image: "https://mdbootstrap.com/img/Marketing/mdb-press-pack/vuejs/admin-pack.jpg",
        price: 79
      },
      {
        url: "https://mdbootstrap.com/freebies/vue/admin-dashboard?utm_ref_id=82665",
        name: "Vue Admin Bootstrap",
        description: "Free Vue.js Templates Pack",
        image: "https://mdbootstrap.com/img/Marketing/mdb-press-pack/vuejs/admin-pack.jpg",
        price: 0
      }
    ]
  },
  {
    name: 'PrimeVue',
    description: `The open-source UI component library [PrimeVue](https://www.primefaces.org/primevue/#/?af_id=4218) offers over 50 flexible components to build your apps with! They have a ton of different component themes and Vue-CLI application templates available to get the look&feel that suits you best.`,
    seeMoreUrl: 'https://www.primefaces.org/primevue/#/?af_id=4218',
    products: [
      {
        name: 'Sapphire',
        price: 79,
        description: 'Materal Design Admin Template',
        url: 'https://www.primefaces.org/layouts/sapphire-vue?af_id=4218',
        image: 'https://www.primefaces.org/vue-templates/sapphire.jpg'
      },
      {
        name: 'Avalon',
        price: 79,
        description: 'Bootstrap Inspired Admin Template',
        url: 'https://www.primefaces.org/layouts/avalon-vue?af_id=4218',
        image: 'https://www.primefaces.org/vue-templates/avalon.jpg'
      },
      {
        name: 'Serenity',
        price: 79,
        description: 'Materal Design Admin Template',
        url: 'https://www.primefaces.org/layouts/serenity-vue?af_id=4218',
        image: 'https://www.primefaces.org/vue-templates/serenity.jpg'
      },
      {
        name: 'Apollo',
        price: 79,
        description: 'Admin Template with a Dark Mode',
        url: 'https://www.primefaces.org/layouts/apollo-vue?af_id=4218',
        image: 'https://www.primefaces.org/vue-templates/apollo.jpg'
      },
      {
        name: 'Babylon',
        price: 79,
        description: 'Admin Template with Extensive Options',
        url: 'https://www.primefaces.org/layouts/babylon-vue?af_id=4218',
        image: 'https://www.primefaces.org/vue-templates/babylon.jpg'
      },
      {
        name: 'Roma',
        price: 59,
        description: 'Admin Template with a Clean Design System',
        url: 'https://www.primefaces.org/layouts/roma-vue?af_id=4218',
        image: 'https://www.primefaces.org/vue-templates/roma.jpg'
      },
      {
        name: 'Sigma',
        price: 0,
        description: 'Free Admin Template',
        url: 'https://www.primefaces.org/sigma-vue/#/?af_id=4218',
        image: 'https://www.primefaces.org/vue-templates/sigma.jpg'
      }
    ]
  }

]<|endoftext|>


<commit_before>var THREEx = THREEx || {}

THREEx.ArToolkitSource = function(parameters){
	// handle default parameters
	this.parameters = {
		// type of source - ['webcam', 'image', 'video']
		sourceType : parameters.sourceType !== undefined ? parameters.sourceType : 'webcam',
		// url of the source - valid if sourceType = image|video
		sourceUrl : parameters.sourceUrl !== undefined ? parameters.sourceUrl : null,

		// resolution of at which we initialize in the source image
		sourceWidth: parameters.sourceWidth !== undefined ? parameters.sourceWidth : 640,
		sourceHeight: parameters.sourceHeight !== undefined ? parameters.sourceHeight : 480,
		// resolution displayed for the source
		displayWidth: parameters.displayWidth !== undefined ? parameters.displayWidth : 640,
		displayHeight: parameters.displayHeight !== undefined ? parameters.displayHeight : 480,
	}

	this.ready = false
        this.domElement = null
}

//////////////////////////////////////////////////////////////////////////////
//		Code Separator
//////////////////////////////////////////////////////////////////////////////
THREEx.ArToolkitSource.prototype.init = function(onReady){
	var _this = this

        if( this.parameters.sourceType === 'image' ){
                var domElement = this._initSourceImage(onSourceReady)
        }else if( this.parameters.sourceType === 'video' ){
                var domElement = this._initSourceVideo(onSourceReady)
        }else if( this.parameters.sourceType === 'webcam' ){
                var domElement = this._initSourceWebcam(onSourceReady)
        }else{
                console.assert(false)
        }

	// attach
        this.domElement = domElement
        this.domElement.style.position = 'absolute'
        this.domElement.style.top = '0px'
        this.domElement.style.zIndex = '-2'
        this.domElement.style.zIndex = '-2'

	return this
        function onSourceReady(){
		document.body.appendChild(_this.domElement);

		_this.ready = true

		onReady && onReady()
        }
}

////////////////////////////////////////////////////////////////////////////////
//          init image source
////////////////////////////////////////////////////////////////////////////////

THREEx.ArToolkitSource.prototype._initSourceImage = function(onReady) {
	// TODO make it static
        var domElement = document.createElement('img')
	domElement.src = this.parameters.sourceUrl

	domElement.width = this.parameters.sourceWidth
	domElement.height = this.parameters.sourceHeight
	domElement.style.width = this.parameters.displayWidth+'px'
	domElement.style.height = this.parameters.displayHeight+'px'

	// wait until the video stream is ready
	var interval = setInterval(function() {
		if (!domElement.naturalWidth)	return;
		onReady()
		clearInterval(interval)
	}, 1000/50);

	return domElement
}

////////////////////////////////////////////////////////////////////////////////
//          init video source
////////////////////////////////////////////////////////////////////////////////

THREEx.ArToolkitSource.prototype._initSourceVideo = function(onReady) {
	// TODO make it static
	var domElement = document.createElement('video');
	domElement.src = this.parameters.sourceUrl

	domElement.style.objectFit = 'initial'

	domElement.autoplay = true;
	domElement.webkitPlaysinline = true;
	domElement.controls = false;
	domElement.loop = true;
	domElement.muted = true

	// trick to trigger the video on android
	document.body.addEventListener('click', function onClick(){
		document.body.removeEventListener('click', onClick);
		domElement.play()
	})

	domElement.width = this.parameters.sourceWidth
	domElement.height = this.parameters.sourceHeight
	domElement.style.width = this.parameters.displayWidth+'px'
	domElement.style.height = this.parameters.displayHeight+'px'

	// wait until the video stream is ready
	var interval = setInterval(function() {
		if (!domElement.videoWidth)	return;
		onReady()
		clearInterval(interval)
	}, 1000/50);
	return domElement
}

////////////////////////////////////////////////////////////////////////////////
//          handle webcam source
////////////////////////////////////////////////////////////////////////////////

THREEx.ArToolkitSource.prototype._initSourceWebcam = function(onReady) {
	var _this = this
	// TODO make it static
	navigator.getUserMedia  = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia;

	var domElement = document.createElement('video');
	domElement.style.width = this.parameters.displayWidth+'px'
	domElement.style.height = this.parameters.displayHeight+'px'

	if (navigator.getUserMedia === undefined ){
		alert("WebRTC issue! navigator.getUserMedia not present in your browser");
	}
	if (navigator.mediaDevices === undefined || navigator.mediaDevices.enumerateDevices === undefined ){
		alert("WebRTC issue! navigator.mediaDevices.enumerateDevices not present in your browser");
	}

	navigator.mediaDevices.enumerateDevices().then(function(devices) {
                // define getUserMedia() constraints
                var constraints = {
			audio: false,
			video: {
				mandatory: {
					maxWidth: _this.parameters.sourceWidth,
					maxHeight: _this.parameters.sourceHeight
		    		}
		  	}
                }

		// TODO super unclear how to get the backward facing camera...
		// use heuristic - on chrome android current algo is working
		//
		// on macosx it isnt. figure out the algo, and do if(macosx)
		// - with one or two camera
		//
		// some issue on window

		/**
		 * how to test
		 * - one or two camera on macbook
		 * - my phone
		 */
		var runOnMobile = 'ontouchstart' in window ? true : false
		if( runOnMobile === true ){
			pickDeviceAndroid()
		}else{
			pickDeviceMacosx()
		}

		function pickDeviceAndroid(){
			devices.forEach(function(device) {
				if( device.kind !== 'videoinput' )	return
				constraints.video.optional = [{sourceId: device.deviceId}]
			});
		}
		function pickDeviceMacosx(){
			devices.forEach(function(device) {
				if( device.kind !== 'videoinput' )	return

				if( constraints.video.optional !== undefined )	return
				constraints.video.optional = [{sourceId: device.deviceId}]
			});
		}

		// OLD API
                // it it finds the videoSource 'environment', modify constraints.video
                // for (var i = 0; i != sourceInfos.length; ++i) {
                //         var sourceInfo = sourceInfos[i];
                //         if(sourceInfo.kind == "video" && sourceInfo.facing == "environment") {
                //                 constraints.video.optional = [{sourceId: sourceInfo.id}]
                //         }
                // }

		navigator.getUserMedia(constraints, function success(stream) {
			// console.log('success', stream);
			domElement.src = window.URL.createObjectURL(stream);
			// to start the video, when it is possible to start it only on userevent. like in android
			document.body.addEventListener('click', function(){
				domElement.play();
			})
			// domElement.play();

			// wait until the video stream is ready
			var interval = setInterval(function() {
				if (!domElement.videoWidth)	return;
				onReady()
				clearInterval(interval)
			}, 1000/50);
		}, function(error) {
			console.log("Can't access user media", error);
			alert("Can't access user media :()");
		});
	}).catch(function(err) {
		console.log(err.name + ": " + err.message);
	});

	return domElement
}

////////////////////////////////////////////////////////////////////////////////
//          handle resize
////////////////////////////////////////////////////////////////////////////////

THREEx.ArToolkitSource.prototype.onResize = function(mirrorDomElements){
	var _this = this
	var screenWidth = window.innerWidth
	var screenHeight = window.innerHeight

	// compute sourceWidth, sourceHeight
	if( this.domElement.nodeName === "IMG" ){
		var sourceWidth = this.domElement.naturalWidth
		var sourceHeight = this.domElement.naturalHeight
	}else if( this.domElement.nodeName === "VIDEO" ){
		var sourceWidth = this.domElement.videoWidth
		var sourceHeight = this.domElement.videoHeight
	}else{
		console.assert(false)
	}

	// compute sourceAspect
	var sourceAspect = sourceWidth / sourceHeight
	// compute screenAspect
	var screenAspect = screenWidth / screenHeight

	// if screenAspect < sourceAspect, then change the width, else change the height
	if( screenAspect < sourceAspect ){
		// compute newWidth and set .width/.marginLeft
		var newWidth = sourceAspect * screenHeight
		this.domElement.style.width = newWidth+'px'
		this.domElement.style.marginLeft = -(newWidth-screenWidth)/2+'px'

		// init style.height/.marginTop to normal value
		this.domElement.style.height = screenHeight+'px'
		this.domElement.style.marginTop = '0px'
	}else{
		// compute newHeight and set .height/.marginTop
		var newHeight = 1 / (sourceAspect / screenWidth)
		this.domElement.style.height = newHeight+'px'
		this.domElement.style.marginTop = -(newHeight-screenHeight)/2+'px'

		// init style.width/.marginLeft to normal value
		this.domElement.style.width = screenWidth+'px'
		this.domElement.style.marginLeft = '0px'
	}

	// honor default parameters
	if( mirrorDomElements === undefined )	mirrorDomElements = []
	if( mirrorDomElements instanceof Array === false )	mirrorDomElements = [mirrorDomElements]

	// Mirror _this.domElement.style to mirrorDomElements
	mirrorDomElements.forEach(function(domElement){
		domElement.style.width = _this.domElement.style.width
		domElement.style.height = _this.domElement.style.height
		domElement.style.marginLeft = _this.domElement.style.marginLeft
		domElement.style.marginTop = _this.domElement.style.marginTop
	})
}
<commit_msg>fix css for fullscreen<commit_after>var THREEx = THREEx || {}

THREEx.ArToolkitSource = function(parameters){
	// handle default parameters
	this.parameters = {
		// type of source - ['webcam', 'image', 'video']
		sourceType : parameters.sourceType !== undefined ? parameters.sourceType : 'webcam',
		// url of the source - valid if sourceType = image|video
		sourceUrl : parameters.sourceUrl !== undefined ? parameters.sourceUrl : null,

		// resolution of at which we initialize in the source image
		sourceWidth: parameters.sourceWidth !== undefined ? parameters.sourceWidth : 640,
		sourceHeight: parameters.sourceHeight !== undefined ? parameters.sourceHeight : 480,
		// resolution displayed for the source
		displayWidth: parameters.displayWidth !== undefined ? parameters.displayWidth : 640,
		displayHeight: parameters.displayHeight !== undefined ? parameters.displayHeight : 480,
	}

	this.ready = false
        this.domElement = null
}

//////////////////////////////////////////////////////////////////////////////
//		Code Separator
//////////////////////////////////////////////////////////////////////////////
THREEx.ArToolkitSource.prototype.init = function(onReady){
	var _this = this

        if( this.parameters.sourceType === 'image' ){
                var domElement = this._initSourceImage(onSourceReady)
        }else if( this.parameters.sourceType === 'video' ){
                var domElement = this._initSourceVideo(onSourceReady)
        }else if( this.parameters.sourceType === 'webcam' ){
                var domElement = this._initSourceWebcam(onSourceReady)
        }else{
                console.assert(false)
        }

	// attach
        this.domElement = domElement
        this.domElement.style.position = 'absolute'
        this.domElement.style.top = '0px'
        this.domElement.style.left = '0px'
        this.domElement.style.zIndex = '-2'
        this.domElement.style.zIndex = '-2'

	return this
        function onSourceReady(){
		document.body.appendChild(_this.domElement);

		_this.ready = true

		onReady && onReady()
        }
}

////////////////////////////////////////////////////////////////////////////////
//          init image source
////////////////////////////////////////////////////////////////////////////////

THREEx.ArToolkitSource.prototype._initSourceImage = function(onReady) {
	// TODO make it static
        var domElement = document.createElement('img')
	domElement.src = this.parameters.sourceUrl

	domElement.width = this.parameters.sourceWidth
	domElement.height = this.parameters.sourceHeight
	domElement.style.width = this.parameters.displayWidth+'px'
	domElement.style.height = this.parameters.displayHeight+'px'

	// wait until the video stream is ready
	var interval = setInterval(function() {
		if (!domElement.naturalWidth)	return;
		onReady()
		clearInterval(interval)
	}, 1000/50);

	return domElement
}

////////////////////////////////////////////////////////////////////////////////
//          init video source
////////////////////////////////////////////////////////////////////////////////

THREEx.ArToolkitSource.prototype._initSourceVideo = function(onReady) {
	// TODO make it static
	var domElement = document.createElement('video');
	domElement.src = this.parameters.sourceUrl

	domElement.style.objectFit = 'initial'

	domElement.autoplay = true;
	domElement.webkitPlaysinline = true;
	domElement.controls = false;
	domElement.loop = true;
	domElement.muted = true

	// trick to trigger the video on android
	document.body.addEventListener('click', function onClick(){
		document.body.removeEventListener('click', onClick);
		domElement.play()
	})

	domElement.width = this.parameters.sourceWidth
	domElement.height = this.parameters.sourceHeight
	domElement.style.width = this.parameters.displayWidth+'px'
	domElement.style.height = this.parameters.displayHeight+'px'

	// wait until the video stream is ready
	var interval = setInterval(function() {
		if (!domElement.videoWidth)	return;
		onReady()
		clearInterval(interval)
	}, 1000/50);
	return domElement
}

////////////////////////////////////////////////////////////////////////////////
//          handle webcam source
////////////////////////////////////////////////////////////////////////////////

THREEx.ArToolkitSource.prototype._initSourceWebcam = function(onReady) {
	var _this = this
	// TODO make it static
	navigator.getUserMedia  = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia;

	var domElement = document.createElement('video');
	domElement.style.width = this.parameters.displayWidth+'px'
	domElement.style.height = this.parameters.displayHeight+'px'

	if (navigator.getUserMedia === undefined ){
		alert("WebRTC issue! navigator.getUserMedia not present in your browser");
	}
	if (navigator.mediaDevices === undefined || navigator.mediaDevices.enumerateDevices === undefined ){
		alert("WebRTC issue! navigator.mediaDevices.enumerateDevices not present in your browser");
	}

	navigator.mediaDevices.enumerateDevices().then(function(devices) {
                // define getUserMedia() constraints
                var constraints = {
			audio: false,
			video: {
				mandatory: {
					maxWidth: _this.parameters.sourceWidth,
					maxHeight: _this.parameters.sourceHeight
		    		}
		  	}
                }

		// TODO super unclear how to get the backward facing camera...
		// use heuristic - on chrome android current algo is working
		//
		// on macosx it isnt. figure out the algo, and do if(macosx)
		// - with one or two camera
		//
		// some issue on window

		/**
		 * how to test
		 * - one or two camera on macbook
		 * - my phone
		 */
		var runOnMobile = 'ontouchstart' in window ? true : false
		if( runOnMobile === true ){
			pickDeviceAndroid()
		}else{
			pickDeviceMacosx()
		}

		function pickDeviceAndroid(){
			devices.forEach(function(device) {
				if( device.kind !== 'videoinput' )	return
				constraints.video.optional = [{sourceId: device.deviceId}]
			});
		}
		function pickDeviceMacosx(){
			devices.forEach(function(device) {
				if( device.kind !== 'videoinput' )	return

				if( constraints.video.optional !== undefined )	return
				constraints.video.optional = [{sourceId: device.deviceId}]
			});
		}

		// OLD API
                // it it finds the videoSource 'environment', modify constraints.video
                // for (var i = 0; i != sourceInfos.length; ++i) {
                //         var sourceInfo = sourceInfos[i];
                //         if(sourceInfo.kind == "video" && sourceInfo.facing == "environment") {
                //                 constraints.video.optional = [{sourceId: sourceInfo.id}]
                //         }
                // }

		navigator.getUserMedia(constraints, function success(stream) {
			// console.log('success', stream);
			domElement.src = window.URL.createObjectURL(stream);
			// to start the video, when it is possible to start it only on userevent. like in android
			document.body.addEventListener('click', function(){
				domElement.play();
			})
			// domElement.play();

			// wait until the video stream is ready
			var interval = setInterval(function() {
				if (!domElement.videoWidth)	return;
				onReady()
				clearInterval(interval)
			}, 1000/50);
		}, function(error) {
			console.log("Can't access user media", error);
			alert("Can't access user media :()");
		});
	}).catch(function(err) {
		console.log(err.name + ": " + err.message);
	});

	return domElement
}

////////////////////////////////////////////////////////////////////////////////
//          handle resize
////////////////////////////////////////////////////////////////////////////////

THREEx.ArToolkitSource.prototype.onResize = function(mirrorDomElements){
	var _this = this
	var screenWidth = window.innerWidth
	var screenHeight = window.innerHeight

	// compute sourceWidth, sourceHeight
	if( this.domElement.nodeName === "IMG" ){
		var sourceWidth = this.domElement.naturalWidth
		var sourceHeight = this.domElement.naturalHeight
	}else if( this.domElement.nodeName === "VIDEO" ){
		var sourceWidth = this.domElement.videoWidth
		var sourceHeight = this.domElement.videoHeight
	}else{
		console.assert(false)
	}

	// compute sourceAspect
	var sourceAspect = sourceWidth / sourceHeight
	// compute screenAspect
	var screenAspect = screenWidth / screenHeight

	// if screenAspect < sourceAspect, then change the width, else change the height
	if( screenAspect < sourceAspect ){
		// compute newWidth and set .width/.marginLeft
		var newWidth = sourceAspect * screenHeight
		this.domElement.style.width = newWidth+'px'
		this.domElement.style.marginLeft = -(newWidth-screenWidth)/2+'px'

		// init style.height/.marginTop to normal value
		this.domElement.style.height = screenHeight+'px'
		this.domElement.style.marginTop = '0px'
	}else{
		// compute newHeight and set .height/.marginTop
		var newHeight = 1 / (sourceAspect / screenWidth)
		this.domElement.style.height = newHeight+'px'
		this.domElement.style.marginTop = -(newHeight-screenHeight)/2+'px'

		// init style.width/.marginLeft to normal value
		this.domElement.style.width = screenWidth+'px'
		this.domElement.style.marginLeft = '0px'
	}

	// honor default parameters
	if( mirrorDomElements === undefined )	mirrorDomElements = []
	if( mirrorDomElements instanceof Array === false )	mirrorDomElements = [mirrorDomElements]

	// Mirror _this.domElement.style to mirrorDomElements
	mirrorDomElements.forEach(function(domElement){
		domElement.style.width = _this.domElement.style.width
		domElement.style.height = _this.domElement.style.height
		domElement.style.marginLeft = _this.domElement.style.marginLeft
		domElement.style.marginTop = _this.domElement.style.marginTop
	})
}
<|endoftext|>


<commit_before>const assert = require('assert')
const validate = require('validate.js')
const occupy = require('../services/occupy-pane')
const chooseLocation = require('../services/choose-location')
const mLoc = require('../services/max-location')

function maxOrSkipLocation (state, d) {
  // TODO: move this logic outside this module, grid should not bemakig this decision
  try {
    const { x, y } = mLoc(state, d)
    const skippedLocation = movedInDirection(d, x, y, state)
      ? false
      : chooseLocation(state.grid, state, d)
    if (skippedLocation) return skippedLocation
    if (d === 'up' || d === 'down') return {y, x: state.x}
    if (d === 'left' || d === 'right') return {x, y: state.y}
  } catch (e) {
    throw new Error('location blocked')
  }
}

function movedInDirection (d, x, y, state) {
  if ((d === 'up' || d === 'down') && (state.y === y)) return false
  if ((d === 'left' || d === 'right') && (state.x === x)) return false
  return true
}

function findAxis (state, x, y) {
  if (state.x === x) return 'vertical'
  if (state.y === y) return 'horizontal'
}

function findDirection (state, x, y) {
  const axis = findAxis(state, x, y)
  if (axis === 'vertical') {
    if (y > state.y) return 'down'
    if (y < state.y) return 'up'
  } else if (axis === 'horizontal') {
    if (x > state.x) return 'right'
    if (x < state.x) return 'left'
  }
}

function findBlockingPanes (state, x, y) {
  try {
    occupy(
      state.grid,
      Object.assign({}, state, {x, y}),
      true // TODO: remove this
    )
  } catch (e) {
    if (e.message === 'space is occupied') {
      return e.coords
    } else if (e.message === 'size exceeds grid') {
      return [state]
    } else {
      return []
    }
  }
  return []
}

function pushOrShrinkPane (state, pane, x, y, origLocations) {
  const direction = findDirection(state, x, y)
  const amount = direction === 'right' ? {x: x - state.x, y: 0}
    : direction === 'left' ? {x: x - state.x, y: 0}
    : direction === 'up' ? {y: y - state.y, x: 0}
    : direction === 'down' ? {y: y - state.y, x: 0}
    : {x: 0, y: 0}
  // RECORD ORIG LOCATION
  if (origLocations.filter(o => o.id === pane.id).length > 0) {
    if (pane.x > state.x + amount.x || pane.y > state.y + amount.y) return true
    const err = new Error('will not move pane twice')
    err.origLocations = origLocations
    throw err
  }
  origLocations.push({id: pane.id, x: pane.x, y: pane.y, width: pane.width, height: pane.height})
  const paneState = state.grid.getPane(pane.id)
  // MAX SELF DIRECTION TO PUSHER
  const oppositeDirection = direction === 'left' ? 'right'
    : direction === 'right' ? 'left'
    : direction === 'up' ? 'down'
    : direction === 'down' ? 'up' : ''
  paneState.maxSize({[oppositeDirection]: true})
  const newLocationForPane = {x: pane.x + amount.x, y: pane.y + amount.y}
  try {
    // ATTEMPT TO PUSH PANE
    state.grid.getPane(pane.id).changeLocation(newLocationForPane.x, newLocationForPane.y)
    return true
  } catch (e) {
    try {
      // MAX LOCATION TO NEXT PANE
      paneState.maxLoc({[direction]: true}, true)
      // ATTEMPT TO MOVE ADJACENT PANES OUT OF THE WAY
      const [ nextX, nextY ] = direction === 'left' ? [ x - pane.width, pane.y ]
        : direction === 'right' ? [ state.width + x, pane.y ]
        : direction === 'up' ? [ pane.x, y - pane.height ]
        : direction === 'down' ? [ pane.x, state.height + y ] : [ x, y ]
      const movedOthers = movePanesOutOfTheWay(paneState, nextX, nextY, origLocations)
      state.grid.getPane(pane.id).changeLocation(newLocationForPane.x, newLocationForPane.y)
      return movedOthers
    } catch (e) {
      try {
        // ATTEMPT TO SHRINK PANE
        state.grid.getPane(pane.id).decreaseSizeDirectional(
          direction,
          direction === 'left' || direction === 'right' ? Math.abs(amount.x) : Math.abs(amount.y)
        )
        return true
      } catch (e) {
        e.origLocations = origLocations
        throw e
      }
    }
  }
}

function movePanesOutOfTheWay (state, x, y, origLocations = []) {
  const adjacentPanes = findBlockingPanes(state, x, y)
  const clearedPanesSuccessfully = adjacentPanes.every(pane => pushOrShrinkPane(state, pane, x, y, origLocations))
  return clearedPanesSuccessfully
}

module.exports = function locationChanger (state, implementation) {
  return ({
    changeLocation: function changeLocation (x, y) {
      assert(validate.isInteger(x), `${x} is not numeric`)
      assert(validate.isInteger(y), `${y} is not numeric`)
      assert(state.grid.width >= state.width + x, 'location is outside of grid')
      assert(state.grid.height >= state.height + y, 'location is outside of grid')
      assert(x >= 0, 'location is outside of grid')
      assert(y >= 0, 'location is outside of grid')
      const blockingPanes = findBlockingPanes(state, x, y)
      if (blockingPanes.length === 0) {
        state.x = x
        state.y = y
        if (implementation && typeof implementation.changeSize === 'function') {
          implementation.changeLocation(state, x, y)
        }
      } else {
        const direction = findDirection(state, x, y)
        const axis = findAxis(state, x, y)
        // TODO: remove maxedLoc and let the API user decide whether this should be done or not
        const maxedLoc = mLoc(state, direction)
        const finalX = axis === 'horizontal' ? maxedLoc.x : state.x
        const finalY = axis === 'vertical' ? maxedLoc.y : state.y
        if (finalX === state.x && finalY === state.y) throw new Error('location blocked by one or more other panes')
        state.x = finalX
        state.y = finalY
        if (implementation && typeof implementation.changeSize === 'function') {
          implementation.changeLocation(state, finalX, finalY)
        }
      }
    },
    squashIntoLocation: function squashIntoLocation (x, y) {
      assert(validate.isInteger(x), `${x} is not numeric`)
      assert(validate.isInteger(y), `${y} is not numeric`)
      assert(state.grid.width >= state.width + x, 'location is outside of grid')
      assert(state.grid.height >= state.height + y, 'location is outside of grid')
      assert(x >= 0, 'location is outside of grid')
      assert(y >= 0, 'location is outside of grid')
      const origLocation = {
        id: state.id,
        x: state.x,
        y: state.y,
        width: state.width,
        height: state.height
      }
      try {
        const direction = findDirection(state, x, y)
        state.maxLoc({[direction]: true}, true)
        if (movePanesOutOfTheWay(state, x, y)) {
          state.changeLocation(x, y)
        }
      } catch (e) {
        console.log('e is:', e)
        const origLocations = e.origLocations ? e.origLocations.concat(origLocation) : []
        origLocations.forEach(o => {
          const pane = state.grid.getPane(o.id)
          pane.changeLocation(o.x, o.y)
          pane.changeSize(o.width, o.height)
        })
        throw new Error('location is blocked by one or more panes')
      }
    },
    maxLoc: function maxLoc (directions, noSkip) {
      // TODO: noSkip should be the only option
      assert(validate.isObject(directions), `${directions} shold be an object`)
      const changed = Object.keys(directions)
        .filter(d => d)
        .filter(d => {
          assert(
            d === 'up' || d === 'down' || d === 'left' || d === 'right',
            `${d} should be one of 'up/down/left/right'`
          )
          if (noSkip) {
            const chosenLocation = mLoc(state, d)
            if (d === 'up' || d === 'down') {
              state.y = chosenLocation.y
            } else {
              state.x = chosenLocation.x
            }
          } else {
            const chosenLocation = maxOrSkipLocation(state, d)
            state.x = chosenLocation.x
            state.y = chosenLocation.y
          }
        })
      if (changed && implementation && typeof implementation.changeBounds === 'function') {
        implementation.changeBounds(state)
      }
    }
  })
}
<commit_msg>refactor: unnecessary use of state<commit_after>const assert = require('assert')
const validate = require('validate.js')
const occupy = require('../services/occupy-pane')
const chooseLocation = require('../services/choose-location')
const mLoc = require('../services/max-location')

function maxOrSkipLocation (state, d) {
  // TODO: move this logic outside this module, grid should not bemakig this decision
  try {
    const { x, y } = mLoc(state, d)
    const skippedLocation = movedInDirection(d, x, y, state)
      ? false
      : chooseLocation(state.grid, state, d)
    if (skippedLocation) return skippedLocation
    if (d === 'up' || d === 'down') return {y, x: state.x}
    if (d === 'left' || d === 'right') return {x, y: state.y}
  } catch (e) {
    throw new Error('location blocked')
  }
}

function movedInDirection (d, x, y, state) {
  if ((d === 'up' || d === 'down') && (state.y === y)) return false
  if ((d === 'left' || d === 'right') && (state.x === x)) return false
  return true
}

function findAxis (state, x, y) {
  if (state.x === x) return 'vertical'
  if (state.y === y) return 'horizontal'
}

function findDirection (state, x, y) {
  const axis = findAxis(state, x, y)
  if (axis === 'vertical') {
    if (y > state.y) return 'down'
    if (y < state.y) return 'up'
  } else if (axis === 'horizontal') {
    if (x > state.x) return 'right'
    if (x < state.x) return 'left'
  }
}

function findBlockingPanes (state, x, y) {
  try {
    occupy(
      state.grid,
      Object.assign({}, state, {x, y}),
      true // TODO: remove this
    )
  } catch (e) {
    if (e.message === 'space is occupied') {
      return e.coords
    } else if (e.message === 'size exceeds grid') {
      return [state]
    } else {
      return []
    }
  }
  return []
}

function pushOrShrinkPane (state, pane, x, y, origLocations) {
  const direction = findDirection(state, x, y)
  const amount = direction === 'right' ? {x: x - state.x, y: 0}
    : direction === 'left' ? {x: x - state.x, y: 0}
    : direction === 'up' ? {y: y - state.y, x: 0}
    : direction === 'down' ? {y: y - state.y, x: 0}
    : {x: 0, y: 0}
  // RECORD ORIG LOCATION
  if (origLocations.filter(o => o.id === pane.id).length > 0) {
    if (pane.x > state.x + amount.x || pane.y > state.y + amount.y) return true
    const err = new Error('will not move pane twice')
    err.origLocations = origLocations
    throw err
  }
  origLocations.push({id: pane.id, x: pane.x, y: pane.y, width: pane.width, height: pane.height})
  const paneState = state.grid.getPane(pane.id)
  // MAX SELF DIRECTION TO PUSHER
  const oppositeDirection = direction === 'left' ? 'right'
    : direction === 'right' ? 'left'
    : direction === 'up' ? 'down'
    : direction === 'down' ? 'up' : ''
  paneState.maxSize({[oppositeDirection]: true})
  const newLocationForPane = {x: pane.x + amount.x, y: pane.y + amount.y}
  try {
    // ATTEMPT TO PUSH PANE
    paneState.changeLocation(newLocationForPane.x, newLocationForPane.y)
    return true
  } catch (e) {
    try {
      // MAX LOCATION TO NEXT PANE
      paneState.maxLoc({[direction]: true}, true)
      // ATTEMPT TO MOVE ADJACENT PANES OUT OF THE WAY
      const [ nextX, nextY ] = direction === 'left' ? [ x - pane.width, pane.y ]
        : direction === 'right' ? [ state.width + x, pane.y ]
        : direction === 'up' ? [ pane.x, y - pane.height ]
        : direction === 'down' ? [ pane.x, state.height + y ] : [ x, y ]
      const movedOthers = movePanesOutOfTheWay(paneState, nextX, nextY, origLocations)
      state.grid.getPane(pane.id).changeLocation(newLocationForPane.x, newLocationForPane.y)
      return movedOthers
    } catch (e) {
      try {
        // ATTEMPT TO SHRINK PANE
        state.grid.getPane(pane.id).decreaseSizeDirectional(
          direction,
          direction === 'left' || direction === 'right' ? Math.abs(amount.x) : Math.abs(amount.y)
        )
        return true
      } catch (e) {
        e.origLocations = origLocations
        throw e
      }
    }
  }
}

function movePanesOutOfTheWay (state, x, y, origLocations = []) {
  const adjacentPanes = findBlockingPanes(state, x, y)
  const clearedPanesSuccessfully = adjacentPanes.every(pane => pushOrShrinkPane(state, pane, x, y, origLocations))
  return clearedPanesSuccessfully
}

module.exports = function locationChanger (state, implementation) {
  return ({
    changeLocation: function changeLocation (x, y) {
      assert(validate.isInteger(x), `${x} is not numeric`)
      assert(validate.isInteger(y), `${y} is not numeric`)
      assert(state.grid.width >= state.width + x, 'location is outside of grid')
      assert(state.grid.height >= state.height + y, 'location is outside of grid')
      assert(x >= 0, 'location is outside of grid')
      assert(y >= 0, 'location is outside of grid')
      const blockingPanes = findBlockingPanes(state, x, y)
      if (blockingPanes.length === 0) {
        state.x = x
        state.y = y
        if (implementation && typeof implementation.changeSize === 'function') {
          implementation.changeLocation(state, x, y)
        }
      } else {
        const direction = findDirection(state, x, y)
        const axis = findAxis(state, x, y)
        // TODO: remove maxedLoc and let the API user decide whether this should be done or not
        const maxedLoc = mLoc(state, direction)
        const finalX = axis === 'horizontal' ? maxedLoc.x : state.x
        const finalY = axis === 'vertical' ? maxedLoc.y : state.y
        if (finalX === state.x && finalY === state.y) throw new Error('location blocked by one or more other panes')
        state.x = finalX
        state.y = finalY
        if (implementation && typeof implementation.changeSize === 'function') {
          implementation.changeLocation(state, finalX, finalY)
        }
      }
    },
    squashIntoLocation: function squashIntoLocation (x, y) {
      assert(validate.isInteger(x), `${x} is not numeric`)
      assert(validate.isInteger(y), `${y} is not numeric`)
      assert(state.grid.width >= state.width + x, 'location is outside of grid')
      assert(state.grid.height >= state.height + y, 'location is outside of grid')
      assert(x >= 0, 'location is outside of grid')
      assert(y >= 0, 'location is outside of grid')
      const origLocation = {
        id: state.id,
        x: state.x,
        y: state.y,
        width: state.width,
        height: state.height
      }
      try {
        const direction = findDirection(state, x, y)
        state.maxLoc({[direction]: true}, true)
        if (movePanesOutOfTheWay(state, x, y)) {
          state.changeLocation(x, y)
        }
      } catch (e) {
        const origLocations = e.origLocations ? e.origLocations.concat(origLocation) : []
        origLocations.forEach(o => {
          const pane = state.grid.getPane(o.id)
          pane.changeLocation(o.x, o.y)
          pane.changeSize(o.width, o.height)
        })
        throw new Error('location is blocked by one or more panes')
      }
    },
    maxLoc: function maxLoc (directions, noSkip) {
      // TODO: noSkip should be the only option
      assert(validate.isObject(directions), `${directions} shold be an object`)
      const changed = Object.keys(directions)
        .filter(d => d)
        .filter(d => {
          assert(
            d === 'up' || d === 'down' || d === 'left' || d === 'right',
            `${d} should be one of 'up/down/left/right'`
          )
          if (noSkip) {
            const chosenLocation = mLoc(state, d)
            if (d === 'up' || d === 'down') {
              state.y = chosenLocation.y
            } else {
              state.x = chosenLocation.x
            }
          } else {
            const chosenLocation = maxOrSkipLocation(state, d)
            state.x = chosenLocation.x
            state.y = chosenLocation.y
          }
        })
      if (changed && implementation && typeof implementation.changeBounds === 'function') {
        implementation.changeBounds(state)
      }
    }
  })
}
<|endoftext|>


<commit_before>/* unmaze canvas user interface
   Copyright 2017 Mariusz Skoneczko
   Licensed under the MIT license
*/

const TILE_SIZE = 20;

const UI_MODE = {
    WATCHING: 0,
    EDITING: 1
};

const TYPE2COLOR = {};
TYPE2COLOR[MAZE.FREE] = "white";
TYPE2COLOR[MAZE.WALL] = "black";
TYPE2COLOR[MAZE.START] = "yellow";
TYPE2COLOR[MAZE.END] = "green";
TYPE2COLOR[MAZE.TRAIL] = "orange";

const CURSOR_COLOR = "gray";

const ROBOT_COLOR = "red";

let canvas, ctx, ui_maze, ui_mode;

let cursor_pos = {x: null, y: null};

canvas = document.getElementById("unmaze-canvas");

ctx = canvas.getContext("2d");

const MAZE_WIDTH = canvas.width / TILE_SIZE;

const MAZE_HEIGHT = canvas.height / TILE_SIZE;

function maze_setup() {
    ui_maze = new Maze(MAZE_WIDTH, MAZE_HEIGHT);

    ui_maze.setStart(0, 0);
    ui_maze.setEnd(MAZE_WIDTH - 1, MAZE_HEIGHT - 1);
    ui_maze.robotToStart();
}

function render() {
    ctx.clearRect(0, 0, canvas.width, canvas.height);
    for (let i = 0; i < MAZE_WIDTH; i++) {
        for (let j = 0; j < MAZE_HEIGHT; j++) {
            ctx.fillStyle = TYPE2COLOR[ui_maze.maze[i][j].type];
            ctx.fillRect(i * TILE_SIZE, j * TILE_SIZE, TILE_SIZE, TILE_SIZE);
        }
    }

    ctx.fillStyle = ROBOT_COLOR;
    ctx.fillRect(ui_maze.robot.x * TILE_SIZE, ui_maze.robot.y * TILE_SIZE, TILE_SIZE, TILE_SIZE);

    if (cursor_pos.x !== null && cursor_pos.y !== null) {
        ctx.strokeStyle = CURSOR_COLOR;
        ctx.strokeRect(cursor_pos.x * TILE_SIZE, cursor_pos.y * TILE_SIZE, TILE_SIZE, TILE_SIZE);
    }
}

function move_mouse(e) {
    let rect = canvas.getBoundingClientRect();
    cursor_pos.x = Math.floor((e.clientX - rect.left) / TILE_SIZE);
    cursor_pos.y = Math.floor((e.clientY - rect.top) / TILE_SIZE);
    render();
}

function toggle_tile() {
    if (ui_maze.maze[cursor_pos.x][cursor_pos.y].type === MAZE.FREE) {
        ui_maze.maze[cursor_pos.x][cursor_pos.y].type = MAZE.WALL;
    } else if (ui_maze.maze[cursor_pos.x][cursor_pos.y].type === MAZE.WALL) {
        ui_maze.maze[cursor_pos.x][cursor_pos.y].type = MAZE.FREE;
    }
    render();
}

function no_cursor() {
    cursor_pos = {x: null, y: null};
    render();
}

function editing_mode() {
    ui_mode = UI_MODE.EDITING;
    canvas.addEventListener("mousemove", move_mouse);
    canvas.addEventListener("click", toggle_tile);
    canvas.addEventListener("mouseout", no_cursor);
}

function watching_mode() {
    ui_mode = UI_MODE.WATCHING;
    canvas.removeEventListener("mousemove", move_mouse);
    canvas.removeEventListener("click", toggle_tile);
    canvas.removeEventListener("mouseout", no_cursor);
}

function full_reset() {
    maze_setup();
    render();
}

document.getElementById("reset-button").addEventListener("click", full_reset);

maze_setup();
editing_mode();
render();

<commit_msg>Set virtual cursor position on click as well as move<commit_after>/* unmaze canvas user interface
   Copyright 2017 Mariusz Skoneczko
   Licensed under the MIT license
*/

const TILE_SIZE = 20;

const UI_MODE = {
    WATCHING: 0,
    EDITING: 1
};

const TYPE2COLOR = {};
TYPE2COLOR[MAZE.FREE] = "white";
TYPE2COLOR[MAZE.WALL] = "black";
TYPE2COLOR[MAZE.START] = "yellow";
TYPE2COLOR[MAZE.END] = "green";
TYPE2COLOR[MAZE.TRAIL] = "orange";

const CURSOR_COLOR = "gray";

const ROBOT_COLOR = "red";

let canvas, ctx, ui_maze, ui_mode;

let cursor_pos = {x: null, y: null};

canvas = document.getElementById("unmaze-canvas");

ctx = canvas.getContext("2d");

const MAZE_WIDTH = canvas.width / TILE_SIZE;

const MAZE_HEIGHT = canvas.height / TILE_SIZE;

function maze_setup() {
    ui_maze = new Maze(MAZE_WIDTH, MAZE_HEIGHT);

    ui_maze.setStart(0, 0);
    ui_maze.setEnd(MAZE_WIDTH - 1, MAZE_HEIGHT - 1);
    ui_maze.robotToStart();
}

function render() {
    ctx.clearRect(0, 0, canvas.width, canvas.height);
    for (let i = 0; i < MAZE_WIDTH; i++) {
        for (let j = 0; j < MAZE_HEIGHT; j++) {
            ctx.fillStyle = TYPE2COLOR[ui_maze.maze[i][j].type];
            ctx.fillRect(i * TILE_SIZE, j * TILE_SIZE, TILE_SIZE, TILE_SIZE);
        }
    }

    ctx.fillStyle = ROBOT_COLOR;
    ctx.fillRect(ui_maze.robot.x * TILE_SIZE, ui_maze.robot.y * TILE_SIZE, TILE_SIZE, TILE_SIZE);

    if (cursor_pos.x !== null && cursor_pos.y !== null) {
        ctx.strokeStyle = CURSOR_COLOR;
        ctx.strokeRect(cursor_pos.x * TILE_SIZE, cursor_pos.y * TILE_SIZE, TILE_SIZE, TILE_SIZE);
    }
}

function set_cursor_pos(e) {
    let rect = canvas.getBoundingClientRect();
    cursor_pos.x = Math.floor((e.clientX - rect.left) / TILE_SIZE);
    cursor_pos.y = Math.floor((e.clientY - rect.top) / TILE_SIZE);
}

function move_mouse(e) {
    set_cursor_pos(e);
    render();
}

function toggle_tile(e) {
    set_cursor_pos(e);
    if (ui_maze.maze[cursor_pos.x][cursor_pos.y].type === MAZE.FREE) {
        ui_maze.maze[cursor_pos.x][cursor_pos.y].type = MAZE.WALL;
    } else if (ui_maze.maze[cursor_pos.x][cursor_pos.y].type === MAZE.WALL) {
        ui_maze.maze[cursor_pos.x][cursor_pos.y].type = MAZE.FREE;
    }
    render();
}

function no_cursor() {
    cursor_pos = {x: null, y: null};
    render();
}

function editing_mode() {
    ui_mode = UI_MODE.EDITING;
    canvas.addEventListener("mousemove", move_mouse);
    canvas.addEventListener("click", toggle_tile);
    canvas.addEventListener("mouseout", no_cursor);
}

function watching_mode() {
    ui_mode = UI_MODE.WATCHING;
    canvas.removeEventListener("mousemove", move_mouse);
    canvas.removeEventListener("click", toggle_tile);
    canvas.removeEventListener("mouseout", no_cursor);
}

function full_reset() {
    maze_setup();
    render();
}

document.getElementById("reset-button").addEventListener("click", full_reset);

maze_setup();
editing_mode();
render();

<|endoftext|>