<commit_before>// Holds the current image field name
var image_field_name = '';

function is_validURL(image_url)  {
    // taken from: jquery.validate.js
    return /^(https?|ftp):\/\/(((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:)*@)?(((\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?)(:\d*)?)(\/((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)+(\/(([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)*)*)?)?(\?((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|[\uE000-\uF8FF]|\/|\?)*)?(\#((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|\/|\?)*)?$/i.test(image_url);
}

function id_to_windowname(text) {
    text = text.replace(/\./g, '__dot__');
    text = text.replace(/\-/g, '__dash__');
    return text;
}

function windowname_to_id(text) {
    text = text.replace(/__dot__/g, '.');
    text = text.replace(/__dash__/g, '-');
    return text;
}

function showRelatedObjectLookupPopup(triggeringLink, field_name) {
    image_field_name = field_name;
    var name = triggeringLink.id.replace(/^lookup_/, '');
    name = id_to_windowname(name);
    var href;
    if (triggeringLink.href.search(/\?/) >= 0) {
    	href = triggeringLink.href + '&pop=1';
    } else {
	    href = triggeringLink.href + '?pop=1';
    }
    var win = window.open(href, name, 'height=500,width=800,resizable=yes,scrollbars=yes');
    win.focus();
    return false;
}

dismissRelatedImageLookupPopup = function(win, chosenId, chosenThumbnailUrl, chosenDescriptionTxt) {
    win.close();
    var jxhr = jQuery.ajax({
                url: filer_image_url,
                data: {'id': chosenId},
                success: function(data){
                    if (data.url){
			jQuery("td.invalid_image").html('');
                        jQuery('#var_'+image_field_name).val(data.url);
                    }
                    else{
                        jQuery("td.error_"+image_field_name).html('Please select a valid image type.');
                    }
                },
                error: function(data){
                    alert('Error retrieving file information.');
                }
            });
        return jxhr;
};

jQuery(document).ready(function(){
   jQuery("form#smartsnippetpointer_form").submit(function(){
       var is_valid = true;
       jQuery("td.invalid_image").html('');
       jQuery("input.filer_image_url").each(function(val) {
            if (jQuery(this).val()=='' || !is_validURL(jQuery(this).val())){
                is_valid = false;
	            image_field_name = jQuery(this).attr('id').replace('var_', '');
                jQuery('td.error_'+image_field_name).html('Please select a valid image type.');
            }
       });
       return is_valid;
   }) ;
});<commit_msg>UNICORN-4296. Validate text input - using the validation regex provided in jquery.validate.url - added the link to the jQuery documentaion about validate. We needed to use it like this because the validation plugin needs a form.<commit_after>// Holds the current image field name
var image_field_name = '';

function is_validURL(image_url)  {
    // taken from: jquery.validate.js
    // from: http://docs.jquery.com/Plugins/Validation/validate
    return /^(https?|ftp):\/\/(((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:)*@)?(((\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?)(:\d*)?)(\/((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)+(\/(([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)*)*)?)?(\?((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|[\uE000-\uF8FF]|\/|\?)*)?(\#((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|\/|\?)*)?$/i.test(image_url);
}

function id_to_windowname(text) {
    text = text.replace(/\./g, '__dot__');
    text = text.replace(/\-/g, '__dash__');
    return text;
}

function windowname_to_id(text) {
    text = text.replace(/__dot__/g, '.');
    text = text.replace(/__dash__/g, '-');
    return text;
}

function showRelatedObjectLookupPopup(triggeringLink, field_name) {
    image_field_name = field_name;
    var name = triggeringLink.id.replace(/^lookup_/, '');
    name = id_to_windowname(name);
    var href;
    if (triggeringLink.href.search(/\?/) >= 0) {
    	href = triggeringLink.href + '&pop=1';
    } else {
	    href = triggeringLink.href + '?pop=1';
    }
    var win = window.open(href, name, 'height=500,width=800,resizable=yes,scrollbars=yes');
    win.focus();
    return false;
}

dismissRelatedImageLookupPopup = function(win, chosenId, chosenThumbnailUrl, chosenDescriptionTxt) {
    win.close();
    var jxhr = jQuery.ajax({
                url: filer_image_url,
                data: {'id': chosenId},
                success: function(data){
                    if (data.url){
			jQuery("td.invalid_image").html('');
                        jQuery('#var_'+image_field_name).val(data.url);
                    }
                    else{
                        jQuery("td.error_"+image_field_name).html('Please select a valid image type.');
                    }
                },
                error: function(data){
                    alert('Error retrieving file information.');
                }
            });
        return jxhr;
};

jQuery(document).ready(function(){
   jQuery("form#smartsnippetpointer_form").submit(function(){
       var is_valid = true;
       jQuery("td.invalid_image").html('');
       jQuery("input.filer_image_url").each(function(val) {
            if (jQuery(this).val()=='' || !is_validURL(jQuery(this).val())){
                is_valid = false;
	            image_field_name = jQuery(this).attr('id').replace('var_', '');
                jQuery('td.error_'+image_field_name).html('Please select a valid image type.');
            }
       });
       return is_valid;
   }) ;
});<|endoftext|>


<commit_before>/*
 * https://github.com/5orenso
 *
 * Copyright (c) 2014 Øistein Sørensen
 * Licensed under the MIT license.
 */
'use strict';
var express       = require('express'),
    when          = require('when'),
    _             = require('underscore'),
    swig          = require('swig'),
    fs            = require('fs'),
    path          = require('path'),
    app_path      = __dirname + '/../../',
    template_path = path.normalize(app_path + 'template/current/'),
    content_path  = path.normalize(app_path + 'content/articles/'),
    logger        = require(app_path + 'lib/logger')();

var config;
var web_router = express.Router();
web_router.set_config = function (conf, opt) {
    web_router.config = conf;
    web_router.opt = opt;
    if (opt) {
        if (opt.hasOwnProperty('workerId')) {
            logger.set('workerId', opt.workerId);
        }
        if (opt.hasOwnProperty('content_path')) {
            content_path = path.normalize(opt.content_path);
        }
    }
};

function redirectSlash (req, res, next) {
    if (req.url.match(/^\/$/)) {
        res.redirect(301, '/index.html');
    } else {
        next();
    }
}

web_router.use(express.query()); // Parse query_string.
//app.use(Express.cookieParser(opt.cookie.secret)); // Parse cookies.

web_router.use(function(req, res, next) {
    logger.log('info',
        req.method,
        req.url,
        req.get('Content-type'),
        req.get('User-agent')
    );
    next(); // make sure we go to the next routes and don't stop here
});

web_router.use(redirectSlash);
web_router.use('/index.html', express.static(app_path + 'template/current/index.html'));
web_router.use('/js/', express.static(app_path + 'template/current/js/'));
web_router.use('/img/', express.static(app_path + 'template/current/img/'));
web_router.use('/css/', express.static(app_path + 'template/current/css/'));
web_router.use('/fonts/', express.static(app_path + 'template/current/fonts/'));
web_router.use('/favicon.ico', express.static(app_path + 'template/current/favicon.ico'));
web_router.use('/robots.txt', express.static(app_path + 'template/robots.txt'));
web_router.use('/sitemap.xml', express.static(app_path + 'template/sitemap.xml'));

// Main route for blog articles.
web_router.get('/*', function(req, res) {
    res.status(404).send('Page not found');
});
module.exports = web_router;
<commit_msg>Converted to Swig template engine.<commit_after>/*
 * https://github.com/5orenso
 *
 * Copyright (c) 2014 Øistein Sørensen
 * Licensed under the MIT license.
 */
'use strict';
var express       = require('express'),
    when          = require('when'),
    _             = require('underscore'),
    swig          = require('swig'),
    fs            = require('fs'),
    path          = require('path'),
    app_path      = __dirname + '/../../',
    template_path = path.normalize(app_path + 'template/current'),
    logger        = require(app_path + 'lib/logger')();

var config;
var web_router = express.Router();
web_router.set_config = function (conf, opt) {
    web_router.config = conf;
    web_router.opt = opt;
    if (opt) {
        if (opt.hasOwnProperty('workerId')) {
            logger.set('workerId', opt.workerId);
        }
    }
};

function redirectSlash (req, res, next) {
    if (req.url.match(/^\/$/)) {
        res.redirect(301, '/index.html');
    } else {
        next();
    }
}

web_router.use(express.query()); // Parse query_string.
//app.use(Express.cookieParser(opt.cookie.secret)); // Parse cookies.

web_router.use(function(req, res, next) {
    logger.log('info',
        req.method,
        req.url,
        req.get('Content-type'),
        req.get('User-agent')
    );
    next(); // make sure we go to the next routes and don't stop here
});

web_router.use(redirectSlash);
web_router.use('/js/', express.static(app_path + 'template/current/js/'));
web_router.use('/img/', express.static(app_path + 'template/current/img/'));
web_router.use('/css/', express.static(app_path + 'template/current/css/'));
web_router.use('/fonts/', express.static(app_path + 'template/current/fonts/'));
web_router.use('/favicon.ico', express.static(app_path + 'template/current/favicon.ico'));
web_router.use('/robots.txt', express.static(app_path + 'template/robots.txt'));
web_router.use('/sitemap.xml', express.static(app_path + 'template/sitemap.xml'));

// Main route for html files.
web_router.get('/*', function(req, res) {
    var request_pathname = req._parsedUrl.pathname;
    try {
        var tpl = swig.compileFile(template_path + request_pathname);
        res.send(tpl({
            title: 'Hello world',
            query_string: req.query
        }));

    } catch (err) {
        res.status(404).send('Page not found: ' + err);

    }
});
module.exports = web_router;
<|endoftext|>


<commit_before>"use strict";

var debug = require('debug')('waigo-acl');

var waigo = require('../../'),
  _ = waigo._,
  errors = waigo.load('support/errors');

/** ACL error. */
var AclError = exports.AclError = errors.define('AclError');

/**
 * @constructor
 */
var ACL = exports.ACL = function(app) {
  this.app = app;
  this.logger = app.logger.create('ACL');
};

/**
 * Initialise the ACL.
 */
ACL.prototype.init = function*() {
  this.logger.info('Initialising');

  yield this.reload();

  // access to admin content must be protected
  if (!this.res.admin) {
    this.logger.info('Admin resources rules not found, so creating them now');

    this.res.admin = {
      read: {
        role: {
          admin: true
        }
      },
      write: {
        role: {
          admin: true
        }
      }
    }

    this.roles.admin = this.roles.admin || {};
    this.roles.admin.admin = {
      read: true,
      write: true
    }

    // save to db
    this.logger.info('Saving Admin resource rules to db');

    yield this.app.models.Acl.insert({
      resource: 'admin',
      access: 'read',
      entityType: 'role',
      entity: 'admin'
    });
    yield this.app.models.Acl.insert({
      resource: 'admin',
      access: 'write',
      entityType: 'role',
      entity: 'admin'
    });
  }
};

/**
 * Reload the ACL from the database.
 */
ACL.prototype.reload = function*() {
  this.logger.info('Reloading rules from db');

  var data = yield this.app.models.Acl.find({}, {
    raw: true
  });

  var res = this.res = {},
    users = this.users = {},
    roles = this.roles = {};

  data.forEach(function(doc){

    console.log(_.keys(doc[0]));

    // resource perspective
    res[doc.resource] =
      res[doc.resource] || {};

    res[doc.resource][doc.access] =
      res[doc.resource][doc.access] || {};

    res[doc.resource][doc.access][doc.entityType] =
      res[doc.resource][doc.access][doc.entityType] || {};

    res[doc.resource][doc.access][doc.entityType][doc.entity] = true;

    // entity perspsective
    var entity = ('user' === doc.entityType ? users : roles);
    entity[doc.entity] = entity[doc.entity] || {};
    entity[doc.entity][doc.resource] =
      entity[doc.entity][doc.resource] || {};
    entity[doc.entity][doc.resource][doc.access] = true;
  });
};

/**
 * Get whether given user can read given resource.
 * @param  {String} resource Resource name.
 * @param  {Object} user     User object.
 * @param  {String} access     Access type, `read` or `write`.
 * @return {Boolean} true if allowed; false otherwise.
 */
ACL.prototype.can = function(resource, user, access) {
  this.logger.debug('can', resource, user._id, access);

  // if no entry for resource then all ok
  if (!_.get(this.res, resource)) {
    return true;
  }

  // if user has access then it's ok
  if (_.get(this.users, user._id)) {
    let res = _.get(this.users, user._id + '.' + resource);

    if (res && res[access]) {
      return true;
    }
  }

  // if one of user's roles has access it's ok
  let roles = user.roles || [];

  for (let i in roles) {
    let role = roles[i];

    let res = _.get(this.roles, roles[i] + '.' + resource);

    if (res && res[access]) {
      return true;
    }
  }

  return false;
};

/**
 * Get whether given user can read given resource.
 * @param  {String} resource Resource name.
 * @param  {Object} user     User object.
 * @param  {String} access     Access type, `read` or `write`.
 * @throws Error if not allowed.
 */
ACL.prototype.assert = function(resource, user, access) {
  this.logger.debug('assert', resource, user._id, access);

  if (!this.can(resource, user, access)) {
    throw new AclError('User ' + user._id + ' does not have permission to access ' + resource, 403);
  }
};

/**
 * Initialise ACL
 * @param {App} app           The app.
 */
exports.init = function*(app) {
  var a = new ACL(app);

  yield a.init();

  return a;
};

<commit_msg>remove redundant console.log<commit_after>"use strict";

var debug = require('debug')('waigo-acl');

var waigo = require('../../'),
  _ = waigo._,
  errors = waigo.load('support/errors');

/** ACL error. */
var AclError = exports.AclError = errors.define('AclError');

/**
 * @constructor
 */
var ACL = exports.ACL = function(app) {
  this.app = app;
  this.logger = app.logger.create('ACL');
};

/**
 * Initialise the ACL.
 */
ACL.prototype.init = function*() {
  this.logger.info('Initialising');

  yield this.reload();

  // access to admin content must be protected
  if (!this.res.admin) {
    this.logger.info('Admin resources rules not found, so creating them now');

    this.res.admin = {
      read: {
        role: {
          admin: true
        }
      },
      write: {
        role: {
          admin: true
        }
      }
    }

    this.roles.admin = this.roles.admin || {};
    this.roles.admin.admin = {
      read: true,
      write: true
    }

    // save to db
    this.logger.info('Saving Admin resource rules to db');

    yield this.app.models.Acl.insert({
      resource: 'admin',
      access: 'read',
      entityType: 'role',
      entity: 'admin'
    });
    yield this.app.models.Acl.insert({
      resource: 'admin',
      access: 'write',
      entityType: 'role',
      entity: 'admin'
    });
  }
};

/**
 * Reload the ACL from the database.
 */
ACL.prototype.reload = function*() {
  this.logger.info('Reloading rules from db');

  var data = yield this.app.models.Acl.find({}, {
    raw: true
  });

  var res = this.res = {},
    users = this.users = {},
    roles = this.roles = {};

  data.forEach(function(doc){
    // resource perspective
    res[doc.resource] =
      res[doc.resource] || {};

    res[doc.resource][doc.access] =
      res[doc.resource][doc.access] || {};

    res[doc.resource][doc.access][doc.entityType] =
      res[doc.resource][doc.access][doc.entityType] || {};

    res[doc.resource][doc.access][doc.entityType][doc.entity] = true;

    // entity perspsective
    var entity = ('user' === doc.entityType ? users : roles);
    entity[doc.entity] = entity[doc.entity] || {};
    entity[doc.entity][doc.resource] =
      entity[doc.entity][doc.resource] || {};
    entity[doc.entity][doc.resource][doc.access] = true;
  });
};

/**
 * Get whether given user can read given resource.
 * @param  {String} resource Resource name.
 * @param  {Object} user     User object.
 * @param  {String} access     Access type, `read` or `write`.
 * @return {Boolean} true if allowed; false otherwise.
 */
ACL.prototype.can = function(resource, user, access) {
  this.logger.debug('can', resource, user._id, access);

  // if no entry for resource then all ok
  if (!_.get(this.res, resource)) {
    return true;
  }

  // if user has access then it's ok
  if (_.get(this.users, user._id)) {
    let res = _.get(this.users, user._id + '.' + resource);

    if (res && res[access]) {
      return true;
    }
  }

  // if one of user's roles has access it's ok
  let roles = user.roles || [];

  for (let i in roles) {
    let role = roles[i];

    let res = _.get(this.roles, roles[i] + '.' + resource);

    if (res && res[access]) {
      return true;
    }
  }

  return false;
};

/**
 * Get whether given user can read given resource.
 * @param  {String} resource Resource name.
 * @param  {Object} user     User object.
 * @param  {String} access     Access type, `read` or `write`.
 * @throws Error if not allowed.
 */
ACL.prototype.assert = function(resource, user, access) {
  this.logger.debug('assert', resource, user._id, access);

  if (!this.can(resource, user, access)) {
    throw new AclError('User ' + user._id + ' does not have permission to access ' + resource, 403);
  }
};

/**
 * Initialise ACL
 * @param {App} app           The app.
 */
exports.init = function*(app) {
  var a = new ACL(app);

  yield a.init();

  return a;
};

<|endoftext|>


<commit_before>import * as _ from "../deps/underscore-1.10.2-esm.js"
import { nil } from "../header.js"
import { to_write, inspect } from "./_writer.js"
import Class from "./class.js"
import BiwaSet from "./set.js"
import { isPair } from "./_types.js"

//
// Pair
// cons cell
//

const Pair = Class.create({
  initialize: function(car, cdr){
    this.car = car;
    this.cdr = cdr;
  },

  caar: function(){ return this.car.car; },
  cadr: function(){ return this.cdr.car; },
  cdar: function(){ return this.cdr.car; },
  cddr: function(){ return this.cdr.cdr; },

  first:  function(){ return this.car; },
  second: function(){ return this.cdr.car; },
  third:  function(){ return this.cdr.cdr.car; },
  fourth: function(){ return this.cdr.cdr.cdr.car; },
  fifth:  function(){ return this.cdr.cdr.cdr.cdr.car; },

  // returns array containing all the car's of list
  // '(1 2 3) => [1,2,3]
  // '(1 2 . 3) => [1,2]
  to_array: function(){
    var ary = [];
    for(var o = this; o instanceof Pair; o=o.cdr){
      ary.push(o.car);
    }
    return ary;
  },

  to_set: function(){
    var set = new BiwaSet();
    for(var o = this; o instanceof Pair; o=o.cdr){
      set.add(o.car);
    }
    return set;
  },

  length: function(){
    var n = 0;
    for(var o = this; o instanceof Pair; o=o.cdr){
      n++;
    }
    return n;
  },

  // Return the last cdr
  last_cdr: function(){
    var o;
    for(o = this; o instanceof Pair; o = o.cdr)
      ;
    return o;
  },

  // calls the given func passing each car of list
  // returns cdr of last Pair
  foreach: function(func){
    for(var o = this; o instanceof Pair; o=o.cdr){
      func(o.car);
    }
    return o;
  },

  // Returns an array which contains the resuls of calling func
  // with the car's as an argument.
  // If the receiver is not a proper list, the last cdr is ignored.
  // The receiver must not be a cyclic list.
  map: function(func){
    var ary = [];
    for(var o = this; isPair(o); o = o.cdr){
      ary.push(func(o.car));
    }
    return ary;
  },

  // Returns a new list made by applying `func` to each element
  mapList: function(func) {
    return array_to_list(this.map(func));
  },

  // Destructively concat the given list to the receiver.
  // The receiver must be a proper list.
  // Returns the receiver.
  concat: function(list){
    var o = this;
    while(o instanceof Pair && o.cdr != nil){
      o = o.cdr;
    }
    o.cdr = list;
    return this;
  },

  // returns human-redable string of pair
  inspect: function(conv){
    conv || (conv = inspect);
    var a = [];
    var last = this.foreach(function(o){
      a.push(conv(o));
    });
    if(last != nil){
      a.push(".");
      a.push(conv(last));
    }
    return "(" + a.join(" ") + ")";
  },
  toString : function(){
    return this.inspect();
  },

  to_write: function(){
    return this.inspect(to_write);
  }
});

// Creates a list out of the arguments, optionally converting any nested arrays into nested lists if the deep argument is true.
// Example:
//   BiwaScheme.List(1, 2, [3, 4]) ;=> (list 1 2 (vector 3 4))
//   BiwaScheme.deep_array_to_list(1, 2, [3, 4]) ;=> (list 1 2 (list 3 4))
const array_to_list_ = function(ary, deep) {
  var list = nil;
  for(var i=ary.length-1; i>=0; i--){
    var obj = ary[i];
    if(deep && _.isArray(obj) && !obj.is_vector){
      obj = array_to_list_(obj, deep);
    }
    list = new Pair(obj, list);
  }
  return list;
}

// Shallow: List(1, 2, [3]) == (list 1 2 (vector 3 4))
const List = function() {
  var ary = _.toArray(arguments);
  return array_to_list_(ary, false);
};

// Shallow: array_to_list(1, 2, [3]) == (list 1 2 (vector 3 4))
const array_to_list = function(ary) {
  return array_to_list_(ary, false);
};

// Deep: deep_array_to_list(1, 2, [3, 4]) == (list 1 2 (list 3 4))
// deep_array_to_list([1, 2, 3]) - deep
const deep_array_to_list = function(ary) {
  return array_to_list_(ary, true);
};

const Cons = function(car, cdr) {
  return new Pair(car, cdr);
};

const js_obj_to_alist = function(obj) {
  if (obj === undefined) {
    return nil;
  }
  var arr = [];
  _.each(obj, function(val, key) {
    arr.push(new Pair(key, val));
  });
  var alist = array_to_list(arr);
  return alist;
};

const alist_to_js_obj = function(alist) {
  if (alist === nil) {
    return {} ;
  }
  var obj = {};
  alist.foreach(function(item){
    obj[item.car] = item.cdr;
  });
  return obj;
};

export { Pair, List, array_to_list, deep_array_to_list, Cons,
         js_obj_to_alist, alist_to_js_obj };
<commit_msg>feat: Pair.prototype.forEach<commit_after>import * as _ from "../deps/underscore-1.10.2-esm.js"
import { nil } from "../header.js"
import { to_write, inspect } from "./_writer.js"
import Class from "./class.js"
import BiwaSet from "./set.js"
import { isPair } from "./_types.js"

//
// Pair
// cons cell
//

const Pair = Class.create({
  initialize: function(car, cdr){
    this.car = car;
    this.cdr = cdr;
  },

  caar: function(){ return this.car.car; },
  cadr: function(){ return this.cdr.car; },
  cdar: function(){ return this.cdr.car; },
  cddr: function(){ return this.cdr.cdr; },

  first:  function(){ return this.car; },
  second: function(){ return this.cdr.car; },
  third:  function(){ return this.cdr.cdr.car; },
  fourth: function(){ return this.cdr.cdr.cdr.car; },
  fifth:  function(){ return this.cdr.cdr.cdr.cdr.car; },

  // returns array containing all the car's of list
  // '(1 2 3) => [1,2,3]
  // '(1 2 . 3) => [1,2]
  to_array: function(){
    var ary = [];
    for(var o = this; o instanceof Pair; o=o.cdr){
      ary.push(o.car);
    }
    return ary;
  },

  to_set: function(){
    var set = new BiwaSet();
    for(var o = this; o instanceof Pair; o=o.cdr){
      set.add(o.car);
    }
    return set;
  },

  length: function(){
    var n = 0;
    for(var o = this; o instanceof Pair; o=o.cdr){
      n++;
    }
    return n;
  },

  // Return the last cdr
  last_cdr: function(){
    var o;
    for(o = this; o instanceof Pair; o = o.cdr)
      ;
    return o;
  },

  // calls the given func passing each car of list
  // returns cdr of last Pair
  forEach: function(func){
    for(var o = this; o instanceof Pair; o=o.cdr){
      func(o.car);
    }
    return o;
  },

  // Alias of `forEach` (for backward compatibility)
  foreach: function(func){
    for(var o = this; o instanceof Pair; o=o.cdr){
      func(o.car);
    }
    return o;
  },

  // Returns an array which contains the resuls of calling func
  // with the car's as an argument.
  // If the receiver is not a proper list, the last cdr is ignored.
  // The receiver must not be a cyclic list.
  map: function(func){
    var ary = [];
    for(var o = this; isPair(o); o = o.cdr){
      ary.push(func(o.car));
    }
    return ary;
  },

  // Returns a new list made by applying `func` to each element
  mapList: function(func) {
    return array_to_list(this.map(func));
  },

  // Destructively concat the given list to the receiver.
  // The receiver must be a proper list.
  // Returns the receiver.
  concat: function(list){
    var o = this;
    while(o instanceof Pair && o.cdr != nil){
      o = o.cdr;
    }
    o.cdr = list;
    return this;
  },

  // returns human-redable string of pair
  inspect: function(conv){
    conv || (conv = inspect);
    var a = [];
    var last = this.foreach(function(o){
      a.push(conv(o));
    });
    if(last != nil){
      a.push(".");
      a.push(conv(last));
    }
    return "(" + a.join(" ") + ")";
  },
  toString : function(){
    return this.inspect();
  },

  to_write: function(){
    return this.inspect(to_write);
  }
});

// Creates a list out of the arguments, optionally converting any nested arrays into nested lists if the deep argument is true.
// Example:
//   BiwaScheme.List(1, 2, [3, 4]) ;=> (list 1 2 (vector 3 4))
//   BiwaScheme.deep_array_to_list(1, 2, [3, 4]) ;=> (list 1 2 (list 3 4))
const array_to_list_ = function(ary, deep) {
  var list = nil;
  for(var i=ary.length-1; i>=0; i--){
    var obj = ary[i];
    if(deep && _.isArray(obj) && !obj.is_vector){
      obj = array_to_list_(obj, deep);
    }
    list = new Pair(obj, list);
  }
  return list;
}

// Shallow: List(1, 2, [3]) == (list 1 2 (vector 3 4))
const List = function() {
  var ary = _.toArray(arguments);
  return array_to_list_(ary, false);
};

// Shallow: array_to_list(1, 2, [3]) == (list 1 2 (vector 3 4))
const array_to_list = function(ary) {
  return array_to_list_(ary, false);
};

// Deep: deep_array_to_list(1, 2, [3, 4]) == (list 1 2 (list 3 4))
// deep_array_to_list([1, 2, 3]) - deep
const deep_array_to_list = function(ary) {
  return array_to_list_(ary, true);
};

const Cons = function(car, cdr) {
  return new Pair(car, cdr);
};

const js_obj_to_alist = function(obj) {
  if (obj === undefined) {
    return nil;
  }
  var arr = [];
  _.each(obj, function(val, key) {
    arr.push(new Pair(key, val));
  });
  var alist = array_to_list(arr);
  return alist;
};

const alist_to_js_obj = function(alist) {
  if (alist === nil) {
    return {} ;
  }
  var obj = {};
  alist.foreach(function(item){
    obj[item.car] = item.cdr;
  });
  return obj;
};

export { Pair, List, array_to_list, deep_array_to_list, Cons,
         js_obj_to_alist, alist_to_js_obj };
<|endoftext|>


<commit_before>define('table2chart', function () {
    "use strict";

    var me = {};

    me.converters = {
        // TODO handle i18n conversions
        "number": function (val) {
            return parseFloat(val);
        },
        "string": function (val) {
            return val;
        },
        "date": function (val) {
            var d = me.converters.datetime(val);
            // only keep date
            return new Date(d.getFullYear(), d.getMonth(), d.getDate());

        },
        "datetime": function (val) {
            return new Date(val);

        },
        "timeofday": function (val) {
            var m = val.match(/^(\d\d?):(\d\d?):(\d\d?)(\.(\d\d?\d?))?$/);
            if (m.length !== 5 && m.length !== 6) {
                throw Error('Bad timeofday value: ' + val);
            }
            var retval = [m[1], m[2], m[3]];
            if (m.length > 5 && m[5]) retval.push(m[5]);
            return retval;

        },
        "boolean": function (val) {
            if (val &&
                (val.toLowerCase() === 'true' ||
                    val.toLowerCase() === 'yes' ||
                    val.toLowerCase() === 'on')) {
                return true;
            }
            return false;
        }
    };

    // TODO replace the 'ignore' datatype with an option on the placeholder
    me.DATA_TYPE_IGNORE = 'ignore';

    function tableDomAdapter(table) {

        if (table.tagName.toLowerCase() !== 'table') {
            throw Error('Data source must be a table');
        }

        var domCaption, domTBody, domTHead;

        for (var i = 0; i < table.children.length; i++) {
            var child = table.children[i];
            if (child.tagName.toLowerCase() === 'caption') domCaption = child;
            if (child.tagName.toLowerCase() === 'tbody') domTBody = child;
            if (child.tagName.toLowerCase() === 'thead') domTHead = child;
        }
        if (!domTBody) throw Error('table has no "tbody" child');
        if (!domTHead) throw Error('table has no "thead" child');
        if (!domCaption) console.log('table has no "caption" child');

        return {
            getCaption: function () {
                if (domCaption) return domCaption.textContent;
                return null;
            },
            __getHeaderLine: function () {
                return domTHead.children[0];
            },
            __getLine: function (index) {
                return domTBody.children[index];
            },
            getColumnCount: function () {
                return this.__getHeaderLine().childElementCount;
            },
            getLineCount: function () {
                return domTBody.childElementCount;
            },
            getHeaderCell: function (index) {
                var cell = {};

                var domCell = this.__getHeaderLine().children[index];

                cell.label = domCell.textContent;

                cell.dataType = domCell.getAttribute('data-t2c-type');

                if (!cell.dataType) {
                    if (index === 0) cell.dataType = 'string';
                    else cell.dataType = 'number';
                }

                if (cell.dataType && me.converters[cell.dataType]) {
                    cell.converter = me.converters[cell.dataType];
                } else {
                    cell.converter = null;
                }

                return cell;
            },
            getDataCell: function (lineIndex, columnIndex) {
                var header = this.getHeaderCell(columnIndex);
                var line = this.__getLine(lineIndex);
                var td = line.children[columnIndex];
                var retval = td.textContent;
                if (header.converter) {
                    retval = header.converter(retval);
                }
                return retval;
            },

            toGoogleDataTable: function () {
                var googleDataTable = new google.visualization.DataTable();

                var converters = [];

                var columnIndex;

                for (columnIndex = 0; columnIndex < this.getColumnCount(); columnIndex++) {

                    var cell = this.getHeaderCell(columnIndex);
                    if (cell.dataType !== me.DATA_TYPE_IGNORE) {
                        googleDataTable.addColumn(cell.dataType, cell.label);
                    }
                }

                for (var lineIndex = 0; lineIndex < this.getLineCount(); lineIndex++) {

                    var googleDataLine = [];

                    for (columnIndex = 0; columnIndex < this.getColumnCount(); columnIndex++) {

                        var headerCell = this.getHeaderCell(columnIndex);
                        if (headerCell.dataType !== me.DATA_TYPE_IGNORE) {
                            var cellData = this.getDataCell(lineIndex, columnIndex);
                            googleDataLine.push(cellData);
                        }

                    }

                    googleDataTable.addRows([googleDataLine]);
                }

                return googleDataTable;
            }
        };
    }

    function placeholderDomAdapter(placeholder) {
        return {
            applySize: function () {
                var sizeAsString = placeholder.getAttribute('data-t2c-size');
                if (sizeAsString) {
                    var dim = sizeAsString.split('x');
                    if (dim && dim.length === 2) {
                        placeholder.style.width = dim[0];
                        placeholder.style.height = dim[1];
                    }
                }
            },

            getGoogleOptions: function (tableAdapter) {

                var options = {};

                var optAsString = placeholder.getAttribute('data-t2c-options');
                //console.log(optAsString);
                if (optAsString) {
                    try {
                        options = JSON.parse(optAsString);
                    } catch (err) {
                        throw Error('Invalid JSON options: ' + optAsString);
                    }
                }

                if (!options.title) {
                    var caption = tableAdapter.getCaption();
                    if (caption) options.title = caption;
                }

                return options;
            },

            getTableAdapter: function (table) {
                if (!table) table = placeholder;

                var tableId = placeholder.getAttribute('data-t2c-source');

                if (tableId) table = document.getElementById(tableId);

                var tableAdapter = tableDomAdapter(table);

                return tableAdapter;
            },

            createChart: function () {
                var kindId = placeholder.getAttribute('data-t2c');

                if (!google.visualization[kindId]) {
                    throw Error('Unknown Chart kind: ' + kindId);
                }

                return new google.visualization[kindId](placeholder);
            }
        };
    }

    me.apply = function (placeholder, table) {

        var placeholderAdapter = placeholderDomAdapter(placeholder);

        var tableAdapter = placeholderAdapter.getTableAdapter(table);

        var data = tableAdapter.toGoogleDataTable();

        placeholderAdapter.applySize();

        var options = placeholderAdapter.getGoogleOptions(tableAdapter);

        // TODO use ChartWrapper https://developers.google.com/chart/interactive/docs/drawing_charts#chartwrapper
        var chart = placeholderAdapter.createChart();

        chart.draw(data, options);
    };

    return me;
});
<commit_msg>Use Google ChartWrapper API<commit_after>define('table2chart', function () {
    "use strict";

    var me = {};

    me.converters = {
        // TODO handle i18n conversions
        "number": function (val) {
            return parseFloat(val);
        },
        "string": function (val) {
            return val;
        },
        "date": function (val) {
            var d = me.converters.datetime(val);
            // only keep date
            return new Date(d.getFullYear(), d.getMonth(), d.getDate());

        },
        "datetime": function (val) {
            return new Date(val);

        },
        "timeofday": function (val) {
            var m = val.match(/^(\d\d?):(\d\d?):(\d\d?)(\.(\d\d?\d?))?$/);
            if (m.length !== 5 && m.length !== 6) {
                throw Error('Bad timeofday value: ' + val);
            }
            var retval = [m[1], m[2], m[3]];
            if (m.length > 5 && m[5]) retval.push(m[5]);
            return retval;

        },
        "boolean": function (val) {
            if (val &&
                (val.toLowerCase() === 'true' ||
                    val.toLowerCase() === 'yes' ||
                    val.toLowerCase() === 'on')) {
                return true;
            }
            return false;
        }
    };

    // TODO replace the 'ignore' datatype with an option on the placeholder
    me.DATA_TYPE_IGNORE = 'ignore';

    function tableDomAdapter(table) {

        if (table.tagName.toLowerCase() !== 'table') {
            throw Error('Data source must be a table');
        }

        var domCaption, domTBody, domTHead;

        for (var i = 0; i < table.children.length; i++) {
            var child = table.children[i];
            if (child.tagName.toLowerCase() === 'caption') domCaption = child;
            if (child.tagName.toLowerCase() === 'tbody') domTBody = child;
            if (child.tagName.toLowerCase() === 'thead') domTHead = child;
        }
        if (!domTBody) throw Error('table has no "tbody" child');
        if (!domTHead) throw Error('table has no "thead" child');
        if (!domCaption) console.log('table has no "caption" child');

        return {
            getCaption: function () {
                if (domCaption) return domCaption.textContent;
                return null;
            },
            __getHeaderLine: function () {
                return domTHead.children[0];
            },
            __getLine: function (index) {
                return domTBody.children[index];
            },
            getColumnCount: function () {
                return this.__getHeaderLine().childElementCount;
            },
            getLineCount: function () {
                return domTBody.childElementCount;
            },
            getHeaderCell: function (index) {
                var cell = {};

                var domCell = this.__getHeaderLine().children[index];

                cell.label = domCell.textContent;

                cell.dataType = domCell.getAttribute('data-t2c-type');

                if (!cell.dataType) {
                    if (index === 0) cell.dataType = 'string';
                    else cell.dataType = 'number';
                }

                if (cell.dataType && me.converters[cell.dataType]) {
                    cell.converter = me.converters[cell.dataType];
                } else {
                    cell.converter = null;
                }

                return cell;
            },
            getDataCell: function (lineIndex, columnIndex) {
                var header = this.getHeaderCell(columnIndex);
                var line = this.__getLine(lineIndex);
                var td = line.children[columnIndex];
                var retval = td.textContent;
                if (header.converter) {
                    retval = header.converter(retval);
                }
                return retval;
            },

            toGoogleDataTable: function () {
                var googleDataTable = new google.visualization.DataTable();

                var converters = [];

                var columnIndex;

                for (columnIndex = 0; columnIndex < this.getColumnCount(); columnIndex++) {

                    var cell = this.getHeaderCell(columnIndex);
                    if (cell.dataType !== me.DATA_TYPE_IGNORE) {
                        googleDataTable.addColumn(cell.dataType, cell.label);
                    }
                }

                for (var lineIndex = 0; lineIndex < this.getLineCount(); lineIndex++) {

                    var googleDataLine = [];

                    for (columnIndex = 0; columnIndex < this.getColumnCount(); columnIndex++) {

                        var headerCell = this.getHeaderCell(columnIndex);
                        if (headerCell.dataType !== me.DATA_TYPE_IGNORE) {
                            var cellData = this.getDataCell(lineIndex, columnIndex);
                            googleDataLine.push(cellData);
                        }

                    }

                    googleDataTable.addRows([googleDataLine]);
                }

                return googleDataTable;
            }
        };
    }

    function placeholderDomAdapter(placeholder) {
        return {
            applySize: function () {
                var sizeAsString = placeholder.getAttribute('data-t2c-size');
                if (sizeAsString) {
                    var dim = sizeAsString.split('x');
                    if (dim && dim.length === 2) {
                        placeholder.style.width = dim[0];
                        placeholder.style.height = dim[1];
                    }
                }
            },

            getGoogleOptions: function (tableAdapter) {

                var options = {};

                var optAsString = placeholder.getAttribute('data-t2c-options');
                //console.log(optAsString);
                if (optAsString) {
                    try {
                        options = JSON.parse(optAsString);
                    } catch (err) {
                        throw Error('Invalid JSON options: ' + optAsString);
                    }
                }

                if (!options.title) {
                    var caption = tableAdapter.getCaption();
                    if (caption) options.title = caption;
                }

                return options;
            },

            getTableAdapter: function (table) {
                if (!table) table = placeholder;

                var tableId = placeholder.getAttribute('data-t2c-source');

                if (tableId) table = document.getElementById(tableId);

                var tableAdapter = tableDomAdapter(table);

                return tableAdapter;
            },

            drawChart: function (table) {
                var kindId = placeholder.getAttribute('data-t2c');

                if (!google.visualization[kindId]) {
                    throw Error('Unknown Chart kind: ' + kindId);
                }

                var tableAdapter = this.getTableAdapter(table);

                var data = tableAdapter.toGoogleDataTable();

                this.applySize();

                var options = this.getGoogleOptions(tableAdapter);

                var wrapper = new google.visualization.ChartWrapper({
                    'chartType': kindId,
                    'dataTable': data,
                    'options': options,
                    'containerId': placeholder
                });
                wrapper.draw();
            }
        };
    }

    me.apply = function (placeholder, table) {

        var placeholderAdapter = placeholderDomAdapter(placeholder);

        placeholderAdapter.drawChart(table);
    };

    return me;
});
<|endoftext|>


<commit_before>import React from 'react';
import Helmet from 'react-helmet';

//components
import ContentWrapper from '../../components/ContentWrapper';
import MainHeadline from '../../components/MainHeadline';
import SecondaryHeadline from '../../components/SecondaryHeadline';
import MainParagraph from '../../components/MainParagraph';
import TextLink from '../../components/TextLink';
import LayoutWrapper from './LayoutWrapper';
import LayoutMainColumn from './LayoutMainColumn';
import LayoutSecondaryColumn from './LayoutSecondaryColumn';
import Section from './Section';
import LinksList from './LinksList';

class MainPage extends React.Component {

  // page is static, no need to re-render it
  shouldComponentUpdate() {
    return false;
  }

  render() {
    return (
      <ContentWrapper>
        <Helmet title="Overview" />
        <MainHeadline>Welcome to React Quickstart</MainHeadline>
        <MainParagraph>React Quickstart is starting kit for creating fast and responsive web applications with React. It takes care of initial developement environment setup, boilerplating and puts everything to use in simple example. Notable technologies used are: React, Redux, React Router, Babel, Webpack2, ESLint.</MainParagraph>
        <LayoutWrapper>
          <LayoutMainColumn>

              <SecondaryHeadline>Getting started!</SecondaryHeadline>

Just follow a few easy steps to set up demo project:

Install Node, preferably > 6.9.1.

Install Git.

Clone the repository git clone https://github.com/blurbyte/react-quickstart.git

Install React Developer Tools – provides insight about virtual DOM and components props.

Install Redux DevTools – lets you inspect application state.

Install <TextLink href="https://yarnpkg.com/">Yarn</TextLink> – installing modules super fast (20 seconds instead of 3 minutes with npm install)

Enter project folder and install all required modules with simple yarn command.

Start demo Redux aplication npm start -s. It will build a project, start webserver up and open app in your browser. It also automaticly lint code and run tests each time you save. Webpack serves all files from memory, no physical files are created.

Take some time and review example counter application code. All project files are placed under /src folder.

Build final project npm run build. Bundled application will be found in /dist directory. Files are created on disk.

You can make a copy of the project for future reference.
Remove all test application files npm run remove-demo. It will leave bare bones for your own React, Redux, React Router app 🙂.

              <SecondaryHeadline>Comprehensive modules overview</SecondaryHeadline>

Althought most important technologies used are React, Redux, React Router and Webpack there are a lot of other interesting tech choices:

                    Related Modules
                    Description

                    react, react-dom
                    Reusable components for web applications in JavaScript

                    redux, react-redux
                    Holds state of application in single place, solves data flow between components

                    redux-saga
                    Library for managing side effects, such as asynchronous data fetching, in redux

                    react-router, react-router-redux, react-router-scroll
                    Simple declarative router for React apps

                    react-helmet
                    Document head management – change title and meta tags of the page

                    whatwg-fetch, promise-polyfill
                    Fetch (modern replacement for XMLHttpRequest) polyfill

                    babel-cli, babel-core, babel-loader and other babel modules
                    Take advantage of the newest JavaScript features, transpiles ES2015+ to ES5

                    eslint, eslint-watch and eslint plugins
                    Reports JavaScript and React syntax errors

                    webpack2, webpack-dev-middleware, webpack-hot-middleware and all loaders
                    Bundles all modules into single file, enables React hot reloading – tweaking components in real time

                    html-webpack-plugin, webpack-md5-hash
                    Creates custom html files for cache busting

                    styled-components
                    Write localized components styles in JavaScript

                    sanitize.css
                    Modern css reset stylesheet

                    express, compression
                    Sets up Node.js development and production webservers

                    jest
                    Automated tests framework

                    chalk
                    Cool text colors and backgrounds for Terminal

                    npm-run-all
                    Allows to run multiple npm scripts in parallel

                    rimraf
                    Small files/direcories removal utility

                    open
                    Opens application in web browser automatically

              <SecondaryHeadline>Directories structure explained</SecondaryHeadline>

List of important files and directories:

package.json

list of all installed npm modules

.npmrc

saves exact version of the module

.eslintrc.json

ESLint configuration file

list of installed ESLint plugins and rules

.babelrc

Babel configuration file

.editorconfig

Enforces typing rules (via code editor plugin)

.gitignore

tells Git which files it should ignore

webpack.config.dev.js

webpack configuration file for development

React hot module replacement enabled

webpack.config.prod.js

webpack configuration file for production

cache busting with hashed names for bundled files

src/routes.js

react-router routes declarations

src/index.js

application starting point

Redux store and provider configuration

src/index.ejs

template html file for html-webpack-plugin

src/webpack-public-path.js

properly sets up webpack public path for development

src/styles

global CSS styles and media queries setup

src/fonts

custom fonts

src/actions

Redux action creators

list of actions which can occur in application

src/actions/actionTypes.js

list of all available action types

src/store.js

Redux store configuration

different middleware configurations for production and development

ReduxDevTools setup

src/reducers

Redux reducers

application state is alerted here based on actions received

src/reducers/initialState.js

default state of reducers

src/reducers/index.js

Redux root reducer

combines all reducers into single one

src/sagas

managing side effects with redux-saga

src/components

React presentational components

tests and styles for each component in the same location

src/containers

React container components

application's logic and root page views

src/containers/App

React application root/wrapper component

          </LayoutMainColumn>
          <LayoutSecondaryColumn>

              <SecondaryHeadline>What will you learn?</SecondaryHeadline>

By inspecting source code of demo counter application you can get a good grasp of:

How to set up JavaScript development environment for single page React web app

How to organize file structure of your project

How to write Redux action creators, reducers and connect components with store

How to manage side effects with redux-saga

How to use Redux application state in combination with container component state

How to write simple unit tests for actions and integration tests for reducers

How to take advantage of styled-components

How to integrate custom fonts

              <SecondaryHeadline>Do you really need all this stuff?</SecondaryHeadline>

If you are not sure wheter you need any of these technologies, you probably don't. It's the easiest rule of thumb you should follow.

In simple real world web app you probably can get away with single style sheet and no Redux or router at all.

The bigger project gets, the more people are working on it, the greater need for modularization and encapsulation. Fortunately React and Redux are extremely flexible and scalable.

              <SecondaryHeadline>Check it out!</SecondaryHeadline>
              <LinksList>

Check my other projects on <TextLink href="https://github.com/blurbyte">GitHub</TextLink>

Contact me on <TextLink href="https://twitter.com/blurbyte">Twitter</TextLink>

Visit <TextLink href="http://blurbyte.com/">blurbyte.com</TextLink>

              </LinksList>

          </LayoutSecondaryColumn>
      </LayoutWrapper>
      </ContentWrapper>
    );
  }
}

export default MainPage;
<commit_msg>Documentation updated<commit_after>import React from 'react';
import Helmet from 'react-helmet';

//components
import ContentWrapper from '../../components/ContentWrapper';
import MainHeadline from '../../components/MainHeadline';
import SecondaryHeadline from '../../components/SecondaryHeadline';
import MainParagraph from '../../components/MainParagraph';
import TextLink from '../../components/TextLink';
import LayoutWrapper from './LayoutWrapper';
import LayoutMainColumn from './LayoutMainColumn';
import LayoutSecondaryColumn from './LayoutSecondaryColumn';
import Section from './Section';
import LinksList from './LinksList';

class MainPage extends React.Component {

  // page is static, no need to re-render it
  shouldComponentUpdate() {
    return false;
  }

  render() {
    return (
      <ContentWrapper>
        <Helmet title="Overview" />
        <MainHeadline>Welcome to React Quickstart</MainHeadline>
        <MainParagraph>React Quickstart is starting kit for creating fast and responsive web applications with React. It takes care of initial developement environment setup, boilerplating and puts everything to use in simple example. Notable technologies used are: React, Redux, React Router, Babel, Webpack2, ESLint.</MainParagraph>
        <LayoutWrapper>
          <LayoutMainColumn>

              <SecondaryHeadline>Getting started!</SecondaryHeadline>

Just follow a few easy steps to set up demo project:

Install Node, preferably > 6.9.1.

Install Git.

Clone the repository git clone https://github.com/blurbyte/react-quickstart.git

Install React Developer Tools – provides insight about virtual DOM and components props.

Install Redux DevTools – lets you inspect application state.

Install <TextLink href="https://yarnpkg.com/">Yarn</TextLink> – installing modules super fast (20 seconds instead of 3 minutes with npm install)

Enter project folder and install all required modules with simple yarn command.

Start demo Redux aplication npm start -s. It will build a project, start webserver up and open app in your browser. It also automaticly lint code and run tests each time you save. Webpack serves all files from memory, no physical files are created.

Take some time and review example counter application code. All project files are placed under /src folder.

Build final project npm run build. Bundled application will be found in /dist directory. Files are created on disk.

You can make a copy of the project for future reference.
Remove all test application files npm run remove-demo. It will leave bare bones for your own React, Redux, React Router app 🙂.

              <SecondaryHeadline>Comprehensive modules overview</SecondaryHeadline>

Althought most important technologies used are React, Redux, React Router and Webpack there are a lot of other interesting tech choices:

                    Related Modules
                    Description

                    react, react-dom
                    Reusable components for web applications in JavaScript

                    redux, react-redux
                    Holds state of application in single place, solves data flow between components

                    redux-saga
                    Library for managing side effects, such as asynchronous data fetching, in redux

                    react-router, react-router-redux, react-router-scroll
                    Simple declarative router for React apps

                    react-helmet
                    Document head management – change title and meta tags of the page

                    whatwg-fetch, promise-polyfill
                    Fetch (modern replacement for XMLHttpRequest) polyfill

                    babel-cli, babel-core, babel-loader and other babel modules
                    Take advantage of the newest JavaScript features, transpiles ES2015+ to ES5

                    eslint, eslint-watch and eslint plugins
                    Reports JavaScript and React syntax errors

                    webpack2, webpack-dev-middleware, webpack-hot-middleware and all loaders
                    Bundles all modules into single file, enables React hot reloading – tweaking components in real time

                    html-webpack-plugin, webpack-md5-hash
                    Creates custom html files for cache busting

                    styled-components
                    Write localized components styles in JavaScript

                    sanitize.css
                    Modern css reset stylesheet

                    express, compression
                    Sets up Node.js development and production webservers

                    jest
                    Automated tests framework

                    chalk
                    Cool text colors and backgrounds for Terminal

                    npm-run-all
                    Allows to run multiple npm scripts in parallel

                    rimraf
                    Small files/direcories removal utility

                    open
                    Opens application in web browser automatically

              <SecondaryHeadline>Directories structure explained</SecondaryHeadline>

List of important files and directories:

package.json

list of all installed npm modules

.npmrc

saves exact version of the module

.eslintrc.json

ESLint configuration file

list of installed ESLint plugins and rules

.babelrc

Babel configuration file

.editorconfig

Enforces typing rules (via code editor plugin)

.gitignore

tells Git which files it should ignore

webpack.config.dev.js

webpack configuration file for development

React hot module replacement enabled

webpack.config.prod.js

webpack configuration file for production

cache busting with hashed names for bundled files

src/index.js

application starting point

Redux store and provider configuration

src/index.ejs

template html file for html-webpack-plugin

src/webpack-public-path.js

properly sets up webpack public path for development

src/styles

global CSS styles and media queries setup

src/fonts

custom fonts

src/actions

Redux action creators

list of actions which can occur in application

src/actions/actionTypes.js

list of all available action types

src/store.js

Redux store configuration

different middleware configurations for production and development

ReduxDevTools setup

src/reducers

Redux reducers

application state is alerted here based on actions received

src/reducers/initialState.js

default state of reducers

src/reducers/index.js

Redux root reducer

combines all reducers into single one

src/sagas

managing side effects with redux-saga

src/components

React presentational components

tests and styles for each component in the same location

src/containers

React container components

application's logic and root page views

src/containers/App

React application root/wrapper component

contains react-router routes declarations

          </LayoutMainColumn>
          <LayoutSecondaryColumn>

              <SecondaryHeadline>What will you learn?</SecondaryHeadline>

By inspecting source code of demo counter application you can get a good grasp of:

How to set up JavaScript development environment for single page React web app

How to organize file structure of your project

How to write Redux action creators, reducers and connect components with store

How to manage side effects with redux-saga

How to use Redux application state in combination with container component state

How to write simple unit tests for actions and integration tests for reducers

How to take advantage of styled-components

How to integrate custom fonts

              <SecondaryHeadline>Do you really need all this stuff?</SecondaryHeadline>

If you are not sure wheter you need any of these technologies, you probably don't. It's the easiest rule of thumb you should follow.

In simple real world web app you probably can get away with single style sheet and no Redux or router at all.

The bigger project gets, the more people are working on it, the greater need for modularization and encapsulation. Fortunately React and Redux are extremely flexible and scalable.

              <SecondaryHeadline>Check it out!</SecondaryHeadline>
              <LinksList>

Check my other projects on <TextLink href="https://github.com/blurbyte">GitHub</TextLink>

Contact me on <TextLink href="https://twitter.com/blurbyte">Twitter</TextLink>

Visit <TextLink href="http://blurbyte.com/">blurbyte.com</TextLink>

              </LinksList>

          </LayoutSecondaryColumn>
      </LayoutWrapper>
      </ContentWrapper>
    );
  }
}

export default MainPage;
<|endoftext|>


<commit_before>var _ = require('underscore');
var request = require('request');
var cheerio = require('cheerio');

var models = require('../models');

var Scrape = models.Scrape;

var makerPage = function(req, res) {

    Scrape.ScrapeModel.findByOwner(req.session.account._id, function(err, docs) {

        if(err) {
            console.log(err);
            return res.status(400).json({error:'An error occurred'});
        }

        res.render('gallery', {scrapes: docs});
    });
};

function getTestResults(){
    request('http://forum.miata.net/vb/forumdisplay.php?f=124', function (error, response, html) {
        if (!error && response.statusCode == 200) {
            var $ = cheerio.load(html);
            console.log('zxcvbnm');
            var tRes= $("a:contains('wheels')").each(function(){
                console.log($(this).href());
            });
        }
    });

    return [{url:'http://www.example.com/'},{url:'http://www.example.com/'}];
}

var testResults = getTestResults();

var makeScrape = function(req, res) {
    if(!req.body.url || !req.body.query) {
        return res.status(400).json({error: "Both URL and Query are required"});
    }

    var scrapeData = {
        url: req.body.url,
        query: req.body.query,
        results: testResults,
        owner: req.session.account._id
    };

    var newScrape = new Scrape.ScrapeModel(scrapeData);

    newScrape.save(function(err) {
        if(err) {
            console.log(err);
            return res.status(400).json({error:'An error occurred'});
        }

        res.json({redirect: '/gallery'});
    });

};

module.exports.makerPage = makerPage;
module.exports.make = makeScrape;<commit_msg>added request and cheerio, wrote a hard coded test query to get all of the links on a page<commit_after>var _ = require('underscore');
var request = require('request');
var cheerio = require('cheerio');

var models = require('../models');

var Scrape = models.Scrape;

var makerPage = function(req, res) {

    Scrape.ScrapeModel.findByOwner(req.session.account._id, function(err, docs) {

        if(err) {
            console.log(err);
            return res.status(400).json({error:'An error occurred'});
        }

        res.render('gallery', {scrapes: docs});
    });
};

function getTestResults(){
    request('http://forum.miata.net/vb/forumdisplay.php?f=124', function (error, response, html) {
        if (!error && response.statusCode == 200) {
            var $ = cheerio.load(html);
            console.log('zxcvbnm');
            var tRes= $("a:contains('wheels')").each(function(){
                console.log($(this).text());
            });
        }
    });

    return [{url:'http://www.example.com/'},{url:'http://www.example.com/'}];
}

var testResults = getTestResults();

var makeScrape = function(req, res) {
    if(!req.body.url || !req.body.query) {
        return res.status(400).json({error: "Both URL and Query are required"});
    }

    var scrapeData = {
        url: req.body.url,
        query: req.body.query,
        results: testResults,
        owner: req.session.account._id
    };

    var newScrape = new Scrape.ScrapeModel(scrapeData);

    newScrape.save(function(err) {
        if(err) {
            console.log(err);
            return res.status(400).json({error:'An error occurred'});
        }

        res.json({redirect: '/gallery'});
    });

};

module.exports.makerPage = makerPage;
module.exports.make = makeScrape;<|endoftext|>


<commit_before>angular.module("routeUrls", [])

.factory("urls", function($route) {

    // Cache the routing paths for any named routes.
    var pathsByName = {};
    angular.forEach($route.routes, function (route, path) {
        if (route.name) {
            pathsByName[route.name] = path;
        }
    });

    // Param name to replacement regex cache.
    var regexs = {};

    // Build a path for the named route from the route's URL and the given
    // params.
    var path = function (name, params) {
        var url = pathsByName[name] || "/";
        angular.forEach(params || {}, function (value, key) {
            var regex = regexs[key];
            if (regex === undefined) {
                regex = regexs[key] = new RegExp(":" + key + "(?=/|$)");
            }
            url = url.replace(regex, value);
        });
        return url;
    };

    return {
        path: path,
        href: function (name, params) {
            return "#" + path(name, params);
        }
    };
});
<commit_msg>Make url construction sensitive to $locationProvider configuration.<commit_after>angular.module("routeUrls", [])

.provider("urls", function($locationProvider) {
    return {
        $get: function($route) {

            // Cache the routing paths for any named routes.
            var pathsByName = {};
            angular.forEach($route.routes, function (route, path) {
                if (route.name) {
                    pathsByName[route.name] = path;
                }
            });

            // Param name to replacement regex cache.
            var regexs = {};

            // Build a path for the named route from the route's URL and the given
            // params.
            var path = function (name, params) {
                var url = pathsByName[name] || "/";
                angular.forEach(params || {}, function (value, key) {
                    var regex = regexs[key];
                    if (regex === undefined) {
                        regex = regexs[key] = new RegExp(":" + key + "(?=/|$)");
                    }
                    url = url.replace(regex, value);
                });
                return url;
            };

            // Query $locationProvider for its configuration.
            var html5Mode = $locationProvider.html5Mode();
            var hashPrefix = $locationProvider.hashPrefix();

            return {
                path: path,
                href: function (name, params) {
                    if (html5Mode) {
                        return path(name, params);
                    }
                    return "#" + hashPrefix + path(name, params);
                }
            };
        }
    };
});
<|endoftext|>


<commit_before>/**
 * Date Range Field
 *
 * @class Hatimeria.core.field.DateRangeField
 * @extends Ext.form.field.Basic
 */
(function() {

    Ext.define('Hatimeria.core.field.DateRangeField', {
        extend: 'Ext.form.field.Picker',

        /**
         * @cfg {Object} value
         */
        value: {
            from: new Date,
            to: new Date
        },

        /**
         * Type of data which will be returned ("object"/"string"/"d/m/Y")
         *
         * @cfg {String} returnFormat
         */
        returnFormat: 'string',

        /**
         * Default width
         *
         * @cfg {Number} width
         */
        width: 170,

        /**
         * @cfg {String} format corresponding to Ext.util.Format.date()
         */
        format: 'd/m/Y',

        /**
         * @private
         * @property {Array}
         */
        rangeBoilerplates: [
            {key: 'yesterday', value: 'Wczoraj'},
            {key: 'week',      value: 'Ostatni tydzień'},
            {key: 'prevweek',  value: 'Przedostatni tydzień'},
            {key: 'month',     value: 'Ostatni miesiąc'},
            {key: 'prevmonth', value: 'Przedostatni miesiąc'},
            {key: 'year',      value: 'Z ostatniego roku'}
        ],

        pickerAlign: 'bl',

        /**
         * @private
         */
        matchFieldWidth: false,

        /**
         * @private
         * @property
         */
        triggerCls : Ext.baseCSSPrefix + 'form-date-trigger',

        /**
         * Converts string to value Object
         *
         * @param {String} value
         * @private
         */
        dateRangeToObject: function(value)
        {
            var dates = value.split(/\s+\-\s+/);
            var date = {
                from: this.convertToDate(dates[0]),
                to: this.convertToDate(dates[1])
            }

            return date;
        },

        /**
         * String to object {Date}
         *
         * @param {String}
         * @return {Date}
         */
        convertToDate: function(value)
        {
            if (typeof value == 'object')
            {
                if (value instanceof Date)
                {
                    return value;
                }
                else
                {
                    throw new Error('Object must be instance of Date');
                }
            }
            else
            {
                return Ext.Date.parse(value, this.format);
            }
        },

        /**
         * Guess!
         *
         * @param {Object}
         */
        setValue: function(value)
        {
            var bp = this.createBoilerplates();

            if (typeof value == 'string' && typeof bp[value] == 'object')
            {
                value = bp[value];
            }
            else
            {
                var defaultValue = {
                    from: new Date,
                    to: new Date
                };

                if (!Ext.isObject(value))
                {
                    value = this.defaultValue;
                }

                value = Ext.apply(defaultValue, value);
                value.from = this.convertToDate(value.from);
                value.to = this.convertToDate(value.to);
            }

            this.lastValue = value;

            return this.callParent([value]);
        },

        /**
         * @param {String} value Must be {from: {Date}, to: {Date}}
         * @return {String}
         */
        valueToRaw: function(value)
        {
            var strFrom = Ext.Date.format(value.from, this.format);
            var strTo = Ext.Date.format(value.to, this.format);

            return strFrom + ' - ' + strTo;
        },

        /**
         * Conversion from text to object
         *
         * @param {String}
         * @return {Object}
         */
        rawToValue: function(value)
        {
            return this.dateRangeToObject(value);
        },

        /**
         * Check if dates are equal
         *
         * @return {Boolean}
         */
        isEqualRangeDate: function(newVal, oldVal)
        {
            return Ext.JSON.encode(newVal) == Ext.JSON.encode(oldVal);
        },

        /**
         * Check if value changed
         *
         * @param {Boolean} force optional to force event
         */
        checkChange: function(force)
        {
            if (!this.suspendCheckChange)
            {
                var me = this,
                    newVal = me.getValue(),
                    oldVal = me.lastValue;

                if (!this.isDateRangeValid(newVal))
                {
                    me.onChange(newVal, oldVal);
                    return false;
                }

                if ((!me.isEqualRangeDate(newVal, oldVal) || force) && !me.isDestroyed)
                {
                    me.lastValue = newVal;
                    var newValFormatted = me.getUnifiedReturnValue(newVal);
                    var oldValFormatted = me.getUnifiedReturnValue(oldVal);
                    me.fireEvent('change', me, newValFormatted, oldValFormatted);
                    me.onChange(newVal, oldVal);
                }
            }
        },

        /**
         * Creates instance of picker
         */
        createPicker: function()
        {
            var _this = this;
            var picker = Ext.create('Ext.container.Container', {
                floating: true,
                border: 0,
                width: 405,
                height: 260,
                hidden: true,
                padding: 5,
                ownerCt: this.ownerCt,
                layout: 'auto',
                items: [
                    {
                        xtype: 'combobox',
                        cls: 'ranges-combo',
                        itemId: 'boilerplates-combobox',
                        margin: '10 0 0 10',
                        store: Ext.create('Ext.data.Store', {
                            fields: ['key', 'value'],
                            data: this.rangeBoilerplates
                        }),
                        listConfig: {
                            cls: 'ranges-combo-boundlist'
                        },
                        editable: false,
                        fieldLabel: 'Wybierz przedział',
                        valueField: 'key',
                        displayField: 'value',
                        listeners: {
                            change: function(combo, value) {
                                _this.onBoilerplateSelect(combo, value);
                            }
                        }
                    },
                    {
                        xtype: 'panel',
                        layout: 'hbox',
                        itemId: 'pickers-panel',
                        border: 0,
                        defaults: {margin: 5},
                        items: [
                            Ext.create('Ext.picker.Date', {
                                itemId: 'picker-from',
                                format: this.format,
                                handler: function(picker, date) {
                                    _this.onDateSelect('from', date);
                                }
                            }),
                            Ext.create('Ext.picker.Date', {
                                itemId: 'picker-to',
                                format: this.format,
                                handler: function(picker, date) {
                                    _this.onDateSelect('to', date)
                                }
                            })
                        ]
                    }
                ]
            });

            this.picker = picker;

            return this.picker;
        },

        /**
         * Apply date values to pickers
         *
         * @param {Object} value {from:Date, to:Date}
         */
        applyToPicker: function(value)
        {
            if (this.picker)
            {
                this.picker.down('#picker-from').setValue(value.from);
                this.picker.down('#picker-to').setValue(value.to);
            }
        },

        /**
         * Walkround collapsing main picker
         */
        collapseIf: function(event, target)
        {
            if (!Ext.get(target).up('.ranges-combo-boundlist'))
            {
                this.callParent(arguments);
            }
        },

        /**
         * Event: react on expanding picker
         */
        onExpand: function()
        {
            this.applyToPicker(this.value);
        },

        /**
         * Apply new date
         *
         * @param {String} type
         * @param {Date} date
         */
        onDateSelect: function(type, date)
        {
            this.value[type] = date;
            this.setValue(this.value);
            this.checkChange(true);
        },

        /**
         * Validator for raw date range
         *
         * @return {Boolean}
         */
        isRawDateRangeValid: function(value)
        {
            value = this.dateRangeToObject(value);

            return (value.from && value.to);
        },

        /**
         * Check if date range object is valid
         *
         * @param {Object} value
         * @return {Boolean}
         */
        isDateRangeValid: function(value)
        {
            return value.from && value.to && typeof value.from.getDate == 'function' && typeof value.to.getDate == 'function' ;
        },

        /**
         * Validate raw value
         *
         * @return {Boolean}/{String}
         */
        validator: function(value)
        {
            if (!this.isRawDateRangeValid(value))
            {
                return "Daty są w nieprawidłowym formacie!";
            }

            return true;
        },

        /**
         * Convert declared output format
         *
         * @param {Object} value Must be {from:Date, to:Date}
         * @return {Object}
         */
        getUnifiedReturnValue: function(value)
        {
            if (this.returnFormat == 'object')
            {
                return value;
            }

            var format = (this.returnFormat == 'string') ? this.format : this.returnFormat;

            return {
                from: Ext.Date.format(value.from, format),
                  to: Ext.Date.format(value.to, format)
            };
        },

        /**
         * Submit output
         *
         * @return {Object}
         */
        getSubmitValue: function()
        {
            return this.getUnifiedReturnValue(this.value);
        },

        /**
         * Ready to use date ranges
         *
         * @retutn {Object}
         */
        createBoilerplates: function()
        {
            if (this.boilerplates)
            {
                return this.boilerplates;
            }

            var now = new Date();

            var dayBack = new Date();
            dayBack.setDate(dayBack.getDate()-1);

            var weekBack = new Date();
            weekBack.setDate(weekBack.getDate()-7);

            var twoweekBack = new Date();
            twoweekBack.setDate(twoweekBack.getDate()-14);

            var monthBack = new Date();
            monthBack.setMonth(monthBack.getMonth()-1);

            var twomonthBack = new Date();
            twomonthBack.setMonth(twomonthBack.getMonth()-2);

            var yearBack = new Date();
            yearBack.setFullYear(yearBack.getFullYear()-1);

            this.boilerplates = {
                yesterday: {to: now, from: dayBack},
                week:      {to: now, from: weekBack},
                prevweek:  {to: weekBack, from: twoweekBack},
                month:     {to: now, from: monthBack},
                prevmonth: {to: monthBack, from: twomonthBack},
                year:      {to: now, from: yearBack}
            }

            return this.boilerplates;
        },

        /**
         * Event: Selected boilerplate
         *
         * @param Ext.form.field.ComboBox combo
         * @param {String} name
         */
        onBoilerplateSelect: function(combo, name)
        {
            var bp = this.createBoilerplates();
            this.value = bp[name];
            this.setValue(this.value);
            this.collapse();
        }
    });

})();
<commit_msg>fixes default date format, boilerplate<commit_after>/**
 * Date Range Field
 *
 * @class Hatimeria.core.field.DateRangeField
 * @extends Ext.form.field.Basic
 */
(function() {

    Ext.define('Hatimeria.core.field.DateRangeField', {
        extend: 'Ext.form.field.Picker',
        requires: [
          "Ext.Date"
        ],

        /**
         * @cfg {Object} value
         */
        value: {
            from: new Date,
            to: new Date
        },

        /**
         * Type of data which will be returned ("object"/"string"/"d/m/Y")
         *
         * @cfg {String} returnFormat
         */
        returnFormat: 'string',

        /**
         * Default width
         *
         * @cfg {Number} width
         */
        width: 170,

        /**
         * @cfg {String} format corresponding to Ext.util.Format.date()
         */
        format: 'Y-m-d',

        /**
         * @private
         * @property {Array}
         */
        rangeBoilerplates: [
            {key: 'yesterday', value: 'Wczoraj'},
            {key: 'week',      value: 'Ostatni tydzień'},
            {key: 'prevweek',  value: 'Przedostatni tydzień'},
            {key: 'month',     value: 'Ten miesiąc'},
            {key: 'prevmonth', value: 'Poprzedni miesiąc'},
            {key: 'year',      value: 'Ten rok'}
        ],

        pickerAlign: 'bl',

        /**
         * @private
         */
        matchFieldWidth: false,

        /**
         * @private
         * @property
         */
        triggerCls : Ext.baseCSSPrefix + 'form-date-trigger',

        /**
         * Converts string to value Object
         *
         * @param {String} value
         * @private
         */
        dateRangeToObject: function(value)
        {
            var dates = value.split(/\s+\-\s+/);
            var date = {
                from: this.convertToDate(dates[0]),
                to: this.convertToDate(dates[1])
            }

            return date;
        },

        /**
         * String to object {Date}
         *
         * @param {String}
         * @return {Date}
         */
        convertToDate: function(value)
        {
            if (typeof value == 'object')
            {
                if (value instanceof Date)
                {
                    return value;
                }
                else
                {
                    throw new Error('Object must be instance of Date');
                }
            }
            else
            {
                return Ext.Date.parse(value, this.format);
            }
        },

        /**
         * Guess!
         *
         * @param {Object}
         */
        setValue: function(value)
        {
            var bp = this.createBoilerplates();

            if (typeof value == 'string' && typeof bp[value] == 'object')
            {
                value = bp[value];
            }
            else
            {
                var defaultValue = {
                    from: new Date,
                    to: new Date
                };

                if (!Ext.isObject(value))
                {
                    value = this.defaultValue;
                }

                value = Ext.apply(defaultValue, value);
                value.from = this.convertToDate(value.from);
                value.to = this.convertToDate(value.to);
            }

            this.lastValue = value;

            return this.callParent([value]);
        },

        /**
         * @param {String} value Must be {from: {Date}, to: {Date}}
         * @return {String}
         */
        valueToRaw: function(value)
        {
            var strFrom = Ext.Date.format(value.from, this.format);
            var strTo = Ext.Date.format(value.to, this.format);

            return strFrom + ' - ' + strTo;
        },

        /**
         * Conversion from text to object
         *
         * @param {String}
         * @return {Object}
         */
        rawToValue: function(value)
        {
            return this.dateRangeToObject(value);
        },

        /**
         * Check if dates are equal
         *
         * @return {Boolean}
         */
        isEqualRangeDate: function(newVal, oldVal)
        {
            return Ext.JSON.encode(newVal) == Ext.JSON.encode(oldVal);
        },

        /**
         * Check if value changed
         *
         * @param {Boolean} force optional to force event
         */
        checkChange: function(force)
        {
            if (!this.suspendCheckChange)
            {
                var me = this,
                    newVal = me.getValue(),
                    oldVal = me.lastValue;

                if (!this.isDateRangeValid(newVal))
                {
                    me.onChange(newVal, oldVal);
                    return false;
                }

                if ((!me.isEqualRangeDate(newVal, oldVal) || force) && !me.isDestroyed)
                {
                    me.lastValue = newVal;
                    var newValFormatted = me.getUnifiedReturnValue(newVal);
                    var oldValFormatted = me.getUnifiedReturnValue(oldVal);
                    me.fireEvent('change', me, newValFormatted, oldValFormatted);
                    me.onChange(newVal, oldVal);
                }
            }
        },

        /**
         * Creates instance of picker
         */
        createPicker: function()
        {
            var _this = this;
            var picker = Ext.create('Ext.container.Container', {
                floating: true,
                border: 0,
                width: 405,
                height: 260,
                hidden: true,
                padding: 5,
                ownerCt: this.ownerCt,
                layout: 'auto',
                items: [
                    {
                        xtype: 'combobox',
                        cls: 'ranges-combo',
                        itemId: 'boilerplates-combobox',
                        margin: '10 0 0 10',
                        store: Ext.create('Ext.data.Store', {
                            fields: ['key', 'value'],
                            data: this.rangeBoilerplates
                        }),
                        listConfig: {
                            cls: 'ranges-combo-boundlist'
                        },
                        editable: false,
                        fieldLabel: 'Wybierz przedział',
                        valueField: 'key',
                        displayField: 'value',
                        listeners: {
                            change: function(combo, value) {
                                _this.onBoilerplateSelect(combo, value);
                            }
                        }
                    },
                    {
                        xtype: 'panel',
                        layout: 'hbox',
                        itemId: 'pickers-panel',
                        border: 0,
                        defaults: {margin: 5},
                        items: [
                            Ext.create('Ext.picker.Date', {
                                itemId: 'picker-from',
                                format: this.format,
                                handler: function(picker, date) {
                                    _this.onDateSelect('from', date);
                                }
                            }),
                            Ext.create('Ext.picker.Date', {
                                itemId: 'picker-to',
                                format: this.format,
                                handler: function(picker, date) {
                                    _this.onDateSelect('to', date)
                                }
                            })
                        ]
                    }
                ]
            });

            this.picker = picker;

            return this.picker;
        },

        /**
         * Apply date values to pickers
         *
         * @param {Object} value {from:Date, to:Date}
         */
        applyToPicker: function(value)
        {
            if (this.picker)
            {
                this.picker.down('#picker-from').setValue(value.from);
                this.picker.down('#picker-to').setValue(value.to);
            }
        },

        /**
         * Walkround collapsing main picker
         */
        collapseIf: function(event, target)
        {
            if (!Ext.get(target).up('.ranges-combo-boundlist'))
            {
                this.callParent(arguments);
            }
        },

        /**
         * Event: react on expanding picker
         */
        onExpand: function()
        {
            this.applyToPicker(this.value);
        },

        /**
         * Apply new date
         *
         * @param {String} type
         * @param {Date} date
         */
        onDateSelect: function(type, date)
        {
            this.value[type] = date;
            this.setValue(this.value);
            this.checkChange(true);
        },

        /**
         * Validator for raw date range
         *
         * @return {Boolean}
         */
        isRawDateRangeValid: function(value)
        {
            value = this.dateRangeToObject(value);

            return (value.from && value.to);
        },

        /**
         * Check if date range object is valid
         *
         * @param {Object} value
         * @return {Boolean}
         */
        isDateRangeValid: function(value)
        {
            return value.from && value.to && typeof value.from.getDate == 'function' && typeof value.to.getDate == 'function' ;
        },

        /**
         * Validate raw value
         *
         * @return {Boolean}/{String}
         */
        validator: function(value)
        {
            if (!this.isRawDateRangeValid(value))
            {
                return "Daty są w nieprawidłowym formacie!";
            }

            return true;
        },

        /**
         * Convert declared output format
         *
         * @param {Object} value Must be {from:Date, to:Date}
         * @return {Object}
         */
        getUnifiedReturnValue: function(value)
        {
            if (this.returnFormat == 'object')
            {
                return value;
            }

            var format = (this.returnFormat == 'string') ? this.format : this.returnFormat;

            return {
                from: Ext.Date.format(value.from, format),
                  to: Ext.Date.format(value.to, format)
            };
        },

        /**
         * Submit output
         *
         * @return {Object}
         */
        getSubmitValue: function()
        {
            return this.getUnifiedReturnValue(this.value);
        },

        /**
         * Ready to use date ranges
         *
         * @retutn {Object}
         */
        createBoilerplates: function()
        {
            if (this.boilerplates)
            {
                return this.boilerplates;
            }

            var now = new Date();

            var dayBack = new Date();
            dayBack.setDate(dayBack.getDate()-1);

            var weekBack = new Date();
            weekBack.setDate(weekBack.getDate()-7);

            var twoweekBack = new Date();
            twoweekBack.setDate(twoweekBack.getDate()-14);

            var prevMonth = new Date();
            prevMonth.setMonth(now.getMonth() - 1);

            var yearBack = new Date();
            yearBack.setDate(1);
            yearBack.setMonth(0);

            this.boilerplates = {
                yesterday: {to: now, from: dayBack},
                week:      {to: now, from: weekBack},
                prevweek:  {to: weekBack, from: twoweekBack},
                month:     {to: now, from: Ext.Date.getFirstDateOfMonth(now)},
                prevmonth: {
                    to: Ext.Date.getFirstDateOfMonth(prevMonth),
                    from: Ext.Date.getLastDateOfMonth(prevMonth)
                },
                year:      {to: now, from: yearBack}
            }

            return this.boilerplates;
        },

        /**
         * Event: Selected boilerplate
         *
         * @param Ext.form.field.ComboBox combo
         * @param {String} name
         */
        onBoilerplateSelect: function(combo, name)
        {
            var bp = this.createBoilerplates();
            this.value = bp[name];
            this.setValue(this.value);
            this.collapse();
        }
    });

})();
<|endoftext|>