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

var mongodb = require('mongodb');
var dbClient = mongodb.MongoClient;
var ObjectId = mongodb.ObjectId;
var dbUrl = process.env.MONGODB_URI || 'mongodb://localhost/trand'

var request = require('request');
var _ = require('underscore');
var bodyParser = require('body-parser');
var cookieParser = require('cookie-parser');

router.use(bodyParser.json());
router.use(cookieParser());

router.get('/', function (req, res) {
  dbClient.connect(dbUrl, function (err, db) {
    if (!err) {
      var combinations = db.collection('combinations');
      combinations.find({email: req.currentUser.email}).toArray(function (err, results) {
        if (results.length>0) {
          results = _.sortBy(results, 'date').reverse();
          res.json(results);
          db.close();
        } else {
          res.sendStatus(404);
          db.close();
        }
      })
    } else {
      res.sendStatus(404);
    }
  });
})

router.post('/new', function (req, res) {
  var items = req.body.combinations;
  var totalPrice = 0;
  var showed = _.where(items, {show: true});
  var totalPieces = showed.length;

  for (var i = 0; i < showed.length; i++) {
    totalPrice = totalPrice + showed[i].item.price;
  }

  var newComb = {
    email: req.currentUser.email,
    information: req.body.information,
    combinations: showed,
    price: totalPrice,
    pieces: totalPieces,
    date: new Date(),
  }

  dbClient.connect(dbUrl, function (err, db) {
    if (!err) {
      var combinations = db.collection('combinations');
      if (typeof(req.body.information._id)==='undefined') {
        combinations.insert(newComb, function (err, results) {
          res.status(201).send(results.ops[0]._id);
          db.close();
        })
      } else {
        combinations.update({_id: ObjectId(req.body.information._id)}, {
          $set: {
            price: totalPrice,
            pieces: totalPieces,
            information: req.body.information,
            combinations: showed,
            date: new Date(),
          }
        }, function (err, results) {
          res.sendStatus(200);
          db.close();
        })
      }
    } else {
      res.sendStatus(404);
    }
  })
})

router.put('/update', function (req, res) {
  var updateComb = {
    _id: req.body.id,
    combinations: req.body.combinations,
  }
  var items = req.body.combinations;
  var totalPrice = 0;
  var showed = _.where(items, {show: true});
  var totalPieces = showed.length;

  for (var i = 0; i < showed.length; i++) {
    totalPrice = totalPrice + showed[i].item.price;
  }
  dbClient.connect(dbUrl, function (err, db) {
    if (!err) {
      var combinations = db.collection('combinations');
      combinations.update({_id: {id: req.body.id }}, {
        $set: {
          price: totalPrice,
          pieces: totalPieces,
          combinations: req.body.combinations,
          date: new Date(),
        }
      }, {
          upsert: true,
        }, function (err, results) {
        res.sendStatus(200);
        db.close();
      })
    } else {
      res.sendStatus(404);
    }
  })
})

router.post('/update-img', function (req, res) {
  var updateComb = {
    date: new Date(),
  }
  if (req.body.type === 'large') {
    updateComb.largeUrl = req.body.largeUrl;
  }
  if (req.body.type === 'thumb') {
    updateComb.thumbUrl = req.body.thumbUrl;
  }

  dbClient.connect(dbUrl, function (err, db) {
    if (!err) {
      var combinations = db.collection('combinations');
      combinations.update({_id: ObjectId(req.body._id)}, {
        $set: updateComb }, {
          upsert: true,
        }, function (err, results) {
        res.sendStatus(200);
        db.close();
      })
    } else {
      res.sendStatus(404);
    }
  })
})

router.delete('/remove/:_id', function (req, res) {
  dbClient.connect(dbUrl, function (err, db) {
    if (!err) {
      var combinations = db.collection('combinations');
      combinations.remove({_id: ObjectId(req.params._id)}, function (err, result) {
        res.sendStatus(200);
        db.close;
      });
    } else {
      res.sendStatus(404);
    }
  })
});

router.delete('/remove-null/', function (req, res) {
  dbClient.connect(dbUrl, function (err, db) {
    if (!err) {
      var combinations = db.collection('combinations');
      combinations.remove({email: null }, function (err, result) {
        res.sendStatus(200);
        db.close;
      });
    } else {
      res.sendStatus(404);
    }
  })
});

module.exports = router;
<commit_msg>Fix the thumb direct linke issue.<commit_after>var express = require('express');
var router = express.Router();

var mongodb = require('mongodb');
var dbClient = mongodb.MongoClient;
var ObjectId = mongodb.ObjectId;
var dbUrl = process.env.MONGODB_URI || 'mongodb://localhost/trand'

var request = require('request');
var _ = require('underscore');
var bodyParser = require('body-parser');
var cookieParser = require('cookie-parser');

router.use(bodyParser.json());
router.use(cookieParser());

router.get('/', function (req, res) {
  dbClient.connect(dbUrl, function (err, db) {
    if (!err) {
      var combinations = db.collection('combinations');
      combinations.find({email: req.currentUser.email}).toArray(function (err, results) {
        if (results.length>0) {
          results = _.sortBy(results, 'date').reverse();
          res.json(results);
          db.close();
        } else {
          res.sendStatus(404);
          db.close();
        }
      })
    } else {
      res.sendStatus(404);
    }
  });
})

router.post('/new', function (req, res) {
  var items = req.body.combinations;
  var totalPrice = 0;
  var showed = _.where(items, {show: true});
  var totalPieces = showed.length;

  for (var i = 0; i < showed.length; i++) {
    totalPrice = totalPrice + showed[i].item.price;
  }

  var newComb = {
    email: req.currentUser.email,
    information: req.body.information,
    combinations: showed,
    price: totalPrice,
    pieces: totalPieces,
    date: new Date(),
  }

  dbClient.connect(dbUrl, function (err, db) {
    if (!err) {
      var combinations = db.collection('combinations');
      if (typeof(req.body.information._id)==='undefined') {
        combinations.insert(newComb, function (err, results) {
          res.status(201).send(results.ops[0]._id);
          db.close();
        })
      } else {
        combinations.update({_id: ObjectId(req.body.information._id)}, {
          $set: {
            price: totalPrice,
            pieces: totalPieces,
            information: req.body.information,
            combinations: showed,
            date: new Date(),
          }
        }, function (err, results) {
          res.status(200).send(req.body.information._id);
          db.close();
        })
      }
    } else {
      res.sendStatus(404);
    }
  })
})

router.put('/update', function (req, res) {
  var updateComb = {
    _id: req.body.id,
    combinations: req.body.combinations,
  }
  var items = req.body.combinations;
  var totalPrice = 0;
  var showed = _.where(items, {show: true});
  var totalPieces = showed.length;

  for (var i = 0; i < showed.length; i++) {
    totalPrice = totalPrice + showed[i].item.price;
  }
  dbClient.connect(dbUrl, function (err, db) {
    if (!err) {
      var combinations = db.collection('combinations');
      combinations.update({_id: {id: req.body.id }}, {
        $set: {
          price: totalPrice,
          pieces: totalPieces,
          combinations: req.body.combinations,
          date: new Date(),
        }
      }, {
          upsert: true,
        }, function (err, results) {
        res.sendStatus(200);
        db.close();
      })
    } else {
      res.sendStatus(404);
    }
  })
})

router.post('/update-img', function (req, res) {
  var updateComb = {
    date: new Date(),
  }
  if (req.body.type === 'large') {
    updateComb.largeUrl = req.body.largeUrl;
  }
  if (req.body.type === 'thumb') {
    updateComb.thumbUrl = req.body.thumbUrl;
  }

  dbClient.connect(dbUrl, function (err, db) {
    if (!err) {
      var combinations = db.collection('combinations');
      combinations.update({_id: ObjectId(req.body._id)}, {
        $set: updateComb }, {
          upsert: true,
        }, function (err, results) {
        res.sendStatus(200);
        db.close();
      })
    } else {
      res.sendStatus(404);
    }
  })
})

router.delete('/remove/:_id', function (req, res) {
  dbClient.connect(dbUrl, function (err, db) {
    if (!err) {
      var combinations = db.collection('combinations');
      combinations.remove({_id: ObjectId(req.params._id)}, function (err, result) {
        res.sendStatus(200);
        db.close;
      });
    } else {
      res.sendStatus(404);
    }
  })
});

router.delete('/remove-null/', function (req, res) {
  dbClient.connect(dbUrl, function (err, db) {
    if (!err) {
      var combinations = db.collection('combinations');
      combinations.remove({email: null }, function (err, result) {
        res.sendStatus(200);
        db.close;
      });
    } else {
      res.sendStatus(404);
    }
  })
});

module.exports = router;
<|endoftext|>


<commit_before>/**
 * jquery.detectSwipe v2.1.1
 * jQuery Plugin to obtain touch gestures from iPhone, iPod Touch, iPad and Android
 * http://github.com/marcandre/detect_swipe
 * Based on touchwipe by Andreas Waltl, netCU Internetagentur (http://www.netcu.de)
 */
(function($) {

  $.detectSwipe = {
    version: '2.1.1',
    enabled: 'ontouchstart' in document.documentElement,
    preventDefault: true,
    threshold: 20
  };

  var startX,
    startY,
    isMoving = false;

  function onTouchEnd() {
    this.removeEventListener('touchmove', onTouchMove);
    this.removeEventListener('touchend', onTouchEnd);
    isMoving = false;
  }

  function onTouchMove(e) {
    if ($.detectSwipe.preventDefault) { e.preventDefault(); }
    if(isMoving) {
      var x = e.touches[0].pageX;
      var y = e.touches[0].pageY;
      var dx = startX - x;
      var dy = startY - y;
      var dir;
      if(Math.abs(dx) >= $.detectSwipe.threshold) {
        dir = dx > 0 ? 'left' : 'right'
      }
      else if(Math.abs(dy) >= $.detectSwipe.threshold) {
        dir = dy > 0 ? 'down' : 'up'
      }
      if(dir) {
        onTouchEnd.call(this);
        $(this).trigger('swipe', dir).trigger('swipe' + dir);
      }
    }
  }

  function onTouchStart(e) {
    if (e.touches.length == 1) {
      startX = e.touches[0].pageX;
      startY = e.touches[0].pageY;
      isMoving = true;
      this.addEventListener('touchmove', onTouchMove, false);
      this.addEventListener('touchend', onTouchEnd, false);
    }
  }

  function setup() {
    this.addEventListener && this.addEventListener('touchstart', onTouchStart, false);
  }

  function teardown() {
    this.removeEventListener('touchstart', onTouchStart);
  }

  $.event.special.swipe = { setup: setup };

  $.each(['left', 'up', 'down', 'right'], function () {
    $.event.special['swipe' + this] = { setup: function(){
      $(this).on('swipe', $.noop);
    } };
  });
})(jQuery);
<commit_msg>Fix embarrassing error mixing up and down... [#12]<commit_after>/**
 * jquery.detectSwipe v2.1.1
 * jQuery Plugin to obtain touch gestures from iPhone, iPod Touch, iPad and Android
 * http://github.com/marcandre/detect_swipe
 * Based on touchwipe by Andreas Waltl, netCU Internetagentur (http://www.netcu.de)
 */
(function($) {

  $.detectSwipe = {
    version: '2.1.1',
    enabled: 'ontouchstart' in document.documentElement,
    preventDefault: true,
    threshold: 20
  };

  var startX,
    startY,
    isMoving = false;

  function onTouchEnd() {
    this.removeEventListener('touchmove', onTouchMove);
    this.removeEventListener('touchend', onTouchEnd);
    isMoving = false;
  }

  function onTouchMove(e) {
    if ($.detectSwipe.preventDefault) { e.preventDefault(); }
    if(isMoving) {
      var x = e.touches[0].pageX;
      var y = e.touches[0].pageY;
      var dx = startX - x;
      var dy = startY - y;
      var dir;
      if(Math.abs(dx) >= $.detectSwipe.threshold) {
        dir = dx > 0 ? 'left' : 'right'
      }
      else if(Math.abs(dy) >= $.detectSwipe.threshold) {
        dir = dy > 0 ? 'up' : 'down'
      }
      if(dir) {
        onTouchEnd.call(this);
        $(this).trigger('swipe', dir).trigger('swipe' + dir);
      }
    }
  }

  function onTouchStart(e) {
    if (e.touches.length == 1) {
      startX = e.touches[0].pageX;
      startY = e.touches[0].pageY;
      isMoving = true;
      this.addEventListener('touchmove', onTouchMove, false);
      this.addEventListener('touchend', onTouchEnd, false);
    }
  }

  function setup() {
    this.addEventListener && this.addEventListener('touchstart', onTouchStart, false);
  }

  function teardown() {
    this.removeEventListener('touchstart', onTouchStart);
  }

  $.event.special.swipe = { setup: setup };

  $.each(['left', 'up', 'down', 'right'], function () {
    $.event.special['swipe' + this] = { setup: function(){
      $(this).on('swipe', $.noop);
    } };
  });
})(jQuery);
<|endoftext|>


<commit_before>var data = require("../evals.json");
var questionData = require ("../questions.json");
var info = require('../logininfo.json');

exports.viewHistory = function(req, res) {

	var id = req.query.id;
	var pos;
	var curUserIdx = info['curUserIdx'];
	//console.log( "evalshistory= "+ curUserIdx);

	for( i = 0; i < data['answeredQuestion'].length; i++){
		var name = data['answeredQuestion'][i]['id'];
		if( name === id)
			pos = i;
	}
	var q = data['answeredQuestion'][pos]['question'];

	var lvl = parseInt(questionData['user'][curUserIdx]['level']);
	var lvlName = questionData['user'][curUserIdx]['levelNames'][lvl]['name'];

    res.render('evalshistory', {
    	"data" : questionData['user'][curUserIdx]['questionText'][0]['history'],
    	"levelName" : lvlName,
    	"question" : q
    });
};

exports.addHistory = function(req, res) {

	var json = req.body;
	var id = parseInt(json['qNum']);
	var curUserIdx = info['curUserIdx'];

	if( questionData['user'][curUserIdx]['questionText'][id]['history'].length == 0)
		json['evalNum'] = 'First Evaluation';
	else
		json['evalNum'] = 'Second Evaluation';

	questionData['user'][curUserIdx]['questionText'][id]['history'].push(json);
};
<commit_msg>Evals hisotry<commit_after>var data = require("../evals.json");
var questionData = require ("../questions.json");
var info = require('../logininfo.json');

exports.viewHistory = function(req, res) {

	var id = req.query.id;
	var pos;
	var curUserIdx = info['curUserIdx'];

	for( i = 0; i < data['answeredQuestion'].length; i++){
		var name = data['answeredQuestion'][i]['id'];
		if( name === id)
			pos = i;
	}
	var q = data['answeredQuestion'][pos]['question'];

	var lvl = parseInt(questionData['user'][curUserIdx]['level']);
	var lvlName = questionData['user'][curUserIdx]['levelNames'][lvl]['name'];

    res.render('evalshistory', {
    	"data" : questionData['user'][curUserIdx]['questionText'][id]['history'],
    	"levelName" : lvlName,
    	"question" : q
    });
};

exports.addHistory = function(req, res) {

	var json = req.body;
	var id = parseInt(json['qNum']);
	var curUserIdx = info['curUserIdx'];

	if( questionData['user'][curUserIdx]['questionText'][id]['history'].length == 0)
		json['evalNum'] = 'First Evaluation';
	else
		json['evalNum'] = 'Second Evaluation';

	questionData['user'][curUserIdx]['questionText'][id]['history'].push(json);
};
<|endoftext|>


<commit_before>/*
 * Every time the form field is changed, sanitize its contents with the given
 * function to only allow input of a certain form.
 */
(function ($) {
    jQuery.fn.restrict = function(sanitizationFunc) {
        $(this).bind($.browser.msie ? "keyup" : "input", function(e) {
            $(this).val(sanitizationFunc($(this).val()));
        });
    };

    /*
     * Every time the form field is changed, modify its contents by eliminating
     * matches for the given regular expression within the field.
     */
    jQuery.fn.regexRestrict = function(regex){
        var sanitize = function(text) {
            return text.replace(regex, '');
        };
        $(this).restrict(sanitize);
    }
})(jQuery);
<commit_msg>Check for oninput support instead of browser type<commit_after>/*
 * Every time the form field is changed, sanitize its contents with the given
 * function to only allow input of a certain form.
 */
(function ($) {
    var inputEvents = "input";
    if (!("oninput" in document || "oninput" in $("<input>")[0])) {
        inputEvents += " keyup";
    }

    jQuery.fn.restrict = function(sanitizationFunc) {
        $(this).bind(inputEvents, function(e) {
            $(this).val(sanitizationFunc($(this).val()));
        });
    };

    /*
     * Every time the form field is changed, modify its contents by eliminating
     * matches for the given regular expression within the field.
     */
    jQuery.fn.regexRestrict = function(regex){
        var sanitize = function(text) {
            return text.replace(regex, '');
        };
        $(this).restrict(sanitize);
    }
})(jQuery);
<|endoftext|>


<commit_before>var fs = require('fs');
var path = require('path');
var Promise = require('bluebird');
var Inotify = require('inotify').Inotify;

var serviceLocator = require('../service-locator');
var logger = serviceLocator.get('logger');
var JobHandlerRegistry = require('./handler/registry');

/**
 * @param {String} jobDirectory, an Absolute one!
 * @param {Array} jobHandlerList
 * @constructor
 */
function JobManager(jobDirectory, jobHandlerList) {
  if (!fs.statSync(jobDirectory).isDirectory()) {
    throw new Error('`jobDirectory` must be a directory');
  }
  this._jobDirectory = jobDirectory;
  this._inotify = new Inotify();
  this._watch = null;
  this._jobHandlerRegistry = null;

  this._setupJobHandlers(jobHandlerList);
  this._handleExistingJobFiles();
  this._installFileListeners();
}

JobManager.prototype.reset = function() {
  this._removeFileListeners();
  this._installFileListeners();
};

/**
 * @param {String} filepath
 */
JobManager.prototype._handleJobFile = function(filepath) {
  this._readJobFile(filepath)
    .then(function(job) {
      var handler = this._jobHandlerRegistry.getHandler(job['plugin'], job['event']);
      return new Promise(function(resolve, reject) {
        handler.handle(job['data']).then(function() {
          fs.unlink(filepath, function(err) {
            if (err) {
              return reject(err);
            }
            resolve();
          });
        })
      });
    }.bind(this))
    .catch(function(error) {
      logger.error(error);
    });
};

JobManager.prototype._handleExistingJobFiles = function() {
  var self = this;
  fs.readdir(this._jobDirectory, function(error, files) {
    if (error) {
      throw error;
    }
    files.forEach(function(filename) {
      self._handleJobFile(path.join(self._jobDirectory, filename));
    });
  });
};

/**
 * @param filepath
 * @returns {Promise}
 */
JobManager.prototype._readJobFile = function(filepath) {
  return new Promise(function(resolve, reject) {
    fs.readFile(filepath, function(error, content) {
      if (error) {
        reject(new Error('Invalid file "' + filepath + "'. " + error.message));
      }
      try {
        var job = JSON.parse(content);
      } catch (e) {
        reject(new Error('Invalid file content "' + filepath + "'. " + e.message));
      }
      if (!job || !job['plugin'] || !job['event'] || !job['data']) {
        reject(new Error('"plugin", "event", "data" are required job file fields'));
      }
      resolve(job);
    });
  });
};

JobManager.prototype._installFileListeners = function() {
  this._watch = this._inotify.addWatch({
    path: this._jobDirectory,
    watch_for: Inotify.IN_CLOSE_WRITE,
    callback: function(event) {
      if (event.name) {
        this._handleJobFile(path.join(this._jobDirectory, event.name));
      }
    }.bind(this)
  });
};

JobManager.prototype._removeFileListeners = function() {
  this._inotify.removeWatch(this._watch);
};

/**
 * @param {Array} jobHandlerList
 */
JobManager.prototype._setupJobHandlers = function(jobHandlerList) {
  this._jobHandlerRegistry = new JobHandlerRegistry();
  jobHandlerList.forEach(function(handler) {
    this._jobHandlerRegistry.register(handler);
  }.bind(this));
};

module.exports = JobManager;
<commit_msg>FIx service usage<commit_after>var fs = require('fs');
var path = require('path');
var Promise = require('bluebird');
var Inotify = require('inotify').Inotify;

var serviceLocator = require('../service-locator');
var JobHandlerRegistry = require('./handler/registry');

/**
 * @param {String} jobDirectory, an Absolute one!
 * @param {Array} jobHandlerList
 * @constructor
 */
function JobManager(jobDirectory, jobHandlerList) {
  if (!fs.statSync(jobDirectory).isDirectory()) {
    throw new Error('`jobDirectory` must be a directory');
  }
  this._jobDirectory = jobDirectory;
  this._inotify = new Inotify();
  this._watch = null;
  this._jobHandlerRegistry = null;

  this._setupJobHandlers(jobHandlerList);
  this._handleExistingJobFiles();
  this._installFileListeners();
}

JobManager.prototype.reset = function() {
  this._removeFileListeners();
  this._installFileListeners();
};

/**
 * @param {String} filepath
 */
JobManager.prototype._handleJobFile = function(filepath) {
  this._readJobFile(filepath)
    .then(function(job) {
      var handler = this._jobHandlerRegistry.getHandler(job['plugin'], job['event']);
      return new Promise(function(resolve, reject) {
        handler.handle(job['data']).then(function() {
          fs.unlink(filepath, function(err) {
            if (err) {
              return reject(err);
            }
            resolve();
          });
        })
      });
    }.bind(this))
    .catch(function(error) {
      serviceLocator.get('logger').error(error);
    });
};

JobManager.prototype._handleExistingJobFiles = function() {
  var self = this;
  fs.readdir(this._jobDirectory, function(error, files) {
    if (error) {
      throw error;
    }
    files.forEach(function(filename) {
      self._handleJobFile(path.join(self._jobDirectory, filename));
    });
  });
};

/**
 * @param filepath
 * @returns {Promise}
 */
JobManager.prototype._readJobFile = function(filepath) {
  return new Promise(function(resolve, reject) {
    fs.readFile(filepath, function(error, content) {
      if (error) {
        reject(new Error('Invalid file "' + filepath + "'. " + error.message));
      }
      try {
        var job = JSON.parse(content);
      } catch (e) {
        reject(new Error('Invalid file content "' + filepath + "'. " + e.message));
      }
      if (!job || !job['plugin'] || !job['event'] || !job['data']) {
        reject(new Error('"plugin", "event", "data" are required job file fields'));
      }
      resolve(job);
    });
  });
};

JobManager.prototype._installFileListeners = function() {
  this._watch = this._inotify.addWatch({
    path: this._jobDirectory,
    watch_for: Inotify.IN_CLOSE_WRITE,
    callback: function(event) {
      if (event.name) {
        this._handleJobFile(path.join(this._jobDirectory, event.name));
      }
    }.bind(this)
  });
};

JobManager.prototype._removeFileListeners = function() {
  this._inotify.removeWatch(this._watch);
};

/**
 * @param {Array} jobHandlerList
 */
JobManager.prototype._setupJobHandlers = function(jobHandlerList) {
  this._jobHandlerRegistry = new JobHandlerRegistry();
  jobHandlerList.forEach(function(handler) {
    this._jobHandlerRegistry.register(handler);
  }.bind(this));
};

module.exports = JobManager;
<|endoftext|>


<commit_before>/*
 * jQuery GrewForm Plugin v0.4.0.1
 *
 * Copyright 2011, Artem Suschev
 * Grandcapital Ltd.
 *
 * Licensed under the MIT license (license.txt)
 */
(function(){jQuery.fn.grewform=function(p,l){jQuery("input,textarea").live("keyup change",function(s){var q=[16,9,33,34,36,35,45,38,40,37,39];if(s.keyCode&&jQuery.inArray(s.keyCode,q)<0){var r=d(this);jQuery(this).attr("value",this.value);a(this,r)}});var n=this;for(var k in p){var o=new b(k,n,p[k])}n.find("*").keyup(function(){var q=setInterval(function(){clearInterval(q);i()},300)});jQuery(document).ready(function(){i()});f();if(l&&l.effects){for(var m in l){f[m]=l[m]}}return n.find("*").change(function(){i()})};function f(){f.effects={show:"slideDown",hide:"slideUp"}}jQuery.fn.grewform.reset=function(){b.id=undefined;b.all=[]};jQuery.fn.grewform.runRules=function(){i()};function c(k){}function i(){jQuery.each(b.all,function(k,l){(function(n){var m=setInterval(function(){try{if(!b.form.find("*").is(":animated")){clearInterval(m);if(n.unmatches()){n.run_unmatch_actions()}}}catch(o){clearInterval(m)}},200)}(l))});jQuery.each(b.all,function(k,l){(function(n){var m=setInterval(function(){try{if(!b.form.find("*").is(":animated")){clearInterval(m);if(n.matches()){n.run_match_actions()}}}catch(o){clearInterval(m)}},200)}(l))})}function b(l,n,k){if(typeof b.id=="undefined"){b.id=0;b.blip_ptr="hgo_grewform_rule";b.unmtach_by_blip=function(o){jQuery.each(b.all,function(p,q){if(q.blip===o){q.run_unmatch_actions()}})};b.all=[];b.form=n}this.id=b.id;b.id=b.id+1;this.blip=b.blip_ptr+this.id;this.selectors=h((""+l).split("AND"));jQuery.each(this.selectors,function(o){return function(q,p){o[q]=jQuery.trim(""+p)}}(this.selectors));this.selector=j(this.selectors);this.trigged=false;this.match_actions=[];this.unmatch_actions=[];this.raw=k;this.raw_selector=l;for(var m in k){e(m,n,this)}this.matches=function(){for(var p=0;p<this.selectors.length;p++){var o=this.selectors[p];if((!this.trigged)){if(b.form.find(o).filter("option:first").length>0){if(b.form.find(o).parent("select:visible:first").length===0){return false}}else{if(b.form.find(o).filter(":visible:first,input[type=hidden]:first").length===0){return false}}}else{return false}}return true};this.unmatches=function(){if(!this.trigged){return false}for(var p=0;p<this.selectors.length;p++){var o=this.selectors[p];if(b.form.find(o+":first").length===0){return true}}return false};this.run_match_actions=function(){c("match: "+this.raw_selector);this.trigged=true;b.form.find(this.selector).addClass(this.blip);for(var o=0;o<this.match_actions.length;o++){var p=this.match_actions[o];p.call(this)}};this.run_match_actions.first_run=true;this.run_unmatch_actions=function(){c("unmatch: "+this.raw_selector);this.trigged=false;jQuery("."+this.blip).removeClass(this.blip);for(var o=0;o<this.unmatch_actions.length;o++){var p=this.unmatch_actions[o];p.call(this)}};b.all.push(this)}function e(n,o,p){switch(n){case"show":p.match_actions.push(function(q){return function(){if(q.is("option")){q.show()}else{q[f.effects.show]()}}}(o.find(p.raw[n])));p.unmatch_actions.push(function(q){return function(){g(q);if(q.is("option")){q.hide()}else{q[f.effects.hide]()}}}(o.find(p.raw[n])));break;case"hide":p.match_actions.push(function(q){return function(){if(q.is("option")){q.hide()}else{q[f.effects.hide]()}}}(o.find(p.raw[n])));p.unmatch_actions.push(function(q){return function(){g(q);if(q.is("option")){q.show()}else{q[f.effects.show]()}}}(o.find(p.raw[n])));break;case"set_value":p.match_actions.push(function(q){return function(){var r=this;jQuery.each(q,function(s,t){jQuery.each(b.form.find(s),function(){var u=jQuery(this).tagName;if(u=="select"){if(r.run_match_actions.first_run){r.unmatch_actions.push(function(v,w){return function(){g(v);w.attr("selected","selected")}}(elems.find("option [value="+t+"]"),elems.find("option:selected")))}elems.find("option").removeAttr("selected");elems.find("option [value="+t+"]").attr("selected","selected")}else{if(r.run_match_actions.first_run){r.unmatch_actions.push(function(w,v){return function(){g(w);w.val(v)}}(jQuery(s),jQuery(s).val()))}jQuery(s).val(t)}})});r.run_match_actions.first_run=false}}(p.raw[n]));break;case"add_options":for(var k in p.raw[n]){var m=p.raw[n][k];if(typeof m=="function"){p.match_actions.push(function(s,q,r){return function(){var u=r();for(var t in u){var w=s.find(q);jQuery("<option></option>").html(u[t]).val(t).appendTo(w)}if(this.run_match_actions.first_run){for(var t in u){this.unmatch_actions.push(function(z,x,y){return function(){g(z.children(x).children("option[value="+y+"]"));z.find(x).children().remove("option[value="+y+"]")}}(o,q,t))}this.run_match_actions.first_run=false}}}(o,k,m))}else{for(var l in m){p.match_actions.push(function(t,q,r,s){return function(){var u=t.find(q);jQuery("<option></option>").html(s).val(r).appendTo(u)}}(o,k,l,m[l]));p.unmatch_actions.push(function(s,q,r){return function(){g(s.children(q).children("option[value="+r+"]"));s.find(q).children().remove("option[value="+r+"]")}}(o,k,l))}}}break;case"disable":p.match_actions.push(function(q){return function(){q.attr("disabled","disabled")}}(o.find(p.raw[n])));p.unmatch_actions.push(function(q){return function(){g(q);q.removeAttr("disabled")}}(o.find(p.raw[n])));break;case"enable":p.match_actions.push(function(q){return function(){q.removeAttr("disabled")}}(o.find(p.raw[n])));p.unmatch_actions.push(function(q){return function(){g(q);q.attr("disabled","disabled")}}(o.find(p.raw[n])));break;case"check":p.match_actions.push(function(q){return function(){q.attr("checked","checked")}}(o.find(p.raw[n])));p.unmatch_actions.push(function(q){return function(){g(q);q.removeAttr("checked")}}(o.find(p.raw[n])));break;case"uncheck":p.match_actions.push(function(q){return function(){q.removeAttr("checked")}}(o.find(p.raw[n])));p.unmatch_actions.push(function(q){return function(){g(q);q.attr("checked","checked")}}(o.find(p.raw[n])));break;case"set_html":p.match_actions.push(function(q){return function(){var r=this;jQuery.each(q,function(s,t){jQuery.each(b.form.find(s),function(){if(r.run_match_actions.first_run){r.unmatch_actions.push(function(u,v){return function(){g(u);u.html(v)}}(jQuery(s),jQuery(s).html()))}jQuery(s).html(t)})});r.run_match_actions.first_run=false}}(p.raw[n]));break;case"custom":p.match_actions.push(function(r,q){return function(){r.call(q)}}(p.raw[n]["match"],jQuery(p.selector)));p.unmatch_actions.push(function(r,q){return function(){r.call(q)}}(p.raw[n]["unmatch"],jQuery(p.selector)));break;default:return}}function g(k){jQuery.each(k,function(){var m=jQuery(this);if(m.attr("class")!==undefined){var l=m.attr("class").split(" ");jQuery.each(l,function(o,n){b.unmtach_by_blip(n)})}g(m.children())})}function h(k){if(k.constructor!==Array){return[k]}return k}function j(k){res="";for(var l=0;l<k.length;l++){res+=jQuery.trim(""+k[l])+","}return res}function d(l){var m=0;if(document.selection){l.focus();var k=document.selection.createRange();k.moveStart("character",-l.value.length);m=Sel.text.length}else{if(l.selectionStart||l.selectionStart=="0"){m=l.selectionStart}}return(m)}function a(l,m){if(l.setSelectionRange){l.focus();l.setSelectionRange(m,m)}else{if(l.createTextRange){var k=l.createTextRange();k.collapse(true);k.moveEnd("character",m);k.moveStart("character",m);k.select()}}}})();<commit_msg>Minifying<commit_after>/*
 * jQuery GrewForm Plugin v0.4.0.1
 *
 * Copyright 2011, Artem Suschev
 * Grandcapital Ltd.
 *
 * Licensed under the MIT license (license.txt)
 */
(function(){jQuery.fn.grewform=function(p,l){jQuery("input,textarea").live("keyup change",function(s){var q=[16,9,33,34,36,35,45,38,40,37,39];if(s.keyCode&&jQuery.inArray(s.keyCode,q)<0){var r=d(this);jQuery(this).attr("value",this.value);a(this,r)}});var n=this;for(var k in p){var o=new b(k,n,p[k])}n.find("*").keyup(function(){var q=setInterval(function(){clearInterval(q);i()},300)});jQuery(document).ready(function(){i()});f();if(l&&l.effects){for(var m in l){f[m]=l[m]}}return n.find("*").change(function(){i()})};function f(){f.effects={show:"slideDown",hide:"slideUp"}}jQuery.fn.grewform.reset=function(){b.id=undefined;b.all=[]};jQuery.fn.grewform.runRules=function(){i()};function c(k){}function i(){jQuery.each(b.all,function(k,l){(function(n){var m=setInterval(function(){try{if(!b.form.find("*").is(":animated")){clearInterval(m);if(n.unmatches()){n.run_unmatch_actions()}}}catch(o){clearInterval(m)}},200)}(l))});jQuery.each(b.all,function(k,l){(function(n){var m=setInterval(function(){try{if(!b.form.find("*").is(":animated")){clearInterval(m);if(n.matches()){n.run_match_actions()}}}catch(o){clearInterval(m)}},200)}(l))})}function b(l,n,k){if(typeof b.id=="undefined"){b.id=0;b.blip_ptr="hgo_grewform_rule";b.unmtach_by_blip=function(o){jQuery.each(b.all,function(p,q){if(q.blip===o){q.run_unmatch_actions()}})};b.all=[];b.form=n}this.id=b.id;b.id=b.id+1;this.blip=b.blip_ptr+this.id;this.selectors=h((""+l).split("AND"));jQuery.each(this.selectors,function(o){return function(q,p){o[q]=jQuery.trim(""+p)}}(this.selectors));this.selector=j(this.selectors);this.trigged=false;this.match_actions=[];this.unmatch_actions=[];this.raw=k;this.raw_selector=l;for(var m in k){e(m,n,this)}this.matches=function(){for(var p=0;p<this.selectors.length;p++){var o=this.selectors[p];if((!this.trigged)){if(b.form.find(o).filter("option:first").length>0){if(b.form.find(o).parent("select:visible:first").length===0){return false}}else{if(b.form.find(o).filter(":visible:first,input[type=hidden]:first").length===0){return false}}}else{return false}}return true};this.unmatches=function(){if(!this.trigged){return false}for(var p=0;p<this.selectors.length;p++){var o=this.selectors[p];if(b.form.find(o+":first").length===0){return true}}return false};this.run_match_actions=function(){c("match: "+this.raw_selector);this.trigged=true;b.form.find(this.selector).addClass(this.blip);for(var o=0;o<this.match_actions.length;o++){var p=this.match_actions[o];p.call(this)}};this.run_match_actions.first_run=true;this.run_unmatch_actions=function(){c("unmatch: "+this.raw_selector);this.trigged=false;jQuery("."+this.blip).removeClass(this.blip);for(var o=0;o<this.unmatch_actions.length;o++){var p=this.unmatch_actions[o];p.call(this)}};b.all.push(this)}function e(n,o,p){switch(n){case"show":p.match_actions.push(function(q){return function(){if(q.is("option")){q.show()}else{q[f.effects.show]()}}}(o.find(p.raw[n])));p.unmatch_actions.push(function(q){return function(){g(q);if(q.is("option")){q.hide()}else{q[f.effects.hide]()}}}(o.find(p.raw[n])));break;case"hide":p.match_actions.push(function(q){return function(){if(q.is("option")){q.hide()}else{q[f.effects.hide]()}}}(o.find(p.raw[n])));p.unmatch_actions.push(function(q){return function(){g(q);if(q.is("option")){q.show()}else{q[f.effects.show]()}}}(o.find(p.raw[n])));break;case"set_value":p.match_actions.push(function(q){return function(){var r=this;jQuery.each(q,function(s,t){jQuery.each(b.form.find(s),function(){var u=jQuery(this).tagName;if(u=="select"){if(r.run_match_actions.first_run){r.unmatch_actions.push(function(v,w){return function(){g(v);w.attr("selected","selected")}}(elems.find("option [value="+t+"]"),elems.find("option:selected")))}elems.find("option").removeAttr("selected");elems.find("option [value="+t+"]").attr("selected","selected")}else{if(r.run_match_actions.first_run){r.unmatch_actions.push(function(w,v){return function(){g(w);w.val(v)}}(jQuery(s),jQuery(s).val()))}jQuery(s).val(t)}})});r.run_match_actions.first_run=false}}(p.raw[n]));break;case"add_options":for(var k in p.raw[n]){var m=p.raw[n][k];if(typeof m=="function"){p.match_actions.push(function(s,q,r){return function(){var u=r();for(var t in u){var w=s.find(q);jQuery("<option></option>").html(u[t]).val(t).appendTo(w)}if(this.run_match_actions.first_run){for(var t in u){this.unmatch_actions.push(function(z,x,y){return function(){g(z.children(x).children("option[value="+y+"]"));z.find(x).children().remove("option[value="+y+"]")}}(o,q,t))}this.run_match_actions.first_run=false}}}(o,k,m))}else{for(var l in m){p.match_actions.push(function(t,q,r,s){return function(){var u=t.find(q);jQuery("<option></option>").html(s).val(r).appendTo(u)}}(o,k,l,m[l]));p.unmatch_actions.push(function(s,q,r){return function(){g(s.children(q).children("option[value="+r+"]"));s.find(q).children().remove("option[value="+r+"]")}}(o,k,l))}}}break;case"disable":p.match_actions.push(function(q){return function(){q.attr("disabled","disabled")}}(o.find(p.raw[n])));p.unmatch_actions.push(function(q){return function(){g(q);q.removeAttr("disabled")}}(o.find(p.raw[n])));break;case"enable":p.match_actions.push(function(q){return function(){q.removeAttr("disabled")}}(o.find(p.raw[n])));p.unmatch_actions.push(function(q){return function(){g(q);q.attr("disabled","disabled")}}(o.find(p.raw[n])));break;case"check":p.match_actions.push(function(q){return function(){q.attr("checked","checked")}}(o.find(p.raw[n])));p.unmatch_actions.push(function(q){return function(){g(q);q.removeAttr("checked")}}(o.find(p.raw[n])));break;case"uncheck":p.match_actions.push(function(q){return function(){q.removeAttr("checked")}}(o.find(p.raw[n])));p.unmatch_actions.push(function(q){return function(){g(q);q.attr("checked","checked")}}(o.find(p.raw[n])));break;case"set_html":p.match_actions.push(function(q){return function(){var r=this;jQuery.each(q,function(s,t){jQuery.each(b.form.find(s),function(){if(r.run_match_actions.first_run){r.unmatch_actions.push(function(u,v){return function(){g(u);u.html(v)}}(jQuery(s),jQuery(s).html()))}jQuery(s).html(t)})});r.run_match_actions.first_run=false}}(p.raw[n]));break;case"custom":p.match_actions.push(function(r,q){return function(){r.call(q)}}(p.raw[n]["match"],jQuery(p.selector)));p.unmatch_actions.push(function(r,q){return function(){r.call(q)}}(p.raw[n]["unmatch"],jQuery(p.selector)));break;default:return}}function g(k){jQuery.each(k,function(){var m=jQuery(this);if(m.attr("class")!==undefined){var l=m.attr("class").split(" ");jQuery.each(l,function(o,n){b.unmtach_by_blip(n)})}g(m.children())})}function h(k){if(k.constructor!==Array){return[k]}return k}function j(k){var m=[];for(var l=0;l<k.length;l++){m.push(jQuery.trim(""+k[l]))}return m.join(",")}function d(l){var m=0;if(document.selection){l.focus();var k=document.selection.createRange();k.moveStart("character",-l.value.length);m=Sel.text.length}else{if(l.selectionStart||l.selectionStart=="0"){m=l.selectionStart}}return(m)}function a(l,m){if(l.setSelectionRange){l.focus();l.setSelectionRange(m,m)}else{if(l.createTextRange){var k=l.createTextRange();k.collapse(true);k.moveEnd("character",m);k.moveStart("character",m);k.select()}}}})();<|endoftext|>


<commit_before>/*!
 * Required Star jQuery Plugin v1.0
 * @link http://github.com/junya/jquery-required-star
 * @author Junya Ogura <http://sooey.com/>
 */
;(function($) {
  $.fn.requiredStar = function(options) {
    var opts = $.extend({}, $.fn.requiredStar.defaults, options);

    return this.each(function() {
      if ($(this).is('.' + opts.requiredClass)) {
        updateValidity($(this), opts); // update validity with default value
        var c = function() { updateValidity($(this), opts); };
        $(this).keypress(c).change(c);
      }
    });
  };

  $.fn.requiredStar.defaults = {
    requiredClass: 'required',
    validClass: 'valid'
  };

  function updateValidity(e, opts) {
    e.is('.' + opts.requiredClass) && !e.val() ? e.removeClass(opts.validClass) : e.addClass(opts.validClass);
  };
})(jQuery);
<commit_msg>update comment.<commit_after>/*!
 * Required Star jQuery Plugin v1.0
 * @link https://github.com/juno/jquery-requiredstar-plugin
 * @author Junya Ogura <http://sooey.com/>
 */
;(function($) {
  $.fn.requiredStar = function(options) {
    var opts = $.extend({}, $.fn.requiredStar.defaults, options);

    return this.each(function() {
      if ($(this).is('.' + opts.requiredClass)) {
        updateValidity($(this), opts); // update validity with default value
        var c = function() { updateValidity($(this), opts); };
        $(this).keypress(c).change(c);
      }
    });
  };

  $.fn.requiredStar.defaults = {
    requiredClass: 'required',
    validClass: 'valid'
  };

  function updateValidity(e, opts) {
    e.is('.' + opts.requiredClass) && !e.val() ? e.removeClass(opts.validClass) : e.addClass(opts.validClass);
  };
})(jQuery);
<|endoftext|>


<commit_before>/* globals define jQuery */
(function (factory) {
  if (typeof define === 'function' && define.amd) {
    // AMD. Register as an anonymous module.
    define([ 'jquery' ], factory)
  } else {
    // Browser globals
    factory(jQuery)
  }
}(function ($) {
  $.widget('ui.rotatable', $.ui.mouse, {
    widgetEventPrefix: 'rotate',

    options: {
      angle: false,       // specify an angle in radians (for backward compatability)
      degrees: false,     // specify angle in degrees
      handle: false,      // an image to use for a handle
      handleOffset: {     // where the handle should appear
        top: 0,
        left: 0
      },
      radians: false,     // specify angle in radians
      rotate: null,       // a callback for during rotation
      rotationCenterOffset: {   // offset the center of the element for rotation
        top: 0,
        left: 0
      },
      snap: false,        // boolean flag, should the element snap to a certain rotation?
      start: null,        // callback when rotation starts
      step: 22.5,         // angle in degrees that the rotation should be snapped to
      stop: null,         // callback when rotation stops
      transforms: null,   // other transforms to performed on the element
      wheelRotate: true   // boolean flag, should the element rotate when the mousewheel is rotated?
    },

    // accessor for the angle in radians
    angle: function (angle) {
      if (angle === undefined) {
        return this.options.angle
      }
      this.options.angle = angle
      this.elementCurrentAngle = angle
      this._performRotation(this.options.angle)
    },

    // calculates the element center if needed and returns it
    getElementCenter: function () {
      if (this.elementCenter == null) {
        this.elementCenter = this._calculateElementCenter()
      }

      return this.elementCenter
    },

    // accessor for the handle
    handle: function (handle) {
      if (handle === undefined) {
        return this.options.handle
      }
      this.options.handle = handle
    },

    plugins: {},

    /* accessor for the center of rotation
    * takes an object with keys of top and left
    */
    rotationCenterOffset: function (offset) {
      if (offset === undefined) {
        return this.options.rotationCenterOffset
      }
      if (offset.top !== null) {
        this.options.rotationCenterOffset.top = offset.top
      }
      if (offset.left !== null) {
        this.options.rotationCenterOffset.left = offset.left
      }
    },

    // listener for rotating the element
    rotateElement: function (event) {
      if (!this.element || this.element.disabled || this.options.disabled) {
        return false
      }

      if (!event.which) {
        this.stopRotate(event)
        return false
      }

      var rotateAngle = this._calculateRotateAngle(event)
      var previousRotateAngle = this.elementCurrentAngle
      this.elementCurrentAngle = rotateAngle

      // Plugins callbacks need to be called first.
      this._propagate('rotate', event)

      if (this._propagate('rotate', event) === false) {
        this.elementCurrentAngle = previousRotateAngle
        return false
      }
      var ui = this.ui()
      if (this._trigger('rotate', event, ui) === false) {
        this.elementCurrentAngle = previousRotateAngle
        return false
      } else if (ui.angle.current !== rotateAngle) {
        rotateAngle = ui.angle.current
        this.elementCurrentAngle = rotateAngle
      }

      this._performRotation(rotateAngle)

      if (previousRotateAngle !== rotateAngle) {
        this.hasRotated = true
      }

      return false
    },

    // listener for starting rotation
    startRotate: function (event) {
      var center = this.getElementCenter()
      var startXFromCenter = event.pageX - center.x
      var startYFromCenter = event.pageY - center.y
      this.mouseStartAngle = Math.atan2(startYFromCenter, startXFromCenter)
      this.elementStartAngle = this.elementCurrentAngle
      this.hasRotated = false

      this._propagate('start', event)

      $(document).bind('mousemove', this.listeners.rotateElement)
      $(document).bind('mouseup', this.listeners.stopRotate)

      return false
    },

    // listener for stopping rotation
    stopRotate: function (event) {
      if (!this.element || this.element.disabled) {
        return
      }

      $(document).unbind('mousemove', this.listeners.rotateElement)
      $(document).unbind('mouseup', this.listeners.stopRotate)

      this.elementStopAngle = this.elementCurrentAngle

      this._propagate('stop', event)

      setTimeout(function () { this.element = false }, 10)
      return false
    },

    // listener for mousewheel rotation
    wheelRotate: function (event) {
      var angle = this._angleInRadians(Math.round(event.originalEvent.deltaY / 10))
      if (this.options.snap || event.shiftKey) {
        angle = this._calculateSnap(angle)
      }
      angle = this.elementCurrentAngle + angle
      this.angle(angle)
      this._trigger('rotate', event, this.ui())
    },

    // for callbacks
    ui: function () {
      return {
        api: this,
        element: this.element,
        angle: {
          start: this.elementStartAngle,
          current: this.elementCurrentAngle,
          degrees: Math.abs(this._angleInDegrees(this.elementCurrentAngle)),
          stop: this.elementStopAngle
        }
      }
    },

/* *********************** private functions ************************** */
    // calculates the radians for a given angle in degrees
    _angleInRadians: function (degrees) {
      return degrees * Math.PI / 180
    },

    // calculates the degrees for a given angle in radians
    _angleInDegrees: function (radians) {
      return radians * 180 / Math.PI
    },

    // calculates the center of the element
    _calculateElementCenter: function () {
      var elementOffset = this._getElementOffset()

      // Rotation center given via options
      if (this._isRotationCenterSet()) {
        return {
          x: elementOffset.left + this.rotationCenterOffset().left,
          y: elementOffset.top + this.rotationCenterOffset().top
        }
      }

      // Deduce rotation center from transform-origin
      if (this.element.css('transform-origin') !== undefined) {
        var originPx = this.element.css('transform-origin').match(/([\d.]+)px +([\d.]+)px/)
        if (originPx != null) {
          return {
            x: elementOffset.left + parseFloat(originPx[1]),
            y: elementOffset.top + parseFloat(originPx[2])
          }
        }
      }

      // Default rotation center: middle of the element
      return {
        x: elementOffset.left + this.element.width() / 2,
        y: elementOffset.top + this.element.height() / 2
      }
    },

    // calculates the angle that the element should snap to and returns it in radians
    _calculateSnap: function (radians) {
      var degrees = this._angleInDegrees(radians)
      degrees = Math.round(degrees / this.options.step) * this.options.step
      return this._angleInRadians(degrees)
    },

    // calculates the angle to rotate the element to, based on input
    _calculateRotateAngle: function (event) {
      var center = this.getElementCenter()

      var xFromCenter = event.pageX - center.x
      var yFromCenter = event.pageY - center.y
      var mouseAngle = Math.atan2(yFromCenter, xFromCenter)
      var rotateAngle = mouseAngle - this.mouseStartAngle + this.elementStartAngle

      if (this.options.snap || event.shiftKey) {
        rotateAngle = this._calculateSnap(rotateAngle)
      }

      return rotateAngle
    },

    // constructor
    _create: function () {
      var handle
      if (!this.options.handle) {
        handle = $(document.createElement('div'))
        handle.addClass('ui-rotatable-handle')
        if (this.options.handleOffset.top !== 0 || this.options.handleOffset.left !== 0) {
          handle.css('position', 'relative')
          handle.css('top', this.options.handleOffset.top + 'px')
          handle.css('left', this.options.handleOffset.left + 'px')
        }
      } else {
        handle = this.options.handle
      }

      this.listeners = {
        rotateElement: $.proxy(this.rotateElement, this),
        startRotate: $.proxy(this.startRotate, this),
        stopRotate: $.proxy(this.stopRotate, this),
        wheelRotate: $.proxy(this.wheelRotate, this)
      }

      if (this.options.wheelRotate) {
        this.element.bind('wheel', this.listeners.wheelRotate)
      }

      handle.draggable({ helper: 'clone', start: this._dragStart, handle: handle })
      handle.bind('mousedown', this.listeners.startRotate)

      if (!handle.closest(this.element).length) {
        handle.appendTo(this.element)
      }
      this.rotationCenterOffset(this.options.rotationCenterOffset)

      if (this.options.degrees) {
        this.elementCurrentAngle = this._angleInRadians(this.options.degrees)
      }
      this.elementCurrentAngle = this.options.radians || this.options.angle || 0
      this._performRotation(this.elementCurrentAngle)
    },

    // destructor
    _destroy: function () {
      this.element.removeClass('ui-rotatable')
      this.element.find('.ui-rotatable-handle').remove()

      if (this.options.wheelRotate) {
        this.element.unbind('wheel', this.listeners.wheelRotate)
      }
    },

    // used for the handle
    _dragStart: function (event) {
      if (this.element) {
        return false
      }
    },

    // retrieves the element offset
    _getElementOffset: function () {
      this._performRotation(0)
      var offset = this.element.offset()
      this._performRotation(this.elementCurrentAngle)
      return offset
    },

    _getTransforms: function (angle) {
      var transforms = 'rotate(' + angle + 'rad)'

      if (this.options.transforms) {
        transforms += ' ' + (function (transforms) {
          var t = []
          for (var i in transforms) {
            if (transforms.hasOwnProperty(i) && transforms[i]) {
              t.push(i + '(' + transforms[i] + ')')
            }
          }
          return t.join(' ')
        }(this.options.transforms))
      }
      return transforms
    },

    // checks to see if the element has a rotationCenterOffset set
    _isRotationCenterSet: function () {
      return (this.options.rotationCenterOffset.top !== 0 || this.options.rotationCenterOffset.left !== 0)
    },

    // performs the actual rotation on the element
    _performRotation: function (angle) {
      if (this._isRotationCenterSet()) {
        this.element.css('transform-origin', this.options.rotationCenterOffset.left + 'px ' + this.options.rotationCenterOffset.top + 'px')
        this.element.css('-ms-transform-origin', this.options.rotationCenterOffset.left + 'px ' + this.options.rotationCenterOffset.top + 'px') /* IE 9 */
        this.element.css('-webkit-transform-origin', this.options.rotationCenterOffset.left + 'px ' + this.options.rotationCenterOffset + 'px') /* Chrome, Safari, Opera */
      }

      var transforms = this._getTransforms(angle)

      this.element.css('transform', transforms)
      this.element.css('-moz-transform', transforms)
      this.element.css('-webkit-transform', transforms)
      this.element.css('-o-transform', transforms)
    },

    // propagates events
    _propagate: function (n, event) {
      $.ui.plugin.call(this, n, [event, this.ui()]);
      (n !== 'rotate' && this._trigger(n, event, this.ui()))
    }
  })

  return $.ui.rotatable
}))
<commit_msg>Allow initial degrees value to work correctly<commit_after>/* globals define jQuery */
(function (factory) {
  if (typeof define === 'function' && define.amd) {
    // AMD. Register as an anonymous module.
    define([ 'jquery' ], factory)
  } else {
    // Browser globals
    factory(jQuery)
  }
}(function ($) {
  $.widget('ui.rotatable', $.ui.mouse, {
    widgetEventPrefix: 'rotate',

    options: {
      angle: false,       // specify an angle in radians (for backward compatability)
      degrees: false,     // specify angle in degrees
      handle: false,      // an image to use for a handle
      handleOffset: {     // where the handle should appear
        top: 0,
        left: 0
      },
      radians: false,     // specify angle in radians
      rotate: null,       // a callback for during rotation
      rotationCenterOffset: {   // offset the center of the element for rotation
        top: 0,
        left: 0
      },
      snap: false,        // boolean flag, should the element snap to a certain rotation?
      start: null,        // callback when rotation starts
      step: 22.5,         // angle in degrees that the rotation should be snapped to
      stop: null,         // callback when rotation stops
      transforms: null,   // other transforms to performed on the element
      wheelRotate: true   // boolean flag, should the element rotate when the mousewheel is rotated?
    },

    // accessor for the angle in radians
    angle: function (angle) {
      if (angle === undefined) {
        return this.options.angle
      }
      this.options.angle = angle
      this.elementCurrentAngle = angle
      this._performRotation(this.options.angle)
    },

    // calculates the element center if needed and returns it
    getElementCenter: function () {
      if (this.elementCenter == null) {
        this.elementCenter = this._calculateElementCenter()
      }

      return this.elementCenter
    },

    // accessor for the handle
    handle: function (handle) {
      if (handle === undefined) {
        return this.options.handle
      }
      this.options.handle = handle
    },

    plugins: {},

    /* accessor for the center of rotation
    * takes an object with keys of top and left
    */
    rotationCenterOffset: function (offset) {
      if (offset === undefined) {
        return this.options.rotationCenterOffset
      }
      if (offset.top !== null) {
        this.options.rotationCenterOffset.top = offset.top
      }
      if (offset.left !== null) {
        this.options.rotationCenterOffset.left = offset.left
      }
    },

    // listener for rotating the element
    rotateElement: function (event) {
      if (!this.element || this.element.disabled || this.options.disabled) {
        return false
      }

      if (!event.which) {
        this.stopRotate(event)
        return false
      }

      var rotateAngle = this._calculateRotateAngle(event)
      var previousRotateAngle = this.elementCurrentAngle
      this.elementCurrentAngle = rotateAngle

      // Plugins callbacks need to be called first.
      this._propagate('rotate', event)

      if (this._propagate('rotate', event) === false) {
        this.elementCurrentAngle = previousRotateAngle
        return false
      }
      var ui = this.ui()
      if (this._trigger('rotate', event, ui) === false) {
        this.elementCurrentAngle = previousRotateAngle
        return false
      } else if (ui.angle.current !== rotateAngle) {
        rotateAngle = ui.angle.current
        this.elementCurrentAngle = rotateAngle
      }

      this._performRotation(rotateAngle)

      if (previousRotateAngle !== rotateAngle) {
        this.hasRotated = true
      }

      return false
    },

    // listener for starting rotation
    startRotate: function (event) {
      var center = this.getElementCenter()
      var startXFromCenter = event.pageX - center.x
      var startYFromCenter = event.pageY - center.y
      this.mouseStartAngle = Math.atan2(startYFromCenter, startXFromCenter)
      this.elementStartAngle = this.elementCurrentAngle
      this.hasRotated = false

      this._propagate('start', event)

      $(document).bind('mousemove', this.listeners.rotateElement)
      $(document).bind('mouseup', this.listeners.stopRotate)

      return false
    },

    // listener for stopping rotation
    stopRotate: function (event) {
      if (!this.element || this.element.disabled) {
        return
      }

      $(document).unbind('mousemove', this.listeners.rotateElement)
      $(document).unbind('mouseup', this.listeners.stopRotate)

      this.elementStopAngle = this.elementCurrentAngle

      this._propagate('stop', event)

      setTimeout(function () { this.element = false }, 10)
      return false
    },

    // listener for mousewheel rotation
    wheelRotate: function (event) {
      var angle = this._angleInRadians(Math.round(event.originalEvent.deltaY / 10))
      if (this.options.snap || event.shiftKey) {
        angle = this._calculateSnap(angle)
      }
      angle = this.elementCurrentAngle + angle
      this.angle(angle)
      this._trigger('rotate', event, this.ui())
    },

    // for callbacks
    ui: function () {
      return {
        api: this,
        element: this.element,
        angle: {
          start: this.elementStartAngle,
          current: this.elementCurrentAngle,
          degrees: Math.abs(this._angleInDegrees(this.elementCurrentAngle)),
          stop: this.elementStopAngle
        }
      }
    },

/* *********************** private functions ************************** */
    // calculates the radians for a given angle in degrees
    _angleInRadians: function (degrees) {
      return degrees * Math.PI / 180
    },

    // calculates the degrees for a given angle in radians
    _angleInDegrees: function (radians) {
      return radians * 180 / Math.PI
    },

    // calculates the center of the element
    _calculateElementCenter: function () {
      var elementOffset = this._getElementOffset()

      // Rotation center given via options
      if (this._isRotationCenterSet()) {
        return {
          x: elementOffset.left + this.rotationCenterOffset().left,
          y: elementOffset.top + this.rotationCenterOffset().top
        }
      }

      // Deduce rotation center from transform-origin
      if (this.element.css('transform-origin') !== undefined) {
        var originPx = this.element.css('transform-origin').match(/([\d.]+)px +([\d.]+)px/)
        if (originPx != null) {
          return {
            x: elementOffset.left + parseFloat(originPx[1]),
            y: elementOffset.top + parseFloat(originPx[2])
          }
        }
      }

      // Default rotation center: middle of the element
      return {
        x: elementOffset.left + this.element.width() / 2,
        y: elementOffset.top + this.element.height() / 2
      }
    },

    // calculates the angle that the element should snap to and returns it in radians
    _calculateSnap: function (radians) {
      var degrees = this._angleInDegrees(radians)
      degrees = Math.round(degrees / this.options.step) * this.options.step
      return this._angleInRadians(degrees)
    },

    // calculates the angle to rotate the element to, based on input
    _calculateRotateAngle: function (event) {
      var center = this.getElementCenter()

      var xFromCenter = event.pageX - center.x
      var yFromCenter = event.pageY - center.y
      var mouseAngle = Math.atan2(yFromCenter, xFromCenter)
      var rotateAngle = mouseAngle - this.mouseStartAngle + this.elementStartAngle

      if (this.options.snap || event.shiftKey) {
        rotateAngle = this._calculateSnap(rotateAngle)
      }

      return rotateAngle
    },

    // constructor
    _create: function () {
      var handle
      if (!this.options.handle) {
        handle = $(document.createElement('div'))
        handle.addClass('ui-rotatable-handle')
        if (this.options.handleOffset.top !== 0 || this.options.handleOffset.left !== 0) {
          handle.css('position', 'relative')
          handle.css('top', this.options.handleOffset.top + 'px')
          handle.css('left', this.options.handleOffset.left + 'px')
        }
      } else {
        handle = this.options.handle
      }

      this.listeners = {
        rotateElement: $.proxy(this.rotateElement, this),
        startRotate: $.proxy(this.startRotate, this),
        stopRotate: $.proxy(this.stopRotate, this),
        wheelRotate: $.proxy(this.wheelRotate, this)
      }

      if (this.options.wheelRotate) {
        this.element.bind('wheel', this.listeners.wheelRotate)
      }

      handle.draggable({ helper: 'clone', start: this._dragStart, handle: handle })
      handle.bind('mousedown', this.listeners.startRotate)

      if (!handle.closest(this.element).length) {
        handle.appendTo(this.element)
      }
      this.rotationCenterOffset(this.options.rotationCenterOffset)

      if (this.options.degrees) {
        this.elementCurrentAngle = this._angleInRadians(this.options.degrees)
      }
      else {
        this.elementCurrentAngle = this.options.radians || this.options.angle || 0
      }
      this._performRotation(this.elementCurrentAngle)
    },

    // destructor
    _destroy: function () {
      this.element.removeClass('ui-rotatable')
      this.element.find('.ui-rotatable-handle').remove()

      if (this.options.wheelRotate) {
        this.element.unbind('wheel', this.listeners.wheelRotate)
      }
    },

    // used for the handle
    _dragStart: function (event) {
      if (this.element) {
        return false
      }
    },

    // retrieves the element offset
    _getElementOffset: function () {
      this._performRotation(0)
      var offset = this.element.offset()
      this._performRotation(this.elementCurrentAngle)
      return offset
    },

    _getTransforms: function (angle) {
      var transforms = 'rotate(' + angle + 'rad)'

      if (this.options.transforms) {
        transforms += ' ' + (function (transforms) {
          var t = []
          for (var i in transforms) {
            if (transforms.hasOwnProperty(i) && transforms[i]) {
              t.push(i + '(' + transforms[i] + ')')
            }
          }
          return t.join(' ')
        }(this.options.transforms))
      }
      return transforms
    },

    // checks to see if the element has a rotationCenterOffset set
    _isRotationCenterSet: function () {
      return (this.options.rotationCenterOffset.top !== 0 || this.options.rotationCenterOffset.left !== 0)
    },

    // performs the actual rotation on the element
    _performRotation: function (angle) {
      if (this._isRotationCenterSet()) {
        this.element.css('transform-origin', this.options.rotationCenterOffset.left + 'px ' + this.options.rotationCenterOffset.top + 'px')
        this.element.css('-ms-transform-origin', this.options.rotationCenterOffset.left + 'px ' + this.options.rotationCenterOffset.top + 'px') /* IE 9 */
        this.element.css('-webkit-transform-origin', this.options.rotationCenterOffset.left + 'px ' + this.options.rotationCenterOffset + 'px') /* Chrome, Safari, Opera */
      }

      var transforms = this._getTransforms(angle)

      this.element.css('transform', transforms)
      this.element.css('-moz-transform', transforms)
      this.element.css('-webkit-transform', transforms)
      this.element.css('-o-transform', transforms)
    },

    // propagates events
    _propagate: function (n, event) {
      $.ui.plugin.call(this, n, [event, this.ui()]);
      (n !== 'rotate' && this._trigger(n, event, this.ui()))
    }
  })

  return $.ui.rotatable
}))
<|endoftext|>


<commit_before>module.exports = {
  'enforce': function(f, msg) {
    if (!f()) {
      console.error("\u001b[31m" + msg + "\u001b[0m");
    }
  },

  'odd': function(x) {
    if ((x % 2) == 1) {
      return true;
    }
    return false;
  },

  'single': function(x) {
    return {'each': function(f) { return f(x); }};
  },

  'below': function(x) {
    return function(y) { return y < x; };
  },

  'above': function(x) {
    return function(y) { return y > x; };
  },

  'equal': function(x) {
    return function(y) { return y === x; };
  },

  // Check if an array is sorted in ascending order
  'sorted': function(x) {
  	var i = 0;
    for (i = 0; i < arr.length-1; i++) {
      if (arr[i] > arr[i+1]) {
        return false;
      }
    }
    return true;
  },

  'all': function(arr) {
    return function(f) {
    	return arr.every(f);
    };
  },

  'number': function(x) {
  	return typeof x === 'number';
  },

  'string': function(x) {
  	return typeof x === 'string';
  },

  'array': function(x) {
  	return x.constructor === Array;
  },

  'object': function(x) {
  	return typeof x === 'object';
  },

  'function': function(x) {
  	return typeof(x) === 'function';
  },

  'in': function(arr) {
    return function(x) {
    	return arr.indexOf(x) !== -1;
    };
  }
}
<commit_msg>small fixes in lib<commit_after>module.exports = {
  'enforce': function(f, msg) {
    if (!f()) {
      console.error("\u001b[31m" + msg + "\u001b[0m");
    }
  },

  'odd': function(x) {
    if ((x % 2) == 1) {
      return true;
    }
    return false;
  },

  'single': function(x) {
    return {'each': function(f) { return f(x); }};
  },

  'below': function(x) {
    return function(y) { return y < x; };
  },

  'above': function(x) {
    return function(y) { return y > x; };
  },

  'equal': function(x) {
    return function(y) { return y === x; };
  },

  // Check if an array is sorted in ascending order
  'sorted': function(arr) {
    try {
      var i = 0;
      for (i = 0; i < arr.length-1; i++) {
        if (arr[i] > arr[i+1]) {
          return false;
        }
      }
      return true;
    } catch (e) { return false; }
  },

  'all': function(arr) {
    return {'each': function(f) {
    	return arr.every(f);
    }};
  },

  'number': function(x) {
  	return typeof x === 'number';
  },

  'string': function(x) {
  	return typeof x === 'string';
  },

  'array': function(x) {
  	return x.constructor === Array;
  },

  'object': function(x) {
  	return typeof x === 'object';
  },

  'function': function(x) {
  	return typeof(x) === 'function';
  },

  'in': function(arr) {
    return function(x) {
    	return arr.indexOf(x) !== -1;
    };
  }
}
<|endoftext|>


<commit_before>// Copyright 2015-2020, University of Colorado Boulder

/**
 * A laser pointer, with optional on/off button (toggle or momentary).
 * Default orientation is pointing to the right. Origin is at right center (the edge of the output nozzle).
 *
 * @author Chris Malley (PixelZoom, Inc.)
 */

import Dimension2 from '../../dot/js/Dimension2.js';
import Utils from '../../dot/js/Utils.js';
import InstanceRegistry from '../../phet-core/js/documentation/InstanceRegistry.js';
import merge from '../../phet-core/js/merge.js';
import Node from '../../scenery/js/nodes/Node.js';
import Rectangle from '../../scenery/js/nodes/Rectangle.js';
import LinearGradient from '../../scenery/js/util/LinearGradient.js';
import RoundMomentaryButton from '../../sun/js/buttons/RoundMomentaryButton.js';
import RoundStickyToggleButton from '../../sun/js/buttons/RoundStickyToggleButton.js';
import Tandem from '../../tandem/js/Tandem.js';
import sceneryPhet from './sceneryPhet.js';
import ShadedSphereNode from './ShadedSphereNode.js';

// constants
const DEFAULT_OPTIONS = {

  // nozzle and body options
  bodySize: new Dimension2( 110, 78 ),
  nozzleSize: new Dimension2( 20, 60 ),
  topColor: 'rgb( 170, 170, 170 )',
  bottomColor: 'rgb( 40, 40, 40 )',
  highlightColor: 'rgb( 245, 245, 245 )',
  highlightColorStop: 0.3,  // {number} color stop for highlight, (0,1) exclusive range
  stroke: 'black',
  lineWidth: 1,
  cornerRadius: 5,

  // button options
  hasButton: true, // {boolean} other button options are ignore if this is false
  buttonType: 'toggle', // {string} 'toggle'|'momentary'
  buttonColor: 'red',
  buttonRadius: 22,
  buttonXMargin: 10,
  buttonYMargin: 10,
  buttonTouchAreaDilation: 15,
  buttonMouseAreaDilation: 0,
  buttonRotation: 0, // {number} use this to adjust lighting on the button
  buttonAccessibleName: '',
  buttonDescriptionContent: '',

  // When enabled, the glass shows a semi-circular blue-ish lens on the output of the laser pointer node.
  // It does not change the origin of the laser pointer node, which is at the center of the casing.  The glass is
  // sometimes used to help cue the user that it is a non-laser light source.
  // See https://github.com/phetsims/scenery-phet/issues/366
  hasGlass: false,
  glassOptions: null, // {Object|null} to be filled in with defaults below, or overriden, see DEFAULT_GLASS_OPTIONS

  // PhET-iO
  tandem: Tandem.REQUIRED
};
assert && Object.freeze( DEFAULT_OPTIONS );

// Glass options, nested as discussed in https://github.com/phetsims/tasks/issues/730
const DEFAULT_GLASS_OPTIONS = {
  mainColor: 'rgb(188,225,238)',
  highlightColor: 'white',
  shadowColor: 'white',
  stroke: 'black',
  heightProportion: 0.7, // The fraction of the nozzle height, between 0 (no height) and 1 (the nozzle height)
  proportionStickingOut: 0.5 // The amount the glass "sticks out" between 0 (not at all) and 1 (hemisphere)
};
assert && Object.freeze( DEFAULT_GLASS_OPTIONS );

class LaserPointerNode extends Node {

  /**
   * @param {Property.<boolean>} onProperty - is the laser on?
   * @param {Object} [options]
   */
  constructor( onProperty, options ) {

    options = merge( {}, DEFAULT_OPTIONS, options );

    options.glassOptions = merge( {}, DEFAULT_GLASS_OPTIONS, options.glassOptions );

    assert && assert( options.highlightColorStop > 0 && options.highlightColorStop < 1 );

    // validate options
    assert && assert( options.buttonType === 'toggle' || options.buttonType === 'momentary',
      'invalid buttonType: ' + options.buttonType );

    const children = [];

    // the narrow part that the light will come out of
    const nozzleNode = new Rectangle( 0, 0, options.nozzleSize.width + options.cornerRadius, options.nozzleSize.height, {
      cornerRadius: options.cornerRadius,
      fill: new LinearGradient( 0, 0, 0, options.nozzleSize.height )
        .addColorStop( 0, options.topColor )
        .addColorStop( options.highlightColorStop, options.highlightColor )
        .addColorStop( 1, options.bottomColor ),
      stroke: options.stroke,
      lineWidth: options.lineWidth,
      right: 0,
      centerY: 0
    } );

    // the main body of the laser pointer
    const bodyNode = new Rectangle( 0, 0, options.bodySize.width, options.bodySize.height, {
      cornerRadius: options.cornerRadius,
      fill: new LinearGradient( 0, 0, 0, options.bodySize.height )
        .addColorStop( 0, options.topColor )
        .addColorStop( options.highlightColorStop, options.highlightColor )
        .addColorStop( 1, options.bottomColor ),
      stroke: options.stroke,
      lineWidth: options.lineWidth,
      right: nozzleNode.left + options.cornerRadius, // overlap to hide corner radius
      centerY: nozzleNode.centerY
    } );
    const bodyAndNozzleNode = new Node( { children: [ nozzleNode, bodyNode ] } );
    children.push( bodyAndNozzleNode );

    // the optional button that controls whether the laser is on or off
    let onOffButton = null;
    if ( options.hasButton ) {

      const buttonOptions = {
        radius: options.buttonRadius,
        xMargin: options.buttonXMargin,
        yMargin: options.buttonYMargin,
        touchAreaDilation: options.buttonTouchAreaDilation,
        mouseAreaDilation: options.buttonMouseAreaDilation,
        baseColor: options.buttonColor,
        rotation: options.buttonRotation,
        center: bodyNode.center,
        tandem: options.tandem.createTandem( 'button' ),

        // pdom
        labelContent: options.buttonAccessibleName,
        labelTagName: 'label',
        descriptionContent: options.buttonDescriptionContent
      };

      onOffButton = ( options.buttonType === 'toggle' ) ?
                    new RoundStickyToggleButton( false, true, onProperty, buttonOptions ) :
                    new RoundMomentaryButton( false, true, onProperty, buttonOptions );

      children.push( onOffButton );
    }

    // Add the glass, if any
    if ( options.hasGlass ) {
      const glassDiameter = options.nozzleSize.height * options.glassOptions.heightProportion;
      const glassOptions = merge( {}, options.glassOptions, {

        // The origin is at the output point of the nozzle, translate accordingly
        centerX: Utils.linear( 0, 1, -glassDiameter / 2, 0, options.glassOptions.proportionStickingOut ),

        // Center vertically
        centerY: 0
      } );
      const glassNode = new ShadedSphereNode( glassDiameter, glassOptions );

      // Glass is behind everything else.
      children.unshift( glassNode );
    }

    // add any children specified by the client
    options.children = children.concat( options.children || [] );

    super( options );

    // @private called by dispose
    this.disposeLaserPointerNode = () => {
      onOffButton && onOffButton.dispose();
    };

    // support for binder documentation, stripped out in builds and only runs when ?binder is specified
    assert && phet.chipper.queryParameters.binder && InstanceRegistry.registerDataURL( 'scenery-phet', 'LaserPointerNode', this );

    // @public (read-only) {Node} - in case the client needs to add input listeners to that part
    this.bodyAndNozzleNode = bodyAndNozzleNode;
  }

  get enabled() { return this.isEnabled(); }

  set enabled( value ) { this.setEnabled( value ); }

  /**
   * @public
   * @override
   */
  dispose() {
    this.disposeLaserPointerNode();
    super.dispose();
  }
}

LaserPointerNode.DEFAULT_OPTIONS = DEFAULT_OPTIONS;

sceneryPhet.register( 'LaserPointerNode', LaserPointerNode );
export default LaserPointerNode;<commit_msg>Add an option to position the button within the body, see https://github.com/phetsims/scenery-phet/issues/661<commit_after>// Copyright 2015-2020, University of Colorado Boulder

/**
 * A laser pointer, with optional on/off button (toggle or momentary).
 * Default orientation is pointing to the right. Origin is at right center (the edge of the output nozzle).
 *
 * @author Chris Malley (PixelZoom, Inc.)
 */

import Dimension2 from '../../dot/js/Dimension2.js';
import Utils from '../../dot/js/Utils.js';
import InstanceRegistry from '../../phet-core/js/documentation/InstanceRegistry.js';
import merge from '../../phet-core/js/merge.js';
import Node from '../../scenery/js/nodes/Node.js';
import Rectangle from '../../scenery/js/nodes/Rectangle.js';
import LinearGradient from '../../scenery/js/util/LinearGradient.js';
import RoundMomentaryButton from '../../sun/js/buttons/RoundMomentaryButton.js';
import RoundStickyToggleButton from '../../sun/js/buttons/RoundStickyToggleButton.js';
import Tandem from '../../tandem/js/Tandem.js';
import sceneryPhet from './sceneryPhet.js';
import ShadedSphereNode from './ShadedSphereNode.js';

// constants
const DEFAULT_OPTIONS = {

  // nozzle and body options
  bodySize: new Dimension2( 110, 78 ),
  nozzleSize: new Dimension2( 20, 60 ),
  topColor: 'rgb( 170, 170, 170 )',
  bottomColor: 'rgb( 40, 40, 40 )',
  highlightColor: 'rgb( 245, 245, 245 )',
  highlightColorStop: 0.3,  // {number} color stop for highlight, (0,1) exclusive range
  stroke: 'black',
  lineWidth: 1,
  cornerRadius: 5,

  // button options
  hasButton: true, // {boolean} other button options are ignore if this is false
  buttonType: 'toggle', // {string} 'toggle'|'momentary'
  buttonColor: 'red',
  buttonRadius: 22,
  buttonXMargin: 10,
  buttonYMargin: 10,
  buttonTouchAreaDilation: 15,
  buttonMouseAreaDilation: 0,
  getButtonLocation: body => body.center, // {function} where to position the button within the body
  buttonRotation: 0, // {number} use this to adjust lighting on the button
  buttonAccessibleName: '',
  buttonDescriptionContent: '',

  // When enabled, the glass shows a semi-circular blue-ish lens on the output of the laser pointer node.
  // It does not change the origin of the laser pointer node, which is at the center of the casing.  The glass is
  // sometimes used to help cue the user that it is a non-laser light source.
  // See https://github.com/phetsims/scenery-phet/issues/366
  hasGlass: false,
  glassOptions: null, // {Object|null} to be filled in with defaults below, or overriden, see DEFAULT_GLASS_OPTIONS

  // PhET-iO
  tandem: Tandem.REQUIRED
};
assert && Object.freeze( DEFAULT_OPTIONS );

// Glass options, nested as discussed in https://github.com/phetsims/tasks/issues/730
const DEFAULT_GLASS_OPTIONS = {
  mainColor: 'rgb(188,225,238)',
  highlightColor: 'white',
  shadowColor: 'white',
  stroke: 'black',
  heightProportion: 0.7, // The fraction of the nozzle height, between 0 (no height) and 1 (the nozzle height)
  proportionStickingOut: 0.5 // The amount the glass "sticks out" between 0 (not at all) and 1 (hemisphere)
};
assert && Object.freeze( DEFAULT_GLASS_OPTIONS );

class LaserPointerNode extends Node {

  /**
   * @param {Property.<boolean>} onProperty - is the laser on?
   * @param {Object} [options]
   */
  constructor( onProperty, options ) {

    options = merge( {}, DEFAULT_OPTIONS, options );

    options.glassOptions = merge( {}, DEFAULT_GLASS_OPTIONS, options.glassOptions );

    assert && assert( options.highlightColorStop > 0 && options.highlightColorStop < 1 );

    // validate options
    assert && assert( options.buttonType === 'toggle' || options.buttonType === 'momentary',
      'invalid buttonType: ' + options.buttonType );

    const children = [];

    // the narrow part that the light will come out of
    const nozzleNode = new Rectangle( 0, 0, options.nozzleSize.width + options.cornerRadius, options.nozzleSize.height, {
      cornerRadius: options.cornerRadius,
      fill: new LinearGradient( 0, 0, 0, options.nozzleSize.height )
        .addColorStop( 0, options.topColor )
        .addColorStop( options.highlightColorStop, options.highlightColor )
        .addColorStop( 1, options.bottomColor ),
      stroke: options.stroke,
      lineWidth: options.lineWidth,
      right: 0,
      centerY: 0
    } );

    // the main body of the laser pointer
    const bodyNode = new Rectangle( 0, 0, options.bodySize.width, options.bodySize.height, {
      cornerRadius: options.cornerRadius,
      fill: new LinearGradient( 0, 0, 0, options.bodySize.height )
        .addColorStop( 0, options.topColor )
        .addColorStop( options.highlightColorStop, options.highlightColor )
        .addColorStop( 1, options.bottomColor ),
      stroke: options.stroke,
      lineWidth: options.lineWidth,
      right: nozzleNode.left + options.cornerRadius, // overlap to hide corner radius
      centerY: nozzleNode.centerY
    } );
    const bodyAndNozzleNode = new Node( { children: [ nozzleNode, bodyNode ] } );
    children.push( bodyAndNozzleNode );

    // the optional button that controls whether the laser is on or off
    let onOffButton = null;
    if ( options.hasButton ) {

      const buttonOptions = {
        radius: options.buttonRadius,
        xMargin: options.buttonXMargin,
        yMargin: options.buttonYMargin,
        touchAreaDilation: options.buttonTouchAreaDilation,
        mouseAreaDilation: options.buttonMouseAreaDilation,
        baseColor: options.buttonColor,
        rotation: options.buttonRotation,
        center: options.getButtonLocation( bodyNode ),
        tandem: options.tandem.createTandem( 'button' ),

        // pdom
        labelContent: options.buttonAccessibleName,
        labelTagName: 'label',
        descriptionContent: options.buttonDescriptionContent
      };

      onOffButton = ( options.buttonType === 'toggle' ) ?
                    new RoundStickyToggleButton( false, true, onProperty, buttonOptions ) :
                    new RoundMomentaryButton( false, true, onProperty, buttonOptions );

      children.push( onOffButton );
    }

    // Add the glass, if any
    if ( options.hasGlass ) {
      const glassDiameter = options.nozzleSize.height * options.glassOptions.heightProportion;
      const glassOptions = merge( {}, options.glassOptions, {

        // The origin is at the output point of the nozzle, translate accordingly
        centerX: Utils.linear( 0, 1, -glassDiameter / 2, 0, options.glassOptions.proportionStickingOut ),

        // Center vertically
        centerY: 0
      } );
      const glassNode = new ShadedSphereNode( glassDiameter, glassOptions );

      // Glass is behind everything else.
      children.unshift( glassNode );
    }

    // add any children specified by the client
    options.children = children.concat( options.children || [] );

    super( options );

    // @private called by dispose
    this.disposeLaserPointerNode = () => {
      onOffButton && onOffButton.dispose();
    };

    // support for binder documentation, stripped out in builds and only runs when ?binder is specified
    assert && phet.chipper.queryParameters.binder && InstanceRegistry.registerDataURL( 'scenery-phet', 'LaserPointerNode', this );

    // @public (read-only) {Node} - in case the client needs to add input listeners to that part
    this.bodyAndNozzleNode = bodyAndNozzleNode;
  }

  get enabled() { return this.isEnabled(); }

  set enabled( value ) { this.setEnabled( value ); }

  /**
   * @public
   * @override
   */
  dispose() {
    this.disposeLaserPointerNode();
    super.dispose();
  }
}

LaserPointerNode.DEFAULT_OPTIONS = DEFAULT_OPTIONS;

sceneryPhet.register( 'LaserPointerNode', LaserPointerNode );
export default LaserPointerNode;<|endoftext|>


<commit_before>// Copyright 2017, University of Colorado Boulder

/**
 *
 *
 * @author Sam Reid (PhET Interactive Simulations)
 */
define( function( require ) {
  'use strict';

  // modules
  var ArrowNode = require( 'SCENERY_PHET/ArrowNode' );
  var Line = require( 'SCENERY/nodes/Line' );
  var griddle = require( 'GRIDDLE/griddle' );
  var inherit = require( 'PHET_CORE/inherit' );
  var Rectangle = require( 'SCENERY/nodes/Rectangle' );
  var Node = require( 'SCENERY/nodes/Node' );

  // strings
  var LINE_WIDTH = 0.8; // Empirically determined
  var STROKE_COLOR = 'gray';
  var CHART_WIDTH = 210;

  /**
   * @constructor
   */
  function VerticalBarChart( barNodes, options ) {
    // TODO: we may want to create the barNodes in the chart because the chart is aware of how much space we are working with.
    options = _.extend( {
      width: 250,
      height: 400,
      backgroundFill: 'white'
    }, options );

    // Background for bar graph
    this.rectangle = new Rectangle( 0, 200, options.width, options.height, {
      fill: options.backgroundFill,
      stroke: STROKE_COLOR,
      cornerRadius: 8,
      lineWidth: LINE_WIDTH
    } );

    // Layer for all bars added/removed from chart
    var barLayer = new Node();

    // Layer that refers to the chart starting with an origin at 0,0
    var chartNode = new Node( {
      children: [
        barLayer,
        new Line( 0, 0, CHART_WIDTH, 0, {
          stroke: 'gray'
        } ),
        new ArrowNode( 0, 0, 0, -300 )
      ],
      center: this.rectangle.center
    } );

    // Adding barNodes to the chart with proper centering
    barNodes.forEach( function( barNode, index ) {
      barNode.centerX = (index + 1) / (barNodes.length + 1) * CHART_WIDTH;
      barLayer.addChild( barNode );
    } );

    Node.call( this, {
      children: [
        this.rectangle, chartNode
      ]
    } );
  }

  griddle.register( 'VerticalBarChart', VerticalBarChart );

  return inherit( Node, VerticalBarChart );
} );
<commit_msg>i18n for screen names. Reference #349.<commit_after>// Copyright 2017, University of Colorado Boulder

/**
 *
 *
 * @author Sam Reid (PhET Interactive Simulations)
 */
define( function( require ) {
  'use strict';

  // modules
  var ArrowNode = require( 'SCENERY_PHET/ArrowNode' );
  var Line = require( 'SCENERY/nodes/Line' );
  var griddle = require( 'GRIDDLE/griddle' );
  var inherit = require( 'PHET_CORE/inherit' );
  var Rectangle = require( 'SCENERY/nodes/Rectangle' );
  var Node = require( 'SCENERY/nodes/Node' );

  // strings
  var LINE_WIDTH = 0.8; // Empirically determined
  var STROKE_COLOR = 'gray';
  var CHART_WIDTH = 210;

  /**
   * @constructor
   */
  function VerticalBarChart( barNodes, options ) {
    // TODO: we may want to create the barNodes in the chart because the chart is aware of how much space we are working with.
    options = _.extend( {
      width: 100,
      height: 300,
      backgroundFill: 'white'
    }, options );

    // Background for bar graph
    this.rectangle = new Rectangle( 0, 200, options.width, options.height, {
      fill: options.backgroundFill,
      stroke: STROKE_COLOR,
      cornerRadius: 8,
      lineWidth: LINE_WIDTH
    } );

    // Layer for all bars added/removed from chart
    var barLayer = new Node();

    // Layer that refers to the chart starting with an origin at 0,0
    var chartNode = new Node( {
      children: [
        barLayer,
        new Line( 0, 0, options.width - 20, 0, {
          stroke: 'gray'
        } ),
        new ArrowNode( 0, 0, 0, -options.height + 20 )
      ],
      center: this.rectangle.center
    } );

    // Adding barNodes to the chart with proper centering
    barNodes.forEach( function( barNode, index ) {
      barNode.centerX = (index + 1) / (barNodes.length + 1) * CHART_WIDTH;
      barLayer.addChild( barNode );
    } );

    Node.call( this, {
      children: [
        this.rectangle, chartNode
      ]
    } );
  }

  griddle.register( 'VerticalBarChart', VerticalBarChart );

  return inherit( Node, VerticalBarChart );
} );
<|endoftext|>