<commit_before>var scaleHandler;

var extensionName = 'Rouming.cz improve UI';

if(/(rouming|maso)Show\.php/.test(location.href)) {
	var olderButton = document.querySelector('.roumingButton a[title="Starší obrázek"],.masoButton a[title="Starší obrázek"]');
	var targetA = document.querySelector('td[height="600"] a');
	targetA.href = olderButton.href;
	var targetImg = document.querySelector('td[height="600"] a img');
	scaleToScreen(targetA, targetImg);

	var head = document.head;
	var prefetch = document.createElement('link');
	prefetch.rel = 'prefetch';
	prefetch.href = olderButton.href;
	head.appendChild(prefetch);
}

if(/(rouming|maso)GIF\.php/.test(location.href)) {
	var olderButton = document.querySelector('.roumingButton a[title="Starší GIF"]');
	var targetA = document.querySelector('td.roumingForumMessage[align="center"] a,td.masoForumMessage[align="center"] a');
	targetA.href = olderButton.href;
}

function makeRealLinks() {
	var us = document.querySelectorAll('.roumingForumMessage u');
	for(var i=0; i < us.length; i++) {
		var u = us.item(i);
		if(u.childNodes.length > 1) {
			console.log('['+extensionName+'] Pseudolink contains more elements, unable to make real link.');
			continue;
		}
		var link = u.textContent;
		if(!link.match(/^\s*https?:\/\//)) {
			console.log('['+extensionName+'] Pseudolink doesn\'t contains URL, unable to make real link.');
			continue;
		}
		var a = document.createElement('a');
		a.href=link;
		a.appendChild(document.createTextNode(link));
		u.parentNode.replaceChild(a, u);
	}
}
makeRealLinks();

function scaleToScreen(parent, img) {
	toggleScale(img);
	parent.classList.add('scaledRouming');

	var floatPanel = document.createElement('div');
	floatPanel.className = 'floatPanel';
	parent.appendChild(floatPanel);

	var expandIcon = document.createElement('img');
	expandIcon.src = chrome.extension.getURL('/expand.svg');
	expandIcon.width = 24;
	expandIcon.title = 'Zobrazit původní velikost obrázku';
	floatPanel.appendChild(expandIcon);

	scaleHandler = function(e){
		toggleScale(img);
		e.preventDefault();
	};
	expandIcon.addEventListener('click', scaleHandler, false);
}

function toggleScale(img) {
	if(img.classList.contains('scaled')) {
		img.className = img.classList.remove('scaled');
		img.style.maxHeight = img.naturalHeight + 'px';
		img.style.maxWidth = img.naturalWidth + 'px';
	}
	else {
		img.classList.add('scaled');
		img.style.maxHeight = 'calc(100vh - ' + img.y + 'px)';
		img.style.maxWidth = 'calc(100vw - ' + img.x + 'px)';
	}
}

function arrowHandler(event) {
	var button;
	if(event.altKey || event.ctrlKey || event.metaKey || event.shiftKey) {
		return;
	}
	if(event.keyCode == 39) { // right arrow key
		button = document.querySelector(
			'.roumingButton a[title="Starší obrázek"],'
			 + '.masoButton a[title="Starší obrázek"],'
			 + '.roumingButton a[title="Následující video"],'
			 + '.roumingButton a[title="Starší GIF"]');
	}
	else if(event.keyCode == 37) { // left arrow key
		button = document.querySelector('.roumingButton a[title="Novější obrázek"],'
			 + '.masoButton a[title="Novější obrázek"],'
			 + '.roumingButton a[title="Předchozí video"],'
			 + '.roumingButton a[title="Novější GIF"]');
	}
	else if(event.keyCode == 76) { // L key
		button = document.querySelector('.roumingButton a[title="Tento obrázek se mi líbí"],'
			 + '.masoButton a[title="Tento obrázek se mi líbí"],'
			 + '.roumingForumTitle a[title="Tento GIF je super!"],'
			 + '.roumingForumTitle a[title="Toto video je super!"],'
			 + '.masoForumTitle a[title="Tento GIF je super!"]');
	}
	else if(event.keyCode == 80 && scaleHandler) { // P key
		scaleHandler(event);
	}
	if(button) {
		button.classList.add('activated');
		window.setTimeout(function(){button.classList.remove('activated')}, 300);
		button.click();
		event.preventDefault();
	}
}

window.addEventListener('keydown', arrowHandler, true);
<commit_msg>Add J and K keys as arrow keys to moving between items. Closing #3<commit_after>var scaleHandler;

var extensionName = 'Rouming.cz improve UI';

if(/(rouming|maso)Show\.php/.test(location.href)) {
	var olderButton = document.querySelector('.roumingButton a[title="Starší obrázek"],.masoButton a[title="Starší obrázek"]');
	var targetA = document.querySelector('td[height="600"] a');
	targetA.href = olderButton.href;
	var targetImg = document.querySelector('td[height="600"] a img');
	scaleToScreen(targetA, targetImg);

	var head = document.head;
	var prefetch = document.createElement('link');
	prefetch.rel = 'prefetch';
	prefetch.href = olderButton.href;
	head.appendChild(prefetch);
}

if(/(rouming|maso)GIF\.php/.test(location.href)) {
	var olderButton = document.querySelector('.roumingButton a[title="Starší GIF"]');
	var targetA = document.querySelector('td.roumingForumMessage[align="center"] a,td.masoForumMessage[align="center"] a');
	targetA.href = olderButton.href;
}

function makeRealLinks() {
	var us = document.querySelectorAll('.roumingForumMessage u');
	for(var i=0; i < us.length; i++) {
		var u = us.item(i);
		if(u.childNodes.length > 1) {
			console.log('['+extensionName+'] Pseudolink contains more elements, unable to make real link.');
			continue;
		}
		var link = u.textContent;
		if(!link.match(/^\s*https?:\/\//)) {
			console.log('['+extensionName+'] Pseudolink doesn\'t contains URL, unable to make real link.');
			continue;
		}
		var a = document.createElement('a');
		a.href=link;
		a.appendChild(document.createTextNode(link));
		u.parentNode.replaceChild(a, u);
	}
}
makeRealLinks();

function scaleToScreen(parent, img) {
	toggleScale(img);
	parent.classList.add('scaledRouming');

	var floatPanel = document.createElement('div');
	floatPanel.className = 'floatPanel';
	parent.appendChild(floatPanel);

	var expandIcon = document.createElement('img');
	expandIcon.src = chrome.extension.getURL('/expand.svg');
	expandIcon.width = 24;
	expandIcon.title = 'Zobrazit původní velikost obrázku';
	floatPanel.appendChild(expandIcon);

	scaleHandler = function(e){
		toggleScale(img);
		e.preventDefault();
	};
	expandIcon.addEventListener('click', scaleHandler, false);
}

function toggleScale(img) {
	if(img.classList.contains('scaled')) {
		img.className = img.classList.remove('scaled');
		img.style.maxHeight = img.naturalHeight + 'px';
		img.style.maxWidth = img.naturalWidth + 'px';
	}
	else {
		img.classList.add('scaled');
		img.style.maxHeight = 'calc(100vh - ' + img.y + 'px)';
		img.style.maxWidth = 'calc(100vw - ' + img.x + 'px)';
	}
}

function arrowHandler(event) {
	var button;
	if(event.altKey || event.ctrlKey || event.metaKey || event.shiftKey) {
		return;
	}
	if(event.keyCode == 39 || event.keyCode == 74) { // right arrow key or J key
		button = document.querySelector(
			'.roumingButton a[title="Starší obrázek"],'
			 + '.masoButton a[title="Starší obrázek"],'
			 + '.roumingButton a[title="Následující video"],'
			 + '.roumingButton a[title="Starší GIF"]');
	}
	else if(event.keyCode == 37 || event.keyCode == 75) { // left arrow key or K key
		button = document.querySelector('.roumingButton a[title="Novější obrázek"],'
			 + '.masoButton a[title="Novější obrázek"],'
			 + '.roumingButton a[title="Předchozí video"],'
			 + '.roumingButton a[title="Novější GIF"]');
	}
	else if(event.keyCode == 76) { // L key
		button = document.querySelector('.roumingButton a[title="Tento obrázek se mi líbí"],'
			 + '.masoButton a[title="Tento obrázek se mi líbí"],'
			 + '.roumingForumTitle a[title="Tento GIF je super!"],'
			 + '.roumingForumTitle a[title="Toto video je super!"],'
			 + '.masoForumTitle a[title="Tento GIF je super!"]');
	}
	else if(event.keyCode == 80 && scaleHandler) { // P key
		scaleHandler(event);
	}
	if(button) {
		button.classList.add('activated');
		window.setTimeout(function(){button.classList.remove('activated')}, 300);
		button.click();
		event.preventDefault();
	}
}

window.addEventListener('keydown', arrowHandler, true);
<|endoftext|>


<commit_before>var express = require('express');
var router = express.Router();
var blog = require('../models/blog');

/* Route: / (fetch all blog posts)
Request:

Response:
200: {blogposts},
404: no posts/ fetching posts failed
*/
router.get('/', function(req, res) {
  blog.getAllPosts(function(result) {
    res.render('blog', {
      posts: result
    });
  });
});

/* Route: /(slug)
Request:
  slug: slug-string
Response:
200: {blogpost},
404: no post/ fetching post failed
*/
router.get('/:slug', function(req, res) {
  blog.getPost(req.params.slug, function(result) {
    res.render('singleblog', {
      post: result[0]
    });
  });
});

module.exports = router;
<commit_msg>added blogpost route and view<commit_after>var express = require('express');
var router = express.Router();
var blog = require('../models/blog');

/* Route: / (fetch all blog posts)
Request:

Response:
200: {blogposts},
404: no posts/ fetching posts failed
*/
router.get('/', function(req, res) {
  blog.getAllPosts(function(result) {
    res.render('blog', {
      posts: result
    });
  });
});

/* Route: /(slug)
Request:
  slug: slug-string
Response:
200: {blogpost},
404: no post/ fetching post failed
*/
router.get('/:slug', function(req, res) {
  blog.getPost(req.params.slug, function(result) {
    res.render('blogpost', {
      post: result[0]
    });
  });
});

module.exports = router;
<|endoftext|>


<commit_before>/**
 * Example of how to create and publish an asset for sale.
 *
 * @author Manu Sporny
 *
 * BSD 3-Clause License
 * Copyright (c) 2011-2012 Digital Bazaar, Inc.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * Redistributions of source code must retain the above copyright notice,
 * this list of conditions and the following disclaimer.
 *
 * Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution.
 *
 * Neither the name of the Digital Bazaar, Inc. nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
 * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
var async = require('async');
var program = require('commander');
var config = require('./config.js');
var payswarm = require('../lib/payswarm-client.js');
var path = require('path');
var fs = require('fs');
var querystring = require('querystring');
var request = require('request');

var assetRegistration = {};

assetRegistration.run = function() {
  var assetId = new Date().getTime().toString(16);
  program
    .version('1.0.0')
    // setup the command line options
    .option('--config <configfile>',
      'The configuration containing public/private keys (default: payswarm.cfg).')
    .option('--asset-name <title>',
      'The asset name (default: \'Test Asset ' + assetId + '\').')
    .option('--price <dollars>',
      'The price (in dollars) of the asset (default: 0.05).')
    .option('--listing-service <listing_url>',
      'The base URL for the listing service (default: http://listings.dev.payswarm.com/)')
    .option('--verbose',
      'Print more detailed program status updates (default: false).')
    .parse(process.argv);

  // initialize settings
  var cfgFile = program.config || 'payswarm.cfg';
  var cfg = {};
  var assetName = program.assetName || 'Test Asset ' + assetId;
  var price = program.price || '0.05';
  var listingService = program.listingService || 'http://listings.dev.payswarm.com/';
  var verbose = program.verbose || false;

  /*
   * To publish an asset for sale, the following steps must be performed:
   *
   * 1. Create and digitally sign the asset.
   * 2. Create and digitally sign the listing.
   * 3. Publish the asset and listing data to the Web.
   */
  async.waterfall([
    function(callback) {
      // read the config file from disk
      config.readConfigFile(cfgFile, callback);
    },
    function(newCfg, callback) {
      cfg = newCfg;
      // generate the asset
      console.log("Generating asset...");
      var assetUrl = listingService + 'payswarm.js/' + assetId;
      var asset = {
        id: assetUrl + '#asset',
        type: ['ps:Asset', 'pto:WebPage'],
        creator: {
          fullName: 'publish-asset-for-sale.js Example'
        },
        title: assetName,
        assetContent: assetUrl,
        assetProvider: cfg.publicKey.owner,
      };

      // sign the asset
      payswarm.sign(asset, {
        publicKeyId: cfg.publicKey.id,
        privateKeyPem: cfg.publicKey.privateKeyPem
      }, callback);
    },
    function(signedAsset, callback) {
      // generate a hash for the signed asset
      payswarm.hash(signedAsset, function(err, assetHash) {
        callback(err, signedAsset, assetHash);
      });
    },
    function(signedAsset, assetHash, callback) {
      // generate the listing validity dates
      var validFrom = new Date();
      var validUntil = new Date();
      validUntil.setFullYear(validFrom.getFullYear() + 1);

      // generate the listing
      console.log("Generating listing...");
      var listingUrl = listingService + 'payswarm.js/' + assetId;

      var listing = {
        id: listingUrl + '#listing',
        type: ['ps:Listing', 'gr:Offering'],
        payee: [{
          id: listingUrl + '#listing-payee-1',
          type: 'com:Payee',
          destination: 'https://payswarm.dev:19443/i/vendor/accounts/primary',
          payeePosition: 0,
          payeeRate: price,
          payeeRateType: 'com:FlatAmount',
          comment: 'Payment for Test Asset ' + assetId + '.'
        }],
        payeeRule : [{
          type: 'com:PayeeRule',
          accountOwnerType: 'ps:Authority',
          maximumPayeeRate: '10.0000000',
          payeeRateContext: ['com:Inclusive', 'com:Tax', 'com:TaxExempt'],
          payeeRateType: 'com:Percentage'
        }],
        asset: assetId,
        assetHash: assetHash,
        license: 'http://purl.org/payswarm/licenses/blogging',
        licenseHash: 'ad8f72fcb47e867231d957c0bffb4c02d275926a',
        validFrom: validFrom,
        validUntil: validUntil,
      };

      // sign the listing
      payswarm.sign(listing, {
        publicKeyId: cfg.publicKey.id,
        privateKeyPem: cfg.publicKey.privateKeyPem
      }, function(err, signedListing) {
        callback(err, signedAsset, signedListing);
      });
    },
    function(signedAsset, signedListing, callback) {
      // register the signed listing
      var assetAndListing = {
        '@context': 'http://purl.org/payswarm/v1',
        '@graph': [signedAsset, signedListing]
      };

      request.post({
        headers: {'content-type': 'application/ld+json'},
        url: signedListing.id.split('#')[0],
        body: JSON.stringify(assetAndListing, null, 2)
      }, function(err, response, body) {
        if(!err && response.statusCode >= 400) {
          err = JSON.stringify(body, null, 2);
        }
        if(err) {
          console.log('Failed to register signed asset and listing: ',
            err.toString());
          return callback(err);
        }

        if(verbose) {
          console.log('Registered signed asset and listing: ' +
            JSON.stringify(assetAndListing, null, 2));
        }
        else {
          console.log('Registered signed asset:\n   ', signedAsset.id);
          console.log('Registered signed listing:\n   ', signedListing.id);
        }
        callback(null);
      });
    }
  ], function(err) {
    if(err) {
      console.log('Failed to register signed asset and listing:',
        err.toString());
    }
  });
};

// log uncaught exception and exit
process.on('uncaughtException', function(err) {
  console.log(err.toString(), err.stack ? {stack: err.stack} : null);
  process.removeAllListeners('uncaughtException');
  process.exit();
});

// run the program
assetRegistration.run();
<commit_msg>Fixed purchasing bugs, migrated to new config object, better error reporting.<commit_after>/**
 * Example of how to create and publish an asset for sale.
 *
 * @author Manu Sporny
 *
 * BSD 3-Clause License
 * Copyright (c) 2011-2012 Digital Bazaar, Inc.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * Redistributions of source code must retain the above copyright notice,
 * this list of conditions and the following disclaimer.
 *
 * Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution.
 *
 * Neither the name of the Digital Bazaar, Inc. nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
 * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
var async = require('async');
var program = require('commander');
var config = require('./config.js');
var payswarm = require('../lib/payswarm-client.js');
var path = require('path');
var fs = require('fs');
var querystring = require('querystring');
var request = require('request');

var assetRegistration = {};

assetRegistration.run = function() {
  var assetId = new Date().getTime().toString(16);
  program
    .version('1.0.0')
    // setup the command line options
    .option('--config <configfile>',
      'The configuration containing public/private keys (default: payswarm.cfg).')
    .option('--asset-name <title>',
      'The asset name (default: \'Test Asset ' + assetId + '\').')
    .option('--price <dollars>',
      'The price (in dollars) of the asset (default: 0.05).')
    .option('--listing-service <listing_url>',
      'The base URL for the listing service (default: http://listings.dev.payswarm.com/)')
    .option('--verbose',
      'Print more detailed program status updates (default: false).')
    .parse(process.argv);

  // initialize settings
  var cfgFile = program.config || 'payswarm.cfg';
  var cfg = {};
  var assetName = program.assetName || 'Test Asset ' + assetId;
  var price = program.price || '0.05';
  var listingService = program.listingService || 'http://listings.dev.payswarm.com/';
  var verbose = program.verbose || false;

  /*
   * To publish an asset for sale, the following steps must be performed:
   *
   * 1. Create and digitally sign the asset.
   * 2. Create and digitally sign the listing.
   * 3. Publish the asset and listing data to the Web.
   */
  async.waterfall([
    function(callback) {
      // read the config file from disk
      config.readConfigFile(cfgFile, callback);
    },
    function(newCfg, callback) {
      cfg = newCfg;
      // generate the asset
      console.log("Generating asset...");
      var assetUrl = listingService + 'payswarm.js/' + assetId;
      var asset = {
        '@context': 'http://purl.org/payswarm/v1',
        id: assetUrl + '#asset',
        type: ['ps:Asset', 'pto:WebPage'],
        creator: {
          fullName: 'publish-asset-for-sale.js Example'
        },
        title: assetName,
        assetContent: assetUrl,
        assetProvider: cfg.owner,
      };

      // sign the asset
      payswarm.sign(asset, {
        publicKeyId: cfg.publicKey.id,
        privateKeyPem: cfg.publicKey.privateKeyPem
      }, callback);
    },
    function(signedAsset, callback) {
      // generate a hash for the signed asset
      payswarm.hash(signedAsset, function(err, assetHash) {
        callback(err, signedAsset, assetHash);
      });
    },
    function(signedAsset, assetHash, callback) {
      // generate the listing validity dates
      var validFrom = new Date();
      var validUntil = new Date();
      validUntil.setFullYear(validFrom.getFullYear() + 1);

      // generate the listing
      console.log("Generating listing...");
      var listingUrl = listingService + 'payswarm.js/' + assetId;

      var listing = {
        '@context': 'http://purl.org/payswarm/v1',
        id: listingUrl + '#listing',
        type: ['ps:Listing', 'gr:Offering'],
        payee: [{
          id: listingUrl + '#listing-payee-1',
          type: 'com:Payee',
          destination: 'https://payswarm.dev:19443/i/vendor/accounts/primary',
          payeePosition: 0,
          payeeRate: price,
          payeeRateType: 'com:FlatAmount',
          comment: 'Payment for Test Asset ' + assetId + '.'
        }],
        payeeRule : [{
          type: 'com:PayeeRule',
          accountOwnerType: 'ps:Authority',
          maximumPayeeRate: '10.0000000',
          payeeRateContext: ['com:Inclusive', 'com:Tax', 'com:TaxExempt'],
          payeeRateType: 'com:Percentage'
        }],
        asset: listingUrl + '#asset',
        assetHash: assetHash,
        license: 'http://purl.org/payswarm/licenses/blogging',
        licenseHash: 'ad8f72fcb47e867231d957c0bffb4c02d275926a',
        validFrom: payswarm.w3cDate(validFrom),
        validUntil: payswarm.w3cDate(validUntil),
      };

      // sign the listing
      payswarm.sign(listing, {
        publicKeyId: cfg.publicKey.id,
        privateKeyPem: cfg.publicKey.privateKeyPem
      }, function(err, signedListing) {
        callback(err, signedAsset, signedListing);
      });
    },
    function(signedAsset, signedListing, callback) {
      // register the signed listing
      var assetAndListing = {
        '@context': 'http://purl.org/payswarm/v1',
        '@graph': [signedAsset, signedListing]
      };

      request.post({
        headers: {'content-type': 'application/ld+json'},
        url: signedListing.id.split('#')[0],
        body: JSON.stringify(assetAndListing, null, 2)
      }, function(err, response, body) {
        if(!err && response.statusCode >= 400) {
          err = JSON.stringify(body, null, 2);
        }
        if(err) {
          console.log('Failed to register signed asset and listing: ',
            err.toString(), err);
          return callback(err);
        }

        if(verbose) {
          console.log('Registered signed asset and listing: ' +
            JSON.stringify(assetAndListing, null, 2));
        }
        else {
          console.log('Registered signed asset:\n   ', signedAsset.id);
          console.log('Registered signed listing:\n   ', signedListing.id);
        }
        callback(null);
      });
    }
  ], function(err) {
    if(err) {
      console.log('Failed to register signed asset and listing:',
        err.toString());
    }
  });
};

// log uncaught exception and exit
process.on('uncaughtException', function(err) {
  console.log(err.toString(), err.stack ? {stack: err.stack} : null);
  process.removeAllListeners('uncaughtException');
  process.exit();
});

// run the program
assetRegistration.run();
<|endoftext|>


<commit_before>'use strict'

const { Router } = require('express')
const { ServerError, ClientError, NotFoundError } = require('./errors')
const { identity, set, map, pick, difference } = require('lodash/fp')
const bodyParser = require('body-parser')
const es = require('./elasticsearch')
const { applyTemplates } = require('./model-utils')

const restHandler = exports.restHandler = fn => (req, res, next) => {
	Promise.resolve().then(() => fn(req, res))
	.then(data => res.send(data))
	.catch(err => {
		if (!err.status) {
			err.status = err.errors ? 400 : 500
		}
		next(err)
	})
}

const saveDocument = (format = identity) => doc => {
	return doc.save()
		.then(format)
		.catch(e => {
			let err = new ClientError({ title: 'Validation error' })
			if (e.name === 'ValidationError') {
				err.errors = Object.keys(e.errors).reduce(
					(errors, error) => set(error, e.errors[error].message, errors),
					{}
				)
			}
			return Promise.reject(err)
		})
}

// http.ServerRequest, mongoose.Model => Promise<{ editable }>
const defaultGetPermissions = (req, object) => Promise.resolve({ // eslint-disable-line no-unused-vars
	editable: true // TODO check permissions in req.session against object
})

const formatWithOpts = (req, format, getPermissions, applyTemplates) => o =>
	Promise.all([
		getPermissions(req, o),
		format(applyTemplates ? o.applyTemplates() : o)
	])
	.then(([ { editable }, o ]) => set('opts', { editable })(o))

exports.restRouter = (Model, format = identity, esIndex = null, getPermissions = defaultGetPermissions) => {
	const save = saveDocument(format)
	const router = Router()

	router.use(bodyParser.json())

	if (esIndex) {
		router.get('/search', restHandler(searchModel(esIndex, Model, format, getPermissions)))
	}

	router.get('/', restHandler(listModel(Model, format, getPermissions)))
	router.get('/:id([A-Za-f0-9]{24})', restHandler(getModel(Model, format, getPermissions)))
	router.get('/:ids([A-Za-f0-9,]+)/string', restHandler(getModelStrings(Model)))
	router.put('/:id([A-Za-f0-9]{24})', restHandler(updateModel(Model, save, getPermissions)))
	router.post('/', restHandler(createModel(Model, save)))
	router.delete('/:id([A-Za-f0-9]{24})', restHandler(deleteModel(Model, getPermissions)))

	return router
}

const listModel = (Model, format, getPermissions) => req => {
	// Always keep 'opts' technical field
	const selectFields = req.query.fields ? pick(req.query.fields.split(',').concat([ 'opts' ])) : identity
	const applyTemplates = Boolean(Number(req.query.applyTemplates))
	// Note: we don't apply field selection directly in query as some fields may be not asked, but
	// required for some other fields' templates to be correctly calculated
	return Model.find().then(peoples => Promise.all(peoples.map(people =>
		people.populateAll()
		.then(formatWithOpts(req, format, getPermissions, applyTemplates))
		.then(selectFields)
	)))
}

const getModel = (Model, format, getPermissions) => req =>
	Model.findById(req.params.id)
	.then(found => found || Promise.reject(NotFoundError({ title: Model.modelName })))
	.then(formatWithOpts(req, format, getPermissions, false))

const getModelStrings = Model => req => {
	const ids = req.params.ids.split(',')
	const invalids = ids.filter(id => !id.match(/^[A-Za-f0-9]{24}$/))
	if (invalids.length > 0) {
		return Promise.reject(ClientError({ title: `Invalid ObjectId: ${invalids.join(', ')}` }))
	}
	return Model.find({ _id: { $in: ids } })
		.then(founds => {
			const missing = difference(ids, map('id', founds))
			if (missing.length > 0) {
				return Promise.reject(NotFoundError({ title: `Model "${Model.modelName}" returned nothing for IDs ${missing.join(', ')}` }))
			}
			// Map over ids instead of found object to keep initial order
			return Promise.all(ids.map(id => founds.find(o => o.id === id).populateAll()))
		})
		.then(map(o => ({ id: String(o._id), value: o.applyTemplates(0) })))
}

const updateModel = (Model, save, getPermissions) => {
	const get = getModel(Model, identity, getPermissions)
	return req =>
		get(req)
		.then(doc => getPermissions(req, doc)
			.then(({ editable }) => {
				if (!editable) {
					return Promise.reject(ClientError({ message: 'Permission refused' }))
				}
				// Update object
				Object.keys(req.body).forEach(field => {
					doc[field] = req.body[field]
				})
				// Sign for EditLogs
				doc.latestChangeBy = 'TODO GET USERNAME FROM WEB SESSION (update)'
				return doc
			})
		)
		.then(doc => save(doc))
}

const createModel = (Model, save) => (req, res) => {
	let o
	try {
		o = new Model(req.body)
	} catch (e) {
		if (e.name === 'StrictModeError') {
			// Extra fields
			return Promise.reject(ClientError({ title: e.message }))
		} else {
			return Promise.reject(ServerError({ title: e.message }))
		}
	}
	o.latestChangeBy = 'TODO GET USERNAME FROM WEB SESSION (create)'
	return save(o).then(saved => {
		res.status(201)
		return saved
	})
}

const deleteModel = (Model, getPermissions) => (req, res) =>
	Model.findById(req.params.id)
	.then(found => found || Promise.reject(NotFoundError({ title: Model.modelName })))
	.then(doc => getPermissions(req, doc)
		.then(({ editable }) => editable ? doc : Promise.reject(ClientError({ message: 'Permission refused' })))
	)
	.then(doc => {
		doc.latestChangeBy = 'TODO GET USERNAME FROM WEB SESSION (delete)'
		return doc.remove()
	})
	.then(() => {
		res.status(204)
		return null
	})

const searchModel = (esIndex, Model, format) => req => {
	const query = req.query.q || '*'
	const fields = req.query.fields ? req.query.fields.split(',') : undefined
	const full = Boolean(Number(req.query.full))
	const fuzzy = !Number(req.query.raw)

	return (fuzzy
			? es.q.forSuggestions(esIndex, { query, fields })
			: es.q(esIndex, { query_string: { query, fields } })
		)
		.then(map(o => full
			? format(o)
			: { value: o._id, label: applyTemplates(o, Model.modelName, 0) }
		))
}
<commit_msg>fix(edit-logs): grabs latestChangeBy value from req.session (refs #3 #20)<commit_after>'use strict'

const { Router } = require('express')
const { ServerError, ClientError, NotFoundError } = require('./errors')
const { identity, set, map, pick, difference } = require('lodash/fp')
const bodyParser = require('body-parser')
const es = require('./elasticsearch')
const { applyTemplates } = require('./model-utils')

const restHandler = exports.restHandler = fn => (req, res, next) => {
	Promise.resolve().then(() => fn(req, res))
	.then(data => res.send(data))
	.catch(err => {
		if (!err.status) {
			err.status = err.errors ? 400 : 500
		}
		next(err)
	})
}

const saveDocument = (format = identity) => doc => {
	return doc.save()
		.then(format)
		.catch(e => {
			let err = new ClientError({ title: 'Validation error' })
			if (e.name === 'ValidationError') {
				err.errors = Object.keys(e.errors).reduce(
					(errors, error) => set(error, e.errors[error].message, errors),
					{}
				)
			}
			return Promise.reject(err)
		})
}

// http.ServerRequest, mongoose.Model => Promise<{ editable }>
const defaultGetPermissions = (req, object) => Promise.resolve({ // eslint-disable-line no-unused-vars
	editable: true // TODO check permissions in req.session against object
})

const formatWithOpts = (req, format, getPermissions, applyTemplates) => o =>
	Promise.all([
		getPermissions(req, o),
		format(applyTemplates ? o.applyTemplates() : o)
	])
	.then(([ { editable }, o ]) => set('opts', { editable })(o))

exports.restRouter = (Model, format = identity, esIndex = null, getPermissions = defaultGetPermissions) => {
	const save = saveDocument(format)
	const router = Router()

	router.use(bodyParser.json())

	if (esIndex) {
		router.get('/search', restHandler(searchModel(esIndex, Model, format, getPermissions)))
	}

	router.get('/', restHandler(listModel(Model, format, getPermissions)))
	router.get('/:id([A-Za-f0-9]{24})', restHandler(getModel(Model, format, getPermissions)))
	router.get('/:ids([A-Za-f0-9,]+)/string', restHandler(getModelStrings(Model)))
	router.put('/:id([A-Za-f0-9]{24})', restHandler(updateModel(Model, save, getPermissions)))
	router.post('/', restHandler(createModel(Model, save)))
	router.delete('/:id([A-Za-f0-9]{24})', restHandler(deleteModel(Model, getPermissions)))

	return router
}

const listModel = (Model, format, getPermissions) => req => {
	// Always keep 'opts' technical field
	const selectFields = req.query.fields ? pick(req.query.fields.split(',').concat([ 'opts' ])) : identity
	const applyTemplates = Boolean(Number(req.query.applyTemplates))
	// Note: we don't apply field selection directly in query as some fields may be not asked, but
	// required for some other fields' templates to be correctly calculated
	return Model.find().then(peoples => Promise.all(peoples.map(people =>
		people.populateAll()
		.then(formatWithOpts(req, format, getPermissions, applyTemplates))
		.then(selectFields)
	)))
}

const getModel = (Model, format, getPermissions) => req =>
	Model.findById(req.params.id)
	.then(found => found || Promise.reject(NotFoundError({ title: Model.modelName })))
	.then(formatWithOpts(req, format, getPermissions, false))

const getModelStrings = Model => req => {
	const ids = req.params.ids.split(',')
	const invalids = ids.filter(id => !id.match(/^[A-Za-f0-9]{24}$/))
	if (invalids.length > 0) {
		return Promise.reject(ClientError({ title: `Invalid ObjectId: ${invalids.join(', ')}` }))
	}
	return Model.find({ _id: { $in: ids } })
		.then(founds => {
			const missing = difference(ids, map('id', founds))
			if (missing.length > 0) {
				return Promise.reject(NotFoundError({ title: `Model "${Model.modelName}" returned nothing for IDs ${missing.join(', ')}` }))
			}
			// Map over ids instead of found object to keep initial order
			return Promise.all(ids.map(id => founds.find(o => o.id === id).populateAll()))
		})
		.then(map(o => ({ id: String(o._id), value: o.applyTemplates(0) })))
}

const updateModel = (Model, save, getPermissions) => {
	const get = getModel(Model, identity, getPermissions)
	return req =>
		get(req)
		.then(doc => getPermissions(req, doc)
			.then(({ editable }) => {
				if (!editable) {
					return Promise.reject(ClientError({ message: 'Permission refused' }))
				}
				// Update object
				Object.keys(req.body).forEach(field => {
					doc[field] = req.body[field]
				})
				// Sign for EditLogs
				doc.latestChangeBy = req.session.login
				return doc
			})
		)
		.then(doc => save(doc))
}

const createModel = (Model, save) => (req, res) => {
	let o
	try {
		o = new Model(req.body)
	} catch (e) {
		if (e.name === 'StrictModeError') {
			// Extra fields
			return Promise.reject(ClientError({ title: e.message }))
		} else {
			return Promise.reject(ServerError({ title: e.message }))
		}
	}
	o.latestChangeBy = req.session.login
	return save(o).then(saved => {
		res.status(201)
		return saved
	})
}

const deleteModel = (Model, getPermissions) => (req, res) =>
	Model.findById(req.params.id)
	.then(found => found || Promise.reject(NotFoundError({ title: Model.modelName })))
	.then(doc => getPermissions(req, doc)
		.then(({ editable }) => editable ? doc : Promise.reject(ClientError({ message: 'Permission refused' })))
	)
	.then(doc => {
		doc.latestChangeBy = req.session.login
		return doc.remove()
	})
	.then(() => {
		res.status(204)
		return null
	})

const searchModel = (esIndex, Model, format) => req => {
	const query = req.query.q || '*'
	const fields = req.query.fields ? req.query.fields.split(',') : undefined
	const full = Boolean(Number(req.query.full))
	const fuzzy = !Number(req.query.raw)

	return (fuzzy
			? es.q.forSuggestions(esIndex, { query, fields })
			: es.q(esIndex, { query_string: { query, fields } })
		)
		.then(map(o => full
			? format(o)
			: { value: o._id, label: applyTemplates(o, Model.modelName, 0) }
		))
}
<|endoftext|>


<commit_before>/**
 * @file BriefPagination component
 * @author liangxiaojun(liangxiaojun@derbysoft.com)
 */

import React, {Component} from 'react';
import PropTypes from 'prop-types';

import PaginationSize from '../_PaginationSize';
import IconButton from '../IconButton';

import Valid from '../_vendors/Valid';
import TableCalculation from '../_vendors/TableCalculation';
import ComponentUtil from '../_vendors/ComponentUtil';

class BriefPagination extends Component {

    constructor(props, ...restArgs) {

        super(props, ...restArgs);

        this.state = {
            page: props.page,
            pageSize: props.pageSize
        };

    }

    handlePageChange = page => {
        this.setState({
            page
        }, () => {
            const {onPageChange} = this.props;
            onPageChange && onPageChange(page);
        });
    };

    handlePageSizeChange = pageSize => {

        const {pageSizeValueField} = this.props,
            originPageSizeValue = TableCalculation.getPageSizeValue(this.state.pageSize, pageSizeValueField),
            pageSizeValue = TableCalculation.getPageSizeValue(pageSize, pageSizeValueField);

        if (pageSizeValue && originPageSizeValue !== pageSizeValue) {

            const {onPageSizeChange} = this.props;
            onPageSizeChange && onPageSizeChange(pageSize);

            const totalPage = Math.ceil(this.props.total / pageSizeValue),
                page = Valid.range(this.state.page, 0, totalPage - 1);
            if (page !== this.state.page) {
                const {onPageChange} = this.props;
                onPageChange && onPageChange(page);
            }

        }

    };

    static getDerivedStateFromProps(props, state) {
        return {
            prevProps: props,
            page: ComponentUtil.getDerivedState(props, state, 'page'),
            pageSize: ComponentUtil.getDerivedState(props, state, 'pageSize')
        };
    }

    render() {

        const {
                total, page, pageSize, pageSizes, pageSizeRightIconCls, pageSizeValueField, pageSizeDisplayField,
                totalVisible, selectedCount, selectionVisible, pageSizeVisible, prevIconCls, nextIconCls,
                selectionRenderer, totalRenderer, parentEl
            } = this.props,

            totalPage = Math.ceil(total / pageSize),

            startNumber = Valid.range(page * pageSize + 1, 0, total),
            stopNumber = Valid.range((page + 1) * pageSize, 0, total);

        return (

                    {
                        selectionVisible ?

                                {
                                    selectionRenderer ?
                                        selectionRenderer(selectedCount, total, page, totalPage, pageSize, pageSizes)
                                        :
                                        `Selected: ${selectedCount}`
                                }

                            :
                            null
                    }

                    {
                        totalVisible ?

                                {
                                    totalRenderer ?
                                        totalRenderer(total, page, totalPage, pageSize, pageSizes)
                                        :
                                        `Total: ${total}`
                                }

                            :
                            null
                    }

                    {
                        pageSizeVisible ?
                            <PaginationSize pageSize={pageSize}
                                            pageSizes={pageSizes}
                                            valueField={pageSizeValueField}
                                            displayField={pageSizeDisplayField}
                                            parentEl={parentEl}
                                            rightIconCls={pageSizeRightIconCls}
                                            onPageSizeChange={this.handlePageSizeChange}/>
                            :
                            null
                    }

                        {`${startNumber}-${stopNumber} of ${total}`}

                    <IconButton iconCls={prevIconCls}
                                disabled={page <= 0}
                                onClick={() => this.handlePageChange(page - 1)}/>

                    <IconButton iconCls={nextIconCls}
                                disabled={page >= totalPage - 1}
                                onClick={() => this.handlePageChange(page + 1)}/>

        );

    }
}

BriefPagination.propTypes = {

    /**
     * The CSS class name of the root element.
     */
    className: PropTypes.string,

    /**
     * Override the styles of the root element.
     */
    style: PropTypes.object,

    /**
     * The total of data.
     */
    total: PropTypes.number,

    /**
     * The valid page.
     */
    page: PropTypes.number,

    /**
     * The number of per page.
     */
    pageSize: PropTypes.number,

    /**
     * The array of pageSize.
     */
    pageSizes: PropTypes.array,

    /**
     * The value field name of PageSize. (default: "value")
     */
    pageSizeValueField: PropTypes.string,

    /**
     * The display field name of PageSize. (default: "text")
     */
    pageSizeDisplayField: PropTypes.string,

    /**
     * The total count of selected.
     */
    selectedCount: PropTypes.number,

    /**
     * If true, the selectedCount will show.
     */
    selectionVisible: PropTypes.bool,

    /**
     * If true, the total will show.
     */
    totalVisible: PropTypes.bool,

    /**
     * If false, the pageSize choice box will not show.
     */
    pageSizeVisible: PropTypes.bool,

    /**
     * Use this property to set page size right icon.
     */
    pageSizeRightIconCls: PropTypes.string,

    /**
     * Use this property to set prev button icon.
     */
    prevIconCls: PropTypes.string,

    /**
     * Use this property to set next button icon.
     */
    nextIconCls: PropTypes.string,

    selectionRenderer: PropTypes.func,
    totalRenderer: PropTypes.func,

    parentEl: PropTypes.object,

    onPageChange: PropTypes.func,
    onPageSizeChange: PropTypes.func

};

BriefPagination.defaultProps = {

    total: 0,
    page: 0,
    pageSize: 10,
    pageSizes: [5, 10, 15, 20],
    pageSizeValueField: 'value',
    pageSizeDisplayField: 'text',
    selectedCount: 0,

    selectionVisible: false,
    totalVisible: true,
    pageSizeVisible: true,

    pageSizeRightIconCls: 'fas fa-angle-down',
    prevIconCls: 'fas fa-angle-left',
    nextIconCls: 'fas fa-angle-right'

};

export default BriefPagination;
<commit_msg>update BriefPagination<commit_after>/**
 * @file BriefPagination component
 * @author liangxiaojun(liangxiaojun@derbysoft.com)
 */

import React, {Component} from 'react';
import PropTypes from 'prop-types';

import PaginationSize from '../_PaginationSize';
import IconButton from '../IconButton';

import Valid from '../_vendors/Valid';
import TableCalculation from '../_vendors/TableCalculation';
import ComponentUtil from '../_vendors/ComponentUtil';

class BriefPagination extends Component {

    constructor(props, ...restArgs) {

        super(props, ...restArgs);

        this.state = {
            page: props.page,
            pageSize: props.pageSize
        };

    }

    handlePageChange = page => {
        this.setState({
            page
        }, () => {
            const {onPageChange} = this.props;
            onPageChange && onPageChange(page);
        });
    };

    handlePageSizeChange = pageSize => {

        const {pageSizeValueField} = this.props,
            originPageSizeValue = TableCalculation.getPageSizeValue(this.state.pageSize, pageSizeValueField),
            pageSizeValue = TableCalculation.getPageSizeValue(pageSize, pageSizeValueField);

        if (pageSizeValue && originPageSizeValue !== pageSizeValue) {

            const {onPageSizeChange} = this.props;
            onPageSizeChange && onPageSizeChange(pageSize);

            const totalPage = Math.ceil(this.props.total / pageSizeValue),
                page = Valid.range(this.state.page, 0, totalPage - 1);
            if (page !== this.state.page) {
                const {onPageChange} = this.props;
                onPageChange && onPageChange(page);
            }

        }

    };

    static getDerivedStateFromProps(props, state) {
        return {
            prevProps: props,
            page: ComponentUtil.getDerivedState(props, state, 'page'),
            pageSize: ComponentUtil.getDerivedState(props, state, 'pageSize')
        };
    }

    render() {

        const {
                total, page, pageSize, pageSizes, pageSizeRightIconCls, pageSizeValueField, pageSizeDisplayField,
                totalVisible, selectedCount, selectionVisible, pageSizeVisible, prevIconCls, nextIconCls,
                selectionRenderer, totalRenderer, parentEl
            } = this.props,

            totalPage = Math.ceil(total / pageSize),

            startNumber = Valid.range(page * pageSize + 1, 0, total),
            stopNumber = Valid.range((page + 1) * pageSize, 0, total);

        return (

                    {
                        selectionVisible ?

                                {
                                    selectionRenderer ?
                                        selectionRenderer(selectedCount, total, page, totalPage, pageSize, pageSizes)
                                        :
                                        `Selected: ${selectedCount}`
                                }

                            :
                            null
                    }

                    {
                        totalVisible ?

                                {
                                    totalRenderer ?
                                        totalRenderer(total, page, totalPage, pageSize, pageSizes)
                                        :
                                        `Total: ${total}`
                                }

                            :
                            null
                    }

                    {
                        pageSizesVisible ?
                            <PaginationSize pageSize={pageSize}
                                            pageSizes={pageSizes}
                                            valueField={pageSizeValueField}
                                            displayField={pageSizeDisplayField}
                                            parentEl={parentEl}
                                            rightIconCls={pageSizeRightIconCls}
                                            onPageSizeChange={this.handlePageSizeChange}/>
                            :
                            null
                    }

                        {`${startNumber}-${stopNumber} of ${total}`}

                    <IconButton iconCls={prevIconCls}
                                disabled={page <= 0}
                                onClick={() => this.handlePageChange(page - 1)}/>

                    <IconButton iconCls={nextIconCls}
                                disabled={page >= totalPage - 1}
                                onClick={() => this.handlePageChange(page + 1)}/>

        );

    }
}

BriefPagination.propTypes = {

    /**
     * The CSS class name of the root element.
     */
    className: PropTypes.string,

    /**
     * Override the styles of the root element.
     */
    style: PropTypes.object,

    /**
     * The total of data.
     */
    total: PropTypes.number,

    /**
     * The valid page.
     */
    page: PropTypes.number,

    /**
     * The number of per page.
     */
    pageSize: PropTypes.number,

    /**
     * The array of pageSize.
     */
    pageSizes: PropTypes.array,

    /**
     * The value field name of PageSize. (default: "value")
     */
    pageSizeValueField: PropTypes.string,

    /**
     * The display field name of PageSize. (default: "text")
     */
    pageSizeDisplayField: PropTypes.string,

    /**
     * The total count of selected.
     */
    selectedCount: PropTypes.number,

    /**
     * If true, the selectedCount will show.
     */
    selectionVisible: PropTypes.bool,

    /**
     * If true, the total will show.
     */
    totalVisible: PropTypes.bool,

    /**
     * If false, the pageSize choice box will not show.
     */
    pageSizeVisible: PropTypes.bool,

    /**
     * Use this property to set page size right icon.
     */
    pageSizeRightIconCls: PropTypes.string,

    /**
     * Use this property to set prev button icon.
     */
    prevIconCls: PropTypes.string,

    /**
     * Use this property to set next button icon.
     */
    nextIconCls: PropTypes.string,

    selectionRenderer: PropTypes.func,
    totalRenderer: PropTypes.func,

    parentEl: PropTypes.object,

    onPageChange: PropTypes.func,
    onPageSizeChange: PropTypes.func

};

BriefPagination.defaultProps = {

    total: 0,
    page: 0,
    pageSize: 10,
    pageSizes: [5, 10, 15, 20],
    pageSizeValueField: 'value',
    pageSizeDisplayField: 'text',
    selectedCount: 0,

    selectionVisible: false,
    totalVisible: true,
    pageSizeVisible: true,

    pageSizeRightIconCls: 'fas fa-angle-down',
    prevIconCls: 'fas fa-angle-left',
    nextIconCls: 'fas fa-angle-right'

};

export default BriefPagination;
<|endoftext|>


<commit_before>define(function(require, exports, module) {
    "use strict";

    main.consumes = [
        "Plugin", "c9", "ui", "menus", "tabManager", "fs", "commands",
        "tree", "apf"
    ];
    main.provides = ["newresource"];
    return main;

    function main(options, imports, register) {
        var Plugin      = imports.Plugin;
        var c9          = imports.c9;
        var ui          = imports.ui;
        var fs          = imports.fs;
        var menus       = imports.menus;
        var commands    = imports.commands;
        var tabs        = imports.tabManager;
        var tree        = imports.tree;
        var apf         = imports.apf;

        var markup    = require("text!./newresource.xml");
        // ui elements
        var trFiles, winNewFileTemplate, btnFileTemplateSave, btnFileTemplateCancel, lstFileTemplates;

        /***** Initialization *****/

        var plugin   = new Plugin("Ajax.org", main.consumes);
        var emit     = plugin.getEmitter();
        var readonly = c9.readonly;

        var loaded   = false;
        function load(callback){
            if (loaded) return false;
            loaded = true;

            commands.addCommand({
                name   : "newfile",
                hint   : "create a new file resource",
                msg    : "New file created.",
                bindKey: { mac: "Option-N", win: "Ctrl-N" },
                exec   : function () {
                    newFile();
                }
            }, plugin);

            commands.addCommand({
                name: "newfiletemplate",
                hint: "create a new directory resource",
                msg: "New directory created.",
                bindKey: { mac: "Option-Shift-N", win: "Alt-N" },
                exec: function () {
                    newFileTemplate();
                }
            }, plugin);

            commands.addCommand({
                name: "newfolder",
                hint: "open the new file template dialog",
                bindKey: { mac: "Ctrl-Shift-N", win: "Ctrl-Shift-N" },
                exec: function () {
                    newFolder();
                }
            }, plugin);

            menus.addItemByPath("File/New File", new ui.item({
                disabled: readonly,
                command : "newfile"
            }), 100, plugin);
            menus.addItemByPath("File/New From Template...", new ui.item({
                disabled: readonly,
                command : "newfiletemplate"
            }), 200, plugin);
            menus.addItemByPath("File/New Folder", new ui.item({
                disabled: readonly,
                command : "newfolder"
            }), 300, plugin);
            menus.addItemByPath("File/~", new ui.divider(), 400, plugin);
        }

        var drawn = false;
        function draw () {
            if (drawn) return;
            drawn = true;

            ui.insertMarkup(null, markup, plugin);

            winNewFileTemplate    = plugin.getElement("winNewFileTemplate");
            lstFileTemplates      = plugin.getElement("lstFileTemplates");
            btnFileTemplateSave   = plugin.getElement("btnFileTemplateSave");
            btnFileTemplateCancel = plugin.getElement("btnFileTemplateCancel");

            btnFileTemplateSave.on("click", function(){
                newFile('.' + lstFileTemplates.value, lstFileTemplates.selected.firstChild.nodeValue);
                winNewFileTemplate.hide();
            });

            btnFileTemplateCancel.on("click", function(){
                winNewFileTemplate.hide();
            });

            lstFileTemplates.on("afterchoose", function() {
                btnFileTemplateSave.dispatchEvent('click');
            });

            emit("draw");
        }

        /***** Methods *****/

        function getDirPath () {
            var node = tree.getSelectedNode();
            var path = node.getAttribute("path");
            if (node.getAttribute("type") == "file" || node.tagName == "file")
                path = path.replace(/\/[^\/]*$/, "/");

            if (!/\/$/.test(path))
                path += "/";

            return path;
        }

        function newFile(type, value, path) {
            if (readonly) return;

            draw();

            var filePath;
            var name  = "Untitled";
            var count = 0;
            type      = type || "";
            path      = path || getDirPath();

            while (tabs.findTab(filePath = path + name + (count || "") + type))
                count++;

            tabs.open({
                path     : filePath,
                value    : value || "",
                active   : true,
                document : {
                    meta : {
                        newfile : true
                    }
                }
            }, function(){});

            // ide.dispatchEvent("track_action", {type: "template", template: type});
        }

        function newFileTemplate(){
            draw();
            winNewFileTemplate.show();
        }

        function newFolder(path, callback) {
            tree.createFolder(path, false, callback || function(){});
        }

        /***** Lifecycle *****/
        plugin.on("load", function(){
            load();
        });
        plugin.on("enable", function(){

        });
        plugin.on("disable", function(){

        });
        plugin.on("unload", function(){
            loaded = false;
            drawn  = false;
        });

        /***** Register and define API *****/

        /**
         * Adds File->New File and File->New Folder menu items as well as the
         * commands for opening a new file as well as an API.
         * @singleton
         **/
        plugin.freezePublicAPI({
            /**
             * Create a new file in the workspace
             *
             * @param {String} type   The encoding of the content for the file
             * @param {String} value  The content of the file
             * @param {String} path   The path of the file to write
             */
            newFile: newFile,

            /**
             * Show the new file template window to create a file
             */
            newFileTemplate: newFileTemplate,

            /**
             * Create a new folder in the workspace and starts its renaming
             *
             * @param {String}   name          The name of the folder to create
             * @param {String}   dirPath       The directory to create the folder into
             * @param {Function} callback      Called after the folder is created
             * @param {Error}    callback.err  The error object if any error occured.
             */
            newFolder: newFolder
        });

        register(null, {
            newresource: plugin
        });
    }
});
<commit_msg>fixed most of tree tests<commit_after>define(function(require, exports, module) {
    "use strict";

    main.consumes = [
        "Plugin", "c9", "ui", "menus", "tabManager", "fs", "commands",
        "tree", "apf"
    ];
    main.provides = ["newresource"];
    return main;

    function main(options, imports, register) {
        var Plugin      = imports.Plugin;
        var c9          = imports.c9;
        var ui          = imports.ui;
        var fs          = imports.fs;
        var menus       = imports.menus;
        var commands    = imports.commands;
        var tabs        = imports.tabManager;
        var tree        = imports.tree;
        var apf         = imports.apf;

        var markup    = require("text!./newresource.xml");
        // ui elements
        var winNewFileTemplate, btnFileTemplateSave, btnFileTemplateCancel, lstFileTemplates;

        /***** Initialization *****/

        var plugin   = new Plugin("Ajax.org", main.consumes);
        var emit     = plugin.getEmitter();
        var readonly = c9.readonly;

        var loaded   = false;
        function load(callback){
            if (loaded) return false;
            loaded = true;

            commands.addCommand({
                name   : "newfile",
                hint   : "create a new file resource",
                msg    : "New file created.",
                bindKey: { mac: "Option-N", win: "Ctrl-N" },
                exec   : function () {
                    newFile();
                }
            }, plugin);

            commands.addCommand({
                name: "newfiletemplate",
                hint: "create a new directory resource",
                msg: "New directory created.",
                bindKey: { mac: "Option-Shift-N", win: "Alt-N" },
                exec: function () {
                    newFileTemplate();
                }
            }, plugin);

            commands.addCommand({
                name: "newfolder",
                hint: "open the new file template dialog",
                bindKey: { mac: "Ctrl-Shift-N", win: "Ctrl-Shift-N" },
                exec: function () {
                    newFolder();
                }
            }, plugin);

            menus.addItemByPath("File/New File", new ui.item({
                disabled: readonly,
                command : "newfile"
            }), 100, plugin);
            menus.addItemByPath("File/New From Template...", new ui.item({
                disabled: readonly,
                command : "newfiletemplate"
            }), 200, plugin);
            menus.addItemByPath("File/New Folder", new ui.item({
                disabled: readonly,
                command : "newfolder"
            }), 300, plugin);
            menus.addItemByPath("File/~", new ui.divider(), 400, plugin);
        }

        var drawn = false;
        function draw () {
            if (drawn) return;
            drawn = true;

            ui.insertMarkup(null, markup, plugin);

            winNewFileTemplate    = plugin.getElement("winNewFileTemplate");
            lstFileTemplates      = plugin.getElement("lstFileTemplates");
            btnFileTemplateSave   = plugin.getElement("btnFileTemplateSave");
            btnFileTemplateCancel = plugin.getElement("btnFileTemplateCancel");

            btnFileTemplateSave.on("click", function(){
                newFile('.' + lstFileTemplates.value, lstFileTemplates.selected.firstChild.nodeValue);
                winNewFileTemplate.hide();
            });

            btnFileTemplateCancel.on("click", function(){
                winNewFileTemplate.hide();
            });

            lstFileTemplates.on("afterchoose", function() {
                btnFileTemplateSave.dispatchEvent('click');
            });

            emit("draw");
        }

        /***** Methods *****/

        function getDirPath () {
            var node = tree.getSelectedNode();
            var path = node.getAttribute("path");
            if (node.getAttribute("type") == "file" || node.tagName == "file")
                path = path.replace(/\/[^\/]*$/, "/");

            if (!/\/$/.test(path))
                path += "/";

            return path;
        }

        function newFile(type, value, path) {
            if (readonly) return;

            draw();

            var filePath;
            var name  = "Untitled";
            var count = 0;
            type      = type || "";
            path      = path || getDirPath();

            while (tabs.findTab(filePath = path + name + (count || "") + type))
                count++;

            tabs.open({
                path     : filePath,
                value    : value || "",
                active   : true,
                document : {
                    meta : {
                        newfile : true
                    }
                }
            }, function(){});

            // ide.dispatchEvent("track_action", {type: "template", template: type});
        }

        function newFileTemplate(){
            draw();
            winNewFileTemplate.show();
        }

        function newFolder(path, callback) {
            tree.createFolder(path, false, callback || function(){});
        }

        /***** Lifecycle *****/
        plugin.on("load", function(){
            load();
        });
        plugin.on("enable", function(){

        });
        plugin.on("disable", function(){

        });
        plugin.on("unload", function(){
            loaded = false;
            drawn  = false;
        });

        /***** Register and define API *****/

        /**
         * Adds File->New File and File->New Folder menu items as well as the
         * commands for opening a new file as well as an API.
         * @singleton
         **/
        plugin.freezePublicAPI({
            /**
             * Create a new file in the workspace
             *
             * @param {String} type   The encoding of the content for the file
             * @param {String} value  The content of the file
             * @param {String} path   The path of the file to write
             */
            newFile: newFile,

            /**
             * Show the new file template window to create a file
             */
            newFileTemplate: newFileTemplate,

            /**
             * Create a new folder in the workspace and starts its renaming
             *
             * @param {String}   name          The name of the folder to create
             * @param {String}   dirPath       The directory to create the folder into
             * @param {Function} callback      Called after the folder is created
             * @param {Error}    callback.err  The error object if any error occured.
             */
            newFolder: newFolder
        });

        register(null, {
            newresource: plugin
        });
    }
});
<|endoftext|>


<commit_before>// Inserts shortcut buttons after all of the following:
//     <input type="text" class="vDateField">
//     <input type="text" class="vTimeField">

var DateTimeShortcuts = {
    calendars: [],
    calendarInputs: [],
    clockInputs: [],
    calendarDivName1: 'calendarbox', // name of calendar
 that gets toggled
    calendarDivName2: 'calendarin',  // name of
 that contains calendar
    calendarLinkName: 'calendarlink',// name of the link that is used to toggle
    clockDivName: 'clockbox',        // name of clock
 that gets toggled
    clockLinkName: 'clocklink',      // name of the link that is used to toggle
    shortCutsClass: 'datetimeshortcuts', // class of the clock and cal shortcuts
    admin_media_prefix: '',
    init: function() {
        // Get admin_media_prefix by grabbing it off the window object. It's
        // set in the admin/base.html template, so if it's not there, someone's
        // overridden the template. In that case, we'll set a clearly-invalid
        // value in the hopes that someone will examine HTTP requests and see it.
        if (window.__admin_media_prefix__ != undefined) {
            DateTimeShortcuts.admin_media_prefix = window.__admin_media_prefix__;
        } else {
            DateTimeShortcuts.admin_media_prefix = '/missing-admin-media-prefix/';
        }

        var inputs = document.getElementsByTagName('input');
        for (i=0; i<inputs.length; i++) {
            var inp = inputs[i];
            if (inp.getAttribute('type') == 'text' && inp.className.match(/vTimeField/)) {
                DateTimeShortcuts.addClock(inp);
            }
            else if (inp.getAttribute('type') == 'text' && inp.className.match(/vDateField/)) {
                DateTimeShortcuts.addCalendar(inp);
            }
        }
    },
    // Add clock widget to a given field
    addClock: function(inp) {
        var num = DateTimeShortcuts.clockInputs.length;
        DateTimeShortcuts.clockInputs[num] = inp;

        // Shortcut links (clock icon and "Now" link)
        var shortcuts_span = document.createElement('span');
        shortcuts_span.className = DateTimeShortcuts.shortCutsClass;
        inp.parentNode.insertBefore(shortcuts_span, inp.nextSibling);
        var now_link = document.createElement('a');
        now_link.setAttribute('href', "javascript:DateTimeShortcuts.handleClockQuicklink(" + num + ", new Date().strftime('" + get_format('TIME_INPUT_FORMATS')[0] + "'));");
        now_link.appendChild(document.createTextNode(gettext('Now')));
        var clock_link = document.createElement('a');
        clock_link.setAttribute('href', 'javascript:DateTimeShortcuts.openClock(' + num + ');');
        clock_link.id = DateTimeShortcuts.clockLinkName + num;
        quickElement('i', clock_link, '', 'class', 'icon-time');
        shortcuts_span.appendChild(document.createTextNode('\240'));
        shortcuts_span.appendChild(now_link);
        shortcuts_span.appendChild(document.createTextNode('\240|\240'));
        shortcuts_span.appendChild(clock_link);

        // Create clock link div
        //
        // Markup looks like:
        //

        //
Choose a time

        //

        //
Now

        //
Midnight

        //
6 a.m.

        //
Noon

        //

        //
Cancel

        //

        var clock_box = document.createElement('div');
        clock_box.style.display = 'none';
        clock_box.style.position = 'absolute';
        clock_box.className = 'clockbox module';
        clock_box.setAttribute('id', DateTimeShortcuts.clockDivName + num);
        yawdadmin.jQuery(inp).closest('.modal, body').append(clock_box);
        addEvent(clock_box, 'click', DateTimeShortcuts.cancelEventPropagation);

        quickElement('h2', clock_box, gettext('Choose a time'));
        var time_list = quickElement('ul', clock_box, '');
        time_list.className = 'timelist';
        var time_format = get_format('TIME_INPUT_FORMATS')[0];
        quickElement("a", quickElement("li", time_list, ""), gettext("Now"), "href", "javascript:DateTimeShortcuts.handleClockQuicklink(" + num + ", new Date().strftime('" + time_format + "'));");
        quickElement("a", quickElement("li", time_list, ""), gettext("Midnight"), "href", "javascript:DateTimeShortcuts.handleClockQuicklink(" + num + ", new Date(1970,1,1,0,0,0,0).strftime('" + time_format + "'));");
        quickElement("a", quickElement("li", time_list, ""), gettext("6 a.m."), "href", "javascript:DateTimeShortcuts.handleClockQuicklink(" + num + ", new Date(1970,1,1,6,0,0,0).strftime('" + time_format + "'));");
        quickElement("a", quickElement("li", time_list, ""), gettext("Noon"), "href", "javascript:DateTimeShortcuts.handleClockQuicklink(" + num + ", new Date(1970,1,1,12,0,0,0).strftime('" + time_format + "'));");

        var cancel_p = quickElement('p', clock_box, '');
        cancel_p.className = 'calendar-cancel';
        quickElement('a', cancel_p, gettext('Cancel'), 'href', 'javascript:DateTimeShortcuts.dismissClock(' + num + ');');
        yawdadmin.jQuery(document).bind('keyup', function(event) {
            if (event.which == 27) {
                // ESC key closes popup
                DateTimeShortcuts.dismissClock(num);
                event.preventDefault();
            }
        });
    },
    openClock: function(num) {
        var clock_box = document.getElementById(DateTimeShortcuts.clockDivName+num)
        var clock_link = document.getElementById(DateTimeShortcuts.clockLinkName+num)

        // Recalculate the clockbox position
        // is it left-to-right or right-to-left layout ?
        if (getStyle(document.body,'direction')!='rtl') {
            clock_box.style.left = findPosX(clock_link) + 17 + 'px';
        }
        else {
            // since style's width is in em, it'd be tough to calculate
            // px value of it. let's use an estimated px for now
            // TODO: IE returns wrong value for findPosX when in rtl mode
            //       (it returns as it was left aligned), needs to be fixed.
            clock_box.style.left = findPosX(clock_link) - 110 + 'px';
        }
        clock_box.style.top = Math.max(0, findPosY(clock_link) - 30) + 'px';

        // Show the clock box
        clock_box.style.display = 'block';
        addEvent(window.document, 'click', function() { DateTimeShortcuts.dismissClock(num); return true; });
    },
    dismissClock: function(num) {
       document.getElementById(DateTimeShortcuts.clockDivName + num).style.display = 'none';
       window.document.onclick = null;
    },
    handleClockQuicklink: function(num, val) {
       DateTimeShortcuts.clockInputs[num].value = val;
       DateTimeShortcuts.clockInputs[num].focus();
       DateTimeShortcuts.dismissClock(num);
    },
    // Add calendar widget to a given field.
    addCalendar: function(inp) {
        var num = DateTimeShortcuts.calendars.length;

        DateTimeShortcuts.calendarInputs[num] = inp;

        // Shortcut links (calendar icon and "Today" link)
        var shortcuts_span = document.createElement('span');
        shortcuts_span.className = DateTimeShortcuts.shortCutsClass;
        inp.parentNode.insertBefore(shortcuts_span, inp.nextSibling);
        var today_link = document.createElement('a');
        today_link.setAttribute('href', 'javascript:DateTimeShortcuts.handleCalendarQuickLink(' + num + ', 0);');
        today_link.appendChild(document.createTextNode(gettext('Today')));
        var cal_link = document.createElement('a');
        cal_link.setAttribute('href', 'javascript:DateTimeShortcuts.openCalendar(' + num + ');');
        cal_link.id = DateTimeShortcuts.calendarLinkName + num;
        quickElement('i', cal_link, '', 'class', 'icon-calendar');
        shortcuts_span.appendChild(document.createTextNode('\240'));
        shortcuts_span.appendChild(today_link);
        shortcuts_span.appendChild(document.createTextNode('\240|\240'));
        shortcuts_span.appendChild(cal_link);

        // Create calendarbox div.
        //
        // Markup looks like:
        //
        //

        //

        //           ‹
        //           › February 2003
        //

        //

        //
        //

        //

        //          Yesterday | Today | Tomorrow
        //

        //
Cancel

        //

        var cal_box = document.createElement('div');
        cal_box.style.display = 'none';
        cal_box.style.position = 'absolute';
        cal_box.className = 'calendarbox module';
        cal_box.setAttribute('id', DateTimeShortcuts.calendarDivName1 + num);
        yawdadmin.jQuery(inp).closest('.modal, body').append(cal_box);
        addEvent(cal_box, 'click', DateTimeShortcuts.cancelEventPropagation);

        // next-prev links
        var cal_nav = quickElement('div', cal_box, '');
        var cal_nav_prev = quickElement('a', cal_nav, '<', 'href', 'javascript:DateTimeShortcuts.drawPrev('+num+');');
        cal_nav_prev.className = 'calendarnav-previous';
        var cal_nav_next = quickElement('a', cal_nav, '>', 'href', 'javascript:DateTimeShortcuts.drawNext('+num+');');
        cal_nav_next.className = 'calendarnav-next';

        // main box
        var cal_main = quickElement('div', cal_box, '', 'id', DateTimeShortcuts.calendarDivName2 + num);
        cal_main.className = 'calendar';
        DateTimeShortcuts.calendars[num] = new Calendar(DateTimeShortcuts.calendarDivName2 + num, DateTimeShortcuts.handleCalendarCallback(num));
        DateTimeShortcuts.calendars[num].drawCurrent();

        // calendar shortcuts
        var shortcuts = quickElement('div', cal_box, '');
        shortcuts.className = 'calendar-shortcuts';
        quickElement('a', shortcuts, gettext('Yesterday'), 'href', 'javascript:DateTimeShortcuts.handleCalendarQuickLink(' + num + ', -1);');
        shortcuts.appendChild(document.createTextNode('\240|\240'));
        quickElement('a', shortcuts, gettext('Today'), 'href', 'javascript:DateTimeShortcuts.handleCalendarQuickLink(' + num + ', 0);');
        shortcuts.appendChild(document.createTextNode('\240|\240'));
        quickElement('a', shortcuts, gettext('Tomorrow'), 'href', 'javascript:DateTimeShortcuts.handleCalendarQuickLink(' + num + ', +1);');

        // cancel bar
        var cancel_p = quickElement('p', cal_box, '');
        cancel_p.className = 'calendar-cancel';
        quickElement('a', cancel_p, gettext('Cancel'), 'href', 'javascript:DateTimeShortcuts.dismissCalendar(' + num + ');');
        yawdadmin.jQuery(document).bind('keyup', function(event) {
            if (event.which == 27) {
                // ESC key closes popup
                DateTimeShortcuts.dismissCalendar(num);
                event.preventDefault();
            }
        });
    },
    openCalendar: function(num) {
        var cal_box = document.getElementById(DateTimeShortcuts.calendarDivName1+num)
        var cal_link = document.getElementById(DateTimeShortcuts.calendarLinkName+num)
        var inp = DateTimeShortcuts.calendarInputs[num];

        // Determine if the current value in the input has a valid date.
        // If so, draw the calendar with that date's year and month.
        if (inp.value) {
            var date_parts = inp.value.split('-');
            var year = date_parts[0];
            var month = parseFloat(date_parts[1]);
            if (year.match(/\d\d\d\d/) && month >= 1 && month <= 12) {
                DateTimeShortcuts.calendars[num].drawDate(month, year);
            }
        }

        // Recalculate the clockbox position
        // is it left-to-right or right-to-left layout ?
        if (getStyle(document.body,'direction')!='rtl') {
            cal_box.style.left = findPosX(cal_link) + 17 + 'px';
        }
        else {
            // since style's width is in em, it'd be tough to calculate
            // px value of it. let's use an estimated px for now
            // TODO: IE returns wrong value for findPosX when in rtl mode
            //       (it returns as it was left aligned), needs to be fixed.
            cal_box.style.left = findPosX(cal_link) - 180 + 'px';
        }
        cal_box.style.top = Math.max(0, findPosY(cal_link) - 75) + 'px';

        cal_box.style.display = 'block';
        addEvent(window.document, 'click', function() { DateTimeShortcuts.dismissCalendar(num); return true; });
    },
    dismissCalendar: function(num) {
        document.getElementById(DateTimeShortcuts.calendarDivName1+num).style.display = 'none';
        window.document.onclick = null;
    },
    drawPrev: function(num) {
        DateTimeShortcuts.calendars[num].drawPreviousMonth();
    },
    drawNext: function(num) {
        DateTimeShortcuts.calendars[num].drawNextMonth();
    },
    handleCalendarCallback: function(num) {
        format = get_format('DATE_INPUT_FORMATS')[0];
        // the format needs to be escaped a little
        format = format.replace('\\', '\\\\');
        format = format.replace('\r', '\\r');
        format = format.replace('\n', '\\n');
        format = format.replace('\t', '\\t');
        format = format.replace("'", "\\'");
        return ["function(y, m, d) { DateTimeShortcuts.calendarInputs[",
               num,
               "].value = new Date(y, m-1, d).strftime('",
               format,
               "');DateTimeShortcuts.calendarInputs[",
               num,
               "].focus();document.getElementById(DateTimeShortcuts.calendarDivName1+",
               num,
               ").style.display='none';}"].join('');
    },
    handleCalendarQuickLink: function(num, offset) {
       var d = new Date();
       d.setDate(d.getDate() + offset)
       DateTimeShortcuts.calendarInputs[num].value = d.strftime(get_format('DATE_INPUT_FORMATS')[0]);
       DateTimeShortcuts.calendarInputs[num].focus();
       DateTimeShortcuts.dismissCalendar(num);
    },
    cancelEventPropagation: function(e) {
        if (!e) e = window.event;
        e.cancelBubble = true;
        if (e.stopPropagation) e.stopPropagation();
    }
}

addEvent(window, 'load', DateTimeShortcuts.init);<commit_msg>fix calendar position inside modal inlines, for firefox<commit_after>// Inserts shortcut buttons after all of the following:
//     <input type="text" class="vDateField">
//     <input type="text" class="vTimeField">

var findPosXRel = function(el) {
	$el = yawdadmin.jQuery(el);
	if ($el.closest('.modal, body').prop("tagName").toLowerCase() == 'body') return findPosX(el);
	return $el.position().left;
}

var findPosYRel = function(el) {
	$el = yawdadmin.jQuery(el);
	$closest = $el.closest('.modal, body');
	if ($closest.prop("tagName").toLowerCase() == 'body') return findPosY(el);
	return $el.position().top + $closest.find('.modal-header').height() + 18;
}

var DateTimeShortcuts = {
    calendars: [],
    calendarInputs: [],
    clockInputs: [],
    calendarDivName1: 'calendarbox', // name of calendar
 that gets toggled
    calendarDivName2: 'calendarin',  // name of
 that contains calendar
    calendarLinkName: 'calendarlink',// name of the link that is used to toggle
    clockDivName: 'clockbox',        // name of clock
 that gets toggled
    clockLinkName: 'clocklink',      // name of the link that is used to toggle
    shortCutsClass: 'datetimeshortcuts', // class of the clock and cal shortcuts
    admin_media_prefix: '',
    init: function() {
        // Get admin_media_prefix by grabbing it off the window object. It's
        // set in the admin/base.html template, so if it's not there, someone's
        // overridden the template. In that case, we'll set a clearly-invalid
        // value in the hopes that someone will examine HTTP requests and see it.
        if (window.__admin_media_prefix__ != undefined) {
            DateTimeShortcuts.admin_media_prefix = window.__admin_media_prefix__;
        } else {
            DateTimeShortcuts.admin_media_prefix = '/missing-admin-media-prefix/';
        }

        var inputs = document.getElementsByTagName('input');
        for (i=0; i<inputs.length; i++) {
            var inp = inputs[i];
            if (inp.getAttribute('type') == 'text' && inp.className.match(/vTimeField/)) {
                DateTimeShortcuts.addClock(inp);
            }
            else if (inp.getAttribute('type') == 'text' && inp.className.match(/vDateField/)) {
                DateTimeShortcuts.addCalendar(inp);
            }
        }
    },
    // Add clock widget to a given field
    addClock: function(inp) {
        var num = DateTimeShortcuts.clockInputs.length;
        DateTimeShortcuts.clockInputs[num] = inp;

        // Shortcut links (clock icon and "Now" link)
        var shortcuts_span = document.createElement('span');
        shortcuts_span.className = DateTimeShortcuts.shortCutsClass;
        inp.parentNode.insertBefore(shortcuts_span, inp.nextSibling);
        var now_link = document.createElement('a');
        now_link.setAttribute('href', "javascript:DateTimeShortcuts.handleClockQuicklink(" + num + ", new Date().strftime('" + get_format('TIME_INPUT_FORMATS')[0] + "'));");
        now_link.appendChild(document.createTextNode(gettext('Now')));
        var clock_link = document.createElement('a');
        clock_link.setAttribute('href', 'javascript:DateTimeShortcuts.openClock(' + num + ');');
        clock_link.id = DateTimeShortcuts.clockLinkName + num;
        quickElement('i', clock_link, '', 'class', 'icon-time');
        shortcuts_span.appendChild(document.createTextNode('\240'));
        shortcuts_span.appendChild(now_link);
        shortcuts_span.appendChild(document.createTextNode('\240|\240'));
        shortcuts_span.appendChild(clock_link);

        // Create clock link div
        //
        // Markup looks like:
        //

        //
Choose a time

        //

        //
Now

        //
Midnight

        //
6 a.m.

        //
Noon

        //

        //
Cancel

        //

        var clock_box = document.createElement('div');
        clock_box.style.display = 'none';
        clock_box.style.position = 'absolute';
        clock_box.className = 'clockbox module';
        clock_box.setAttribute('id', DateTimeShortcuts.clockDivName + num);
        yawdadmin.jQuery(inp).closest('.modal, body').append(clock_box);
        addEvent(clock_box, 'click', DateTimeShortcuts.cancelEventPropagation);

        quickElement('h2', clock_box, gettext('Choose a time'));
        var time_list = quickElement('ul', clock_box, '');
        time_list.className = 'timelist';
        var time_format = get_format('TIME_INPUT_FORMATS')[0];
        quickElement("a", quickElement("li", time_list, ""), gettext("Now"), "href", "javascript:DateTimeShortcuts.handleClockQuicklink(" + num + ", new Date().strftime('" + time_format + "'));");
        quickElement("a", quickElement("li", time_list, ""), gettext("Midnight"), "href", "javascript:DateTimeShortcuts.handleClockQuicklink(" + num + ", new Date(1970,1,1,0,0,0,0).strftime('" + time_format + "'));");
        quickElement("a", quickElement("li", time_list, ""), gettext("6 a.m."), "href", "javascript:DateTimeShortcuts.handleClockQuicklink(" + num + ", new Date(1970,1,1,6,0,0,0).strftime('" + time_format + "'));");
        quickElement("a", quickElement("li", time_list, ""), gettext("Noon"), "href", "javascript:DateTimeShortcuts.handleClockQuicklink(" + num + ", new Date(1970,1,1,12,0,0,0).strftime('" + time_format + "'));");

        var cancel_p = quickElement('p', clock_box, '');
        cancel_p.className = 'calendar-cancel';
        quickElement('a', cancel_p, gettext('Cancel'), 'href', 'javascript:DateTimeShortcuts.dismissClock(' + num + ');');
        yawdadmin.jQuery(document).bind('keyup', function(event) {
            if (event.which == 27) {
                // ESC key closes popup
                DateTimeShortcuts.dismissClock(num);
                event.preventDefault();
            }
        });
    },
    openClock: function(num) {
        var clock_box = document.getElementById(DateTimeShortcuts.clockDivName+num)
        var clock_link = document.getElementById(DateTimeShortcuts.clockLinkName+num)

        // Recalculate the clockbox position
        // is it left-to-right or right-to-left layout ?
        if (getStyle(document.body,'direction')!='rtl') {
            clock_box.style.left = findPosXRel(clock_link) + 17 + 'px';
        }
        else {
            // since style's width is in em, it'd be tough to calculate
            // px value of it. let's use an estimated px for now
            // TODO: IE returns wrong value for findPosX when in rtl mode
            //       (it returns as it was left aligned), needs to be fixed.
            clock_box.style.left = findPosXRel(clock_link) - 110 + 'px';
        }
        clock_box.style.top = Math.max(0, findPosYRel(clock_link) - 30) + 'px';

        // Show the clock box
        clock_box.style.display = 'block';
        addEvent(window.document, 'click', function() { DateTimeShortcuts.dismissClock(num); return true; });
    },
    dismissClock: function(num) {
       document.getElementById(DateTimeShortcuts.clockDivName + num).style.display = 'none';
       window.document.onclick = null;
    },
    handleClockQuicklink: function(num, val) {
       DateTimeShortcuts.clockInputs[num].value = val;
       DateTimeShortcuts.clockInputs[num].focus();
       DateTimeShortcuts.dismissClock(num);
    },
    // Add calendar widget to a given field.
    addCalendar: function(inp) {
        var num = DateTimeShortcuts.calendars.length;

        DateTimeShortcuts.calendarInputs[num] = inp;

        // Shortcut links (calendar icon and "Today" link)
        var shortcuts_span = document.createElement('span');
        shortcuts_span.className = DateTimeShortcuts.shortCutsClass;
        inp.parentNode.insertBefore(shortcuts_span, inp.nextSibling);
        var today_link = document.createElement('a');
        today_link.setAttribute('href', 'javascript:DateTimeShortcuts.handleCalendarQuickLink(' + num + ', 0);');
        today_link.appendChild(document.createTextNode(gettext('Today')));
        var cal_link = document.createElement('a');
        cal_link.setAttribute('href', 'javascript:DateTimeShortcuts.openCalendar(' + num + ');');
        cal_link.id = DateTimeShortcuts.calendarLinkName + num;
        quickElement('i', cal_link, '', 'class', 'icon-calendar');
        shortcuts_span.appendChild(document.createTextNode('\240'));
        shortcuts_span.appendChild(today_link);
        shortcuts_span.appendChild(document.createTextNode('\240|\240'));
        shortcuts_span.appendChild(cal_link);

        // Create calendarbox div.
        //
        // Markup looks like:
        //
        //

        //

        //           ‹
        //           › February 2003
        //

        //

        //
        //

        //

        //          Yesterday | Today | Tomorrow
        //

        //
Cancel

        //

        var cal_box = document.createElement('div');
        cal_box.style.display = 'none';
        cal_box.style.position = 'absolute';
        cal_box.className = 'calendarbox module';
        cal_box.setAttribute('id', DateTimeShortcuts.calendarDivName1 + num);
        yawdadmin.jQuery(inp).closest('.modal, body').append(cal_box);
        addEvent(cal_box, 'click', DateTimeShortcuts.cancelEventPropagation);

        // next-prev links
        var cal_nav = quickElement('div', cal_box, '');
        var cal_nav_prev = quickElement('a', cal_nav, '<', 'href', 'javascript:DateTimeShortcuts.drawPrev('+num+');');
        cal_nav_prev.className = 'calendarnav-previous';
        var cal_nav_next = quickElement('a', cal_nav, '>', 'href', 'javascript:DateTimeShortcuts.drawNext('+num+');');
        cal_nav_next.className = 'calendarnav-next';

        // main box
        var cal_main = quickElement('div', cal_box, '', 'id', DateTimeShortcuts.calendarDivName2 + num);
        cal_main.className = 'calendar';
        DateTimeShortcuts.calendars[num] = new Calendar(DateTimeShortcuts.calendarDivName2 + num, DateTimeShortcuts.handleCalendarCallback(num));
        DateTimeShortcuts.calendars[num].drawCurrent();

        // calendar shortcuts
        var shortcuts = quickElement('div', cal_box, '');
        shortcuts.className = 'calendar-shortcuts';
        quickElement('a', shortcuts, gettext('Yesterday'), 'href', 'javascript:DateTimeShortcuts.handleCalendarQuickLink(' + num + ', -1);');
        shortcuts.appendChild(document.createTextNode('\240|\240'));
        quickElement('a', shortcuts, gettext('Today'), 'href', 'javascript:DateTimeShortcuts.handleCalendarQuickLink(' + num + ', 0);');
        shortcuts.appendChild(document.createTextNode('\240|\240'));
        quickElement('a', shortcuts, gettext('Tomorrow'), 'href', 'javascript:DateTimeShortcuts.handleCalendarQuickLink(' + num + ', +1);');

        // cancel bar
        var cancel_p = quickElement('p', cal_box, '');
        cancel_p.className = 'calendar-cancel';
        quickElement('a', cancel_p, gettext('Cancel'), 'href', 'javascript:DateTimeShortcuts.dismissCalendar(' + num + ');');
        yawdadmin.jQuery(document).bind('keyup', function(event) {
            if (event.which == 27) {
                // ESC key closes popup
                DateTimeShortcuts.dismissCalendar(num);
                event.preventDefault();
            }
        });
    },
    openCalendar: function(num) {
        var cal_box = document.getElementById(DateTimeShortcuts.calendarDivName1+num)
        var cal_link = document.getElementById(DateTimeShortcuts.calendarLinkName+num)
        var inp = DateTimeShortcuts.calendarInputs[num];

        // Determine if the current value in the input has a valid date.
        // If so, draw the calendar with that date's year and month.
        if (inp.value) {
            var date_parts = inp.value.split('-');
            var year = date_parts[0];
            var month = parseFloat(date_parts[1]);
            if (year.match(/\d\d\d\d/) && month >= 1 && month <= 12) {
                DateTimeShortcuts.calendars[num].drawDate(month, year);
            }
        }

        // Recalculate the clockbox position
        // is it left-to-right or right-to-left layout ?
        if (getStyle(document.body,'direction')!='rtl') {
            cal_box.style.left = findPosXRel(cal_link) + 17 + 'px';
        }
        else {
            // since style's width is in em, it'd be tough to calculate
            // px value of it. let's use an estimated px for now
            // TODO: IE returns wrong value for findPosX when in rtl mode
            //       (it returns as it was left aligned), needs to be fixed.
            cal_box.style.left = findPosXRel(cal_link) - 180 + 'px';
        }
        cal_box.style.top = Math.max(0, findPosYRel(cal_link) - 75) + 'px';

        cal_box.style.display = 'block';
        addEvent(window.document, 'click', function() { DateTimeShortcuts.dismissCalendar(num); return true; });
    },
    dismissCalendar: function(num) {
        document.getElementById(DateTimeShortcuts.calendarDivName1+num).style.display = 'none';
        window.document.onclick = null;
    },
    drawPrev: function(num) {
        DateTimeShortcuts.calendars[num].drawPreviousMonth();
    },
    drawNext: function(num) {
        DateTimeShortcuts.calendars[num].drawNextMonth();
    },
    handleCalendarCallback: function(num) {
        format = get_format('DATE_INPUT_FORMATS')[0];
        // the format needs to be escaped a little
        format = format.replace('\\', '\\\\');
        format = format.replace('\r', '\\r');
        format = format.replace('\n', '\\n');
        format = format.replace('\t', '\\t');
        format = format.replace("'", "\\'");
        return ["function(y, m, d) { DateTimeShortcuts.calendarInputs[",
               num,
               "].value = new Date(y, m-1, d).strftime('",
               format,
               "');DateTimeShortcuts.calendarInputs[",
               num,
               "].focus();document.getElementById(DateTimeShortcuts.calendarDivName1+",
               num,
               ").style.display='none';}"].join('');
    },
    handleCalendarQuickLink: function(num, offset) {
       var d = new Date();
       d.setDate(d.getDate() + offset)
       DateTimeShortcuts.calendarInputs[num].value = d.strftime(get_format('DATE_INPUT_FORMATS')[0]);
       DateTimeShortcuts.calendarInputs[num].focus();
       DateTimeShortcuts.dismissCalendar(num);
    },
    cancelEventPropagation: function(e) {
        if (!e) e = window.event;
        e.cancelBubble = true;
        if (e.stopPropagation) e.stopPropagation();
    }
}

addEvent(window, 'load', DateTimeShortcuts.init);<|endoftext|>