<commit_before>'use strict';

var assert = require("assert");
var fs = require('fs');
var path = require('path');
var touch = require('touch');
var middleware = require('../index.js');

//Helper Functions
function getManifest(server, callback) {
  var buf = [];
  var ccHeader = null;
  var ctHeader = null;

  var mockResponse = {
    end: function(s) {
      if (typeof s === 'string') {
        buf.push(s);
      }
      try {
        assert.equal(ccHeader, 'no-cache', 'Cache-Control header should be no-cache');
        assert.equal(ctHeader, 'text/cache-manifest', 'Content-Type header should be text/cache-manifest');
        var manifest = parseManifest(buf.join(''));
        callback(null, manifest);
      } catch(err) {
        callback(err);
      }
    },
    write: function(s) {
      if (typeof s === 'string') {
        buf.push(s);
      }
    },
    set: function(name, value) {
      if (name === 'Cache-Control') {
        ccHeader = value;
      } else if (name === 'Content-Type') {
        ctHeader = value;
      }
    }
  };

  server(null, mockResponse);
}

function parseManifest(body) {
  var manifest = { CACHE: [], COMMENTS: [] };
  var lines = body.split('\n');
  assert.equal(lines[0], 'CACHE MANIFEST', 'First line should be CACHE MANIFEST');
  var section = 'CACHE';
  for (var i = 1; i < lines.length; i++) {
    var line = lines[i];
    assert.equal(line.trim(), line, 'No extra whitespace expected in cache manifest');
    if (line.length === 0) {
      //Blank line
      section = null;
    } else if (line.startsWith('#')) {
      //Comment
      manifest['COMMENTS'].push(line.substr(1));
    } else if (section === null) {
      //New section header
      assert(line.endsWith(':'), 'Cache section lines should end with :');
      section = line.substr(0, line.length - 1);
      if (section !== 'CACHE') {
        assert(!(section in manifest), 'Multiple copies of section header ' + section + ' found in manifest');
        manifest[section] = [];
      }
    } else {
      //Inside of an existing section
      manifest[section].push(line);
    }
  }

  assert.deepEqual(manifest['CACHE'].slice().sort(), manifest['CACHE'], 'Cache entries should be soted');

  return manifest;
}

var CONFIG = [{
    file: 'test_files/some_files',
    url: 'some'
  }, {
    file: 'test_files/more_files',
    url: 'test_files/more_files'
  }, {
    file: 'test_files/hello.txt',
    url: 'hello.txt'
  }
];

var INITIAL_FILES = [
  'test_files/hello.txt',
  'test_files/some_files/a.txt',
  'test_files/some_files/b.txt',
  'test_files/some_files/nested/x.txt',
  'test_files/some_files/nested/y.txt',
  'test_files/more_files/1.txt',
  'test_files/more_files/2.txt'
];

var INITIAL_URLS = [
  '/hello.txt',
  '/some/a.txt',
  '/some/b.txt',
  '/some/nested/x.txt',
  '/some/nested/y.txt',
  '/test_files/more_files/1.txt',
  '/test_files/more_files/2.txt'
];

//Tests
describe('Check filesystem', function() {
  it('Should contain expected files', function(done) {
    var count = 0;
    for(var i = 0; i < INITIAL_FILES.length; i++) {
      var filePath = INITIAL_FILES[i];
      fs.stat(filePath, function(err, stat) {
        count++;
        try {
          assert.equal(err, null, 'Error getting fs stat');
          assert(stat.isFile(), 'Missing initial file');
          if (count === INITIAL_FILES.length) {
            done();
          }
        } catch(err) {
          done(err);
        }
      });
    }
  });
});

describe.skip('Initialization', function() {
  it('should initialize properly', function (done) {
    middleware.generator(CONFIG, { readyCallback: function(server) {
      server.stop();
      done();
    }});
  });

  it('Should contain expected elements', function (done) {
    middleware.generator(CONFIG, { readyCallback: function(server) {
      getManifest(server, function(err, manifest) {
        try {
          if (err) { throw err; }
          assert.deepEqual(manifest['NETWORK'], ['*'], 'Network section doesn\'t hold expected value'); //TODO: pull this from opts
          assert.deepEqual(manifest['CACHE'], INITIAL_URLS, 'Cache section doesn\'t hold expected value(s)');
          done();
        } catch (err) {
          done(err);
        } finally {
          server.stop();
        }
      });
    }});
  });

  it('Should contain expected elements when configured with absolute paths', function (done) {
    var absolutePaths = [];
    for (var i = 0; i < CONFIG.length; i++) {
      absolutePaths.push({
        file: path.resolve(process.cwd(), CONFIG[i]['file']),
        url: CONFIG[i]['url']
      });
    }

    middleware.generator(absolutePaths, { readyCallback: function(server) {
      getManifest(server, function(err, manifest) {
        try {
          if (err) { throw err; }
          assert.deepEqual(manifest['NETWORK'], ['*'], 'Network section doesn\'t hold expected value'); //TODO: pull this from opts
          assert.deepEqual(manifest['CACHE'], INITIAL_URLS, 'Cache section doesn\'t hold expected value(s)');
          done();
        } catch (err) {
          done(err);
        } finally {
          server.stop(); //TODO: if I'm willing to stop the server before the rest of this, it can be simplified a bunch
        }
      });
    }});
  });
});

describe('Observe Changes', function() {
  var newFile = 'test_files/some_files/new_dir/1.txt';
  var newUrl = '/some/new_dir/1.txt';

  function deleteTempFiles() {
    try {
      fs.statSync(path.dirname(newFile));
      try {
        fs.statSync(newFile);
        fs.unlinkSync(newFile);

      } catch (fileErr) {
        if (fileErr.code !== 'ENOENT') {
          throw fileErr;
        }
      }
      fs.rmdirSync(path.dirname(newFile));
    } catch (dirErr) {
      if (dirErr.code !== 'ENOENT') {
        throw dirErr;
      }
    }
  }

  before(deleteTempFiles);
  after(deleteTempFiles);

  var server = null;
  beforeEach(function(done) {
    middleware.generator(CONFIG, { catchupDelay: 0, updateListener: updateListener, readyCallback: function(s) {
      server = s;
      done();
    }});
  });

  afterEach(function() {
    assert(server !== null, 'Server shouldn\'t be null after test');
    server.stop();
    server = null;
  });

  //Functions to watch a callback once and only once with a timeout
  var updateCallback = null;
  function updateListener(val) {
    if (typeof updateCallback === 'function') {
      updateCallback(val);
      updateCallback = null;
    }
  }

  function waitForUpdate(callback, timeout) {
    var outtaTime = false;
    if (typeof timeout !== 'number') {
      timeout = 500;
    }
    var timeoutId = setTimeout(function() {
      outtaTime = true;
      updateCallback = null;
      callback(new Error('Timeout waiting for update'));
    }, timeout);

    updateCallback = function(val) {
      if (! outtaTime) {
        clearTimeout(timeoutId);
        callback(null, val);
      }
    };
  }

  it('Should observe modifications to watched files', function(done) {
    touch('test_files/hello.txt');
    waitForUpdate(function(err, manifest) {
      if (err) { done(err); }
      else { done(); }
    });
  });

  it('Should observe modifications to files in watched directories', function(done) {
    touch('test_files/some_files/a.txt');
    waitForUpdate(function(err, manifest) {
      if (err) { done(err); }
      else { done(); }
    });
  });

  it('Should observe modifications to files in subdirectories', function(done) {
    touch('test_files/some_files/nested/x.txt');
    waitForUpdate(function(err, manifest) {
      if (err) { done(err); }
      else { done(); }
    });
  });

  //TODO: So many callbacks! Convert this to promises or something
  it('Should observe file creations and modifications to those new files', function(done) {
    try {
      fs.mkdirSync(path.dirname(newFile));
      fs.writeFileSync(newFile, 'TEXT');
      waitForUpdate(function(err, manifest) {
        if (err) { return done(err); }
        try {
          assert(manifest['CACHE'].indexOf(newUrl) !== -1, 'Newly created file should be in manifest');
          touch.sync(newFile);
          waitForUpdate(function(err, manifest) {
            //if (err) { return done(err); }
            fs.unlinkSync(newFile);
            //Deleting a directory that is being watched in Windows crashes watchr!
            //fs.rmdirSync(path.dirname(newFile));
            waitForUpdate(function(err, manifest) {
              if (err) { return done(err); }
              try {
                assert(manifest['CACHE'].indexOf(newUrl) === -1, 'Deleted file shouldn\'t be in manifest');
                server.stop();
                done();
              } catch (err) {
                done(err);
              }
            });
          });
        } catch (err) {
          done(err);
        }
      });
    } catch (err) {
      done(err);
    }
  });

  it('Should observe creations and deletions in newly nested directories');
});
<commit_msg>Code simplification, remove unnecessary stop calls<commit_after>'use strict';

var assert = require("assert");
var fs = require('fs');
var path = require('path');
var touch = require('touch');
var middleware = require('../index.js');

//Helper Functions
function getManifest(server, callback) {
  var buf = [];
  var ccHeader = null;
  var ctHeader = null;

  var mockResponse = {
    end: function(s) {
      if (typeof s === 'string') {
        buf.push(s);
      }
      try {
        assert.equal(ccHeader, 'no-cache', 'Cache-Control header should be no-cache');
        assert.equal(ctHeader, 'text/cache-manifest', 'Content-Type header should be text/cache-manifest');
        var manifest = parseManifest(buf.join(''));
        callback(null, manifest);
      } catch(err) {
        callback(err);
      }
    },
    write: function(s) {
      if (typeof s === 'string') {
        buf.push(s);
      }
    },
    set: function(name, value) {
      if (name === 'Cache-Control') {
        ccHeader = value;
      } else if (name === 'Content-Type') {
        ctHeader = value;
      }
    }
  };

  server(null, mockResponse);
}

function parseManifest(body) {
  var manifest = { CACHE: [], COMMENTS: [] };
  var lines = body.split('\n');
  assert.equal(lines[0], 'CACHE MANIFEST', 'First line should be CACHE MANIFEST');
  var section = 'CACHE';
  for (var i = 1; i < lines.length; i++) {
    var line = lines[i];
    assert.equal(line.trim(), line, 'No extra whitespace expected in cache manifest');
    if (line.length === 0) {
      //Blank line
      section = null;
    } else if (line.startsWith('#')) {
      //Comment
      manifest['COMMENTS'].push(line.substr(1));
    } else if (section === null) {
      //New section header
      assert(line.endsWith(':'), 'Cache section lines should end with :');
      section = line.substr(0, line.length - 1);
      if (section !== 'CACHE') {
        assert(!(section in manifest), 'Multiple copies of section header ' + section + ' found in manifest');
        manifest[section] = [];
      }
    } else {
      //Inside of an existing section
      manifest[section].push(line);
    }
  }

  assert.deepEqual(manifest['CACHE'].slice().sort(), manifest['CACHE'], 'Cache entries should be soted');

  return manifest;
}

var CONFIG = [{
    file: 'test_files/some_files',
    url: 'some'
  }, {
    file: 'test_files/more_files',
    url: 'test_files/more_files'
  }, {
    file: 'test_files/hello.txt',
    url: 'hello.txt'
  }
];

var INITIAL_FILES = [
  'test_files/hello.txt',
  'test_files/some_files/a.txt',
  'test_files/some_files/b.txt',
  'test_files/some_files/nested/x.txt',
  'test_files/some_files/nested/y.txt',
  'test_files/more_files/1.txt',
  'test_files/more_files/2.txt'
];

var INITIAL_URLS = [
  '/hello.txt',
  '/some/a.txt',
  '/some/b.txt',
  '/some/nested/x.txt',
  '/some/nested/y.txt',
  '/test_files/more_files/1.txt',
  '/test_files/more_files/2.txt'
];

//Tests
describe('Check filesystem', function() {
  it('Should contain expected files', function(done) {
    var count = 0;
    for(var i = 0; i < INITIAL_FILES.length; i++) {
      var filePath = INITIAL_FILES[i];
      fs.stat(filePath, function(err, stat) {
        count++;
        try {
          assert(err === null, 'Error getting fs stat');
          assert(stat.isFile(), 'Missing initial file');
          if (count === INITIAL_FILES.length) {
            done();
          }
        } catch(err) {
          done(err);
        }
      });
    }
  });
});

describe.skip('Initialization', function() {
  it('should initialize properly', function (done) {
    middleware.generator(CONFIG, { readyCallback: function(server) {
      server.stop();
      done();
    }});
  });

  it('Should contain expected elements', function (done) {
    middleware.generator(CONFIG, { readyCallback: function(server) {
      getManifest(server, function(err, manifest) {
        try {
          if (err) { throw err; }
          assert.deepEqual(manifest['NETWORK'], ['*'], 'Network section doesn\'t hold expected value'); //TODO: pull this from opts
          assert.deepEqual(manifest['CACHE'], INITIAL_URLS, 'Cache section doesn\'t hold expected value(s)');
          done();
        } catch (err) {
          done(err);
        } finally {
          server.stop();
        }
      });
    }});
  });

  it('Should contain expected elements when configured with absolute paths', function (done) {
    var absolutePaths = [];
    for (var i = 0; i < CONFIG.length; i++) {
      absolutePaths.push({
        file: path.resolve(process.cwd(), CONFIG[i]['file']),
        url: CONFIG[i]['url']
      });
    }

    middleware.generator(absolutePaths, { readyCallback: function(server) {
      getManifest(server, function(err, manifest) {
        try {
          server.stop();
          if (err) { return done(err); }
          assert.deepEqual(manifest['NETWORK'], ['*'], 'Network section doesn\'t hold expected value'); //TODO: pull this from opts
          assert.deepEqual(manifest['CACHE'], INITIAL_URLS, 'Cache section doesn\'t hold expected value(s)');
          done();
        } catch (err) {
          done(err);
        }
      });
    }});
  });
});

describe('Observe Changes', function() {
  var newFile = 'test_files/some_files/new_dir/1.txt';
  var newUrl = '/some/new_dir/1.txt';

  function deleteTempFiles() {
    try {
      fs.statSync(path.dirname(newFile));
      try {
        fs.statSync(newFile);
        fs.unlinkSync(newFile);

      } catch (fileErr) {
        if (fileErr.code !== 'ENOENT') {
          throw fileErr;
        }
      }
      fs.rmdirSync(path.dirname(newFile));
    } catch (dirErr) {
      if (dirErr.code !== 'ENOENT') {
        throw dirErr;
      }
    }
  }

  before(deleteTempFiles);
  after(deleteTempFiles);

  var server = null;
  beforeEach(function(done) {
    middleware.generator(CONFIG, { catchupDelay: 0, updateListener: updateListener, readyCallback: function(s) {
      server = s;
      done();
    }});
  });

  afterEach(function() {
    assert(server !== null, 'Server shouldn\'t be null after test');
    server.stop();
    server = null;
  });

  //Functions to watch a callback once and only once with a timeout
  var updateCallback = null;
  function updateListener(val) {
    if (typeof updateCallback === 'function') {
      updateCallback(val);
      updateCallback = null;
    }
  }

  function waitForUpdate(callback, timeout) {
    var outtaTime = false;
    if (typeof timeout !== 'number') {
      timeout = 500;
    }
    var timeoutId = setTimeout(function() {
      outtaTime = true;
      updateCallback = null;
      callback(new Error('Timeout waiting for update'));
    }, timeout);

    updateCallback = function(val) {
      if (! outtaTime) {
        clearTimeout(timeoutId);
        callback(null, val);
      }
    };
  }

  it('Should observe modifications to watched files', function(done) {
    touch('test_files/hello.txt');
    waitForUpdate(function(err, manifest) {
      if (err) { done(err); }
      else { done(); }
    });
  });

  it('Should observe modifications to files in watched directories', function(done) {
    touch('test_files/some_files/a.txt');
    waitForUpdate(function(err, manifest) {
      if (err) { done(err); }
      else { done(); }
    });
  });

  it('Should observe modifications to files in subdirectories', function(done) {
    touch('test_files/some_files/nested/x.txt');
    waitForUpdate(function(err, manifest) {
      if (err) { done(err); }
      else { done(); }
    });
  });

  //TODO: So many callbacks! Convert this to promises or something
  it('Should observe file creations and modifications to those new files', function(done) {
    try {
      fs.mkdirSync(path.dirname(newFile));
      fs.writeFileSync(newFile, 'TEXT');
      waitForUpdate(function(err, manifest) {
        if (err) { return done(err); }
        try {
          assert(manifest['CACHE'].indexOf(newUrl) !== -1, 'Newly created file should be in manifest');
          touch.sync(newFile);
          waitForUpdate(function(err, manifest) {
            //if (err) { return done(err); }
            fs.unlinkSync(newFile);
            //Deleting a directory that is being watched in Windows crashes watchr!
            //fs.rmdirSync(path.dirname(newFile));
            waitForUpdate(function(err, manifest) {
              if (err) { return done(err); }
              try {
                assert(manifest['CACHE'].indexOf(newUrl) === -1, 'Deleted file shouldn\'t be in manifest');
                done();
              } catch (err) {
                done(err);
              }
            });
          });
        } catch (err) {
          done(err);
        }
      });
    } catch (err) {
      done(err);
    }
  });

  it('Should observe creations and deletions in newly nested directories');
});
<|endoftext|>


<commit_before>(function ($) {
	/**
	 * One executor is created for each object containing inline fields.
	 * When the field is modified, the executor is triggered.
	 *
	 * As a delayed executor, a period of grace is given for the field to
	 * be corrected or other fields to be modified. Each modification resets
	 * the counter.
	 *
	 * When modifications stop happening, the entire object is stored in a
	 * single AJAX request.
	 */
	var executors = {}, obtainExecutor;

	obtainExecutor = function (container) {
		var url = $(container).data('object-store-url');

		if (executors[url]) {
			return executors[url];
		}

		return executors[url] = delayedExecutor(5000, function () {
			var parts = [];
			var containers = [];
			$('.editable-inline :input').each(function () {
				var container = $(this).closest('.editable-inline')[0];
				var ownership = $(container).data('object-store-url');

				if (ownership === url) {
					containers.push(container);
					parts.push($(this).serialize());
				}
			});

			$.post(url, parts.join('&'), 'json')
				.success(function () {
					$(containers).
						removeClass('modified').
						removeClass('unsaved').
						each( function () {
							var $input = $("input:first, select:first option:selected", this), newVal, newHtml;
							if ($input.length) {
								var html = $.trim($(this).data("saved_html").replace(" ", " "));
								var oldVal = $.trim($(this).data("saved_text"));
								if ($input.is("input[type=checkbox]")) {
									if ($input.prop("checked")) {
										newVal = tr("Yes");
									} else {
										newVal = tr("No");
									}
								} else if ($input.is("input[type=hidden]")) { // js datetime picker
									newVal = $("#" + $input.attr("id") + "_dptxt").val();
								} else if ($input.is("option")) {
									newVal = $input.text();
								} else {
									newVal = $input.val();
								}
								if (html) {
									newHtml = html.replace(new RegExp(oldVal.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&")), $.trim(newVal));
								} else {
									newHtml = $.trim(newVal);
								}
								if (!newHtml) {
									newHtml = " ";
								}
								$(this).removeClass("loaded").html(newHtml);
							}
						});
				})
				.error(function () {
					$(containers).filter('.modified').addClass('unsaved');
					$.getJSON($.service('object', 'report_error'));
				})
				;
		});
	};

	$(document).on('click', '.editable-inline:not(.loaded)', function () {
		var container = this
			, url = $(this).data('field-fetch-url')
			;

		$(container).
			addClass('loaded').
			data("saved_html", $(container).html()).
			data("saved_text", $(container).text());

		if (url) {
			$.get(url)
				.success(function (data) {
					$(container).html(data);
				})
				.error(function () {
					$(container).addClass('failure');
				})
				;
		}
	});

	$(document).on('change', '.editable-inline.loaded :input', function () {
		var container, executor;

		container = $(this).closest('.editable-inline')[0];
		executor = obtainExecutor(container);
		$(container).addClass('modified');

		executor();
	});
})(jQuery);
<commit_msg>[ENH] chosen: Apply Chosen to inline edit selects (thanks again marclapotre)<commit_after>(function ($) {
	/**
	 * One executor is created for each object containing inline fields.
	 * When the field is modified, the executor is triggered.
	 *
	 * As a delayed executor, a period of grace is given for the field to
	 * be corrected or other fields to be modified. Each modification resets
	 * the counter.
	 *
	 * When modifications stop happening, the entire object is stored in a
	 * single AJAX request.
	 */
	var executors = {}, obtainExecutor;

	obtainExecutor = function (container) {
		var url = $(container).data('object-store-url');

		if (executors[url]) {
			return executors[url];
		}

		return executors[url] = delayedExecutor(5000, function () {
			var parts = [];
			var containers = [];
			$('.editable-inline :input').each(function () {
				var container = $(this).closest('.editable-inline')[0];
				var ownership = $(container).data('object-store-url');

				if (ownership === url) {
					containers.push(container);
					parts.push($(this).serialize());
				}
			});

			$.post(url, parts.join('&'), 'json')
				.success(function () {
					$(containers).
						removeClass('modified').
						removeClass('unsaved').
						each( function () {
							var $input = $("input:first, select:first option:selected", this), newVal, newHtml;
							if ($input.length) {
								var html = $.trim($(this).data("saved_html").replace(" ", " "));
								var oldVal = $.trim($(this).data("saved_text"));
								if ($input.is("input[type=checkbox]")) {
									if ($input.prop("checked")) {
										newVal = tr("Yes");
									} else {
										newVal = tr("No");
									}
								} else if ($input.is("input[type=hidden]")) { // js datetime picker
									newVal = $("#" + $input.attr("id") + "_dptxt").val();
								} else if ($input.is("option")) {
									newVal = $input.text();
								} else {
									newVal = $input.val();
								}
								if (html) {
									newHtml = html.replace(new RegExp(oldVal.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&")), $.trim(newVal));
								} else {
									newHtml = $.trim(newVal);
								}
								if (!newHtml) {
									newHtml = " ";
								}
								$(this).removeClass("loaded").html(newHtml);
							}
						});
				})
				.error(function () {
					$(containers).filter('.modified').addClass('unsaved');
					$.getJSON($.service('object', 'report_error'));
				})
				;
		});
	};

	$(document).on('click', '.editable-inline:not(.loaded)', function () {
		var container = this
			, url = $(this).data('field-fetch-url')
			;

		$(container).
			addClass('loaded').
			data("saved_html", $(container).html()).
			data("saved_text", $(container).text());

		if (url) {
			$.get(url)
				.success(function (data) {
					$(container).html(data);
					if (jqueryTiki.chosen) {
						var $select = $("select", container);
						if ($select.length) {
							$select.tiki("chosen");
						}
					}
				})
				.error(function () {
					$(container).addClass('failure');
				})
				;
		}
	});

	$(document).on('change', '.editable-inline.loaded :input', function () {
		var container, executor;

		container = $(this).closest('.editable-inline')[0];
		executor = obtainExecutor(container);
		$(container).addClass('modified');

		executor();
	});
})(jQuery);
<|endoftext|>


<commit_before>var fs = require('fs');
var express = require('express');
var router = express.Router();
var crypto = require('crypto');
var mongoose = require('mongoose');
var db = require('./db.js');
var fct = require("./functions.js");
var atob = require('atob')

router.get('/', function(req, res) {
	login=req.get("X-API-Login");
	db.User.findOne({login:login}, function(err,user) {
		if (!user) {
			return res.send({success:false});
		} else {

			if (fct.isHashValid(req,user)) {
				return res.send({success:true,pictures:user.pictures});
			} else {
				return res.send({success:false});
			}

		}
	});
});

router.post('/', function(req, res) {

	login=req.get("X-API-Login");
	db.User.findOne({login:login}, function(err,user) {
		if (!user) {
			return res.send({success:false});
		} else {
			if (fct.isHashValid(req,user)) {

				var id = crypto.randomBytes(20).toString('hex');

				fs.writeFileSync("data/"+id+".jpg", new Buffer(req.body.picture, "base64"));

				var lat=Number(req.body.gps.la_D)+Number(req.body.gps.la_M)/60+Number(req.body.gps.la_S)/3600;
				var lon=Number(req.body.gps.lo_D)+Number(req.body.gps.lo_M)/60+Number(req.body.gps.lo_S)/3600;

				pic = new db.Picture({
					hash:id,
					date:req.body.date,
					gps: {
						latitude:lat,
						longitude:lon,
						latitudeRef:req.body.gps.N_S,
						//longitudeRef:req.body.gps.W_E,
						longitudeRef:'E',
					}
				});

				user.pictures.push(pic);
				user.save();

				return res.send({success:true,id:id});

			} else {
				return res.send({success:false});
			}
		}
	});

});

module.exports = router;

<commit_msg>Remove unnecessary dependency<commit_after>var fs = require('fs');
var express = require('express');
var router = express.Router();
var crypto = require('crypto');
var mongoose = require('mongoose');
var db = require('./db.js');
var fct = require("./functions.js");

router.get('/', function(req, res) {
	login=req.get("X-API-Login");
	db.User.findOne({login:login}, function(err,user) {
		if (!user) {
			return res.send({success:false});
		} else {

			if (fct.isHashValid(req,user)) {
				return res.send({success:true,pictures:user.pictures});
			} else {
				return res.send({success:false});
			}

		}
	});
});

router.post('/', function(req, res) {

	login=req.get("X-API-Login");
	db.User.findOne({login:login}, function(err,user) {
		if (!user) {
			return res.send({success:false});
		} else {
			if (fct.isHashValid(req,user)) {

				var id = crypto.randomBytes(20).toString('hex');

				fs.writeFileSync("data/"+id+".jpg", new Buffer(req.body.picture, "base64"));

				var lat=Number(req.body.gps.la_D)+Number(req.body.gps.la_M)/60+Number(req.body.gps.la_S)/3600;
				var lon=Number(req.body.gps.lo_D)+Number(req.body.gps.lo_M)/60+Number(req.body.gps.lo_S)/3600;

				pic = new db.Picture({
					hash:id,
					date:req.body.date,
					gps: {
						latitude:lat,
						longitude:lon,
						latitudeRef:req.body.gps.N_S,
						//longitudeRef:req.body.gps.W_E,
						longitudeRef:'E',
					}
				});

				user.pictures.push(pic);
				user.save();

				return res.send({success:true,id:id});

			} else {
				return res.send({success:false});
			}
		}
	});

});

module.exports = router;

<|endoftext|>


<commit_before>/**
 * Pretty Header
 * @version 0.1.0
 * @author Vladimir Kuznetsov <mistakster@gmail.com>
 */
(function (root, factory) {
	if (typeof define === 'function' && define.amd) {
		define([], factory);
	} else if (typeof module === 'object' && module.exports) {
		module.exports = factory();
	} else {
		root.makePrettyHeader = factory();
	}
}(this, function () {

	return function makePrettyHeader($ele, options) {
		var i, j, k, x;
		var $span = $('');
		var text = $.trim($ele.text());
		$ele.html($span.text(text));
		var lineHeight = parseInt($span.css('line-height'));
		var height = parseInt($span.css('height'));
		var lines = Math.round(height / lineHeight);
		if (lines <= 1) {
			return;
		}

		var maxWidth = $ele.width();
		var words = text.split(/\s+/);
		$span.html('' + words.join('  ') + '');

		var wordSpans = $span.find('> span').addClass(options.nonBreakableCls);
		var wordWidths = $.map(wordSpans, function (wordSpan) {
			return $(wordSpan).width();
		});

		var variations = [];

		function sum(arr) {
			var result = 0;
			for (var i = 0; i < arr.length; i++) {
				result += arr[i];
			}
			return result;
		}

		function computeVariant(dividers) {
			var width = [];
			var i, x, lines;
			dividers.push(words.length);
			lines = dividers.length;
			for (x = 0, i = 0; i < lines; i++) {
				width.push(sum(wordWidths.slice(x, dividers[i])));
				x = dividers[i];
			}
			var diff = Math.max.apply(null, width) - Math.min.apply(null, width);
			return {
				dividers: dividers,
				diff: diff
			};
		}

		var dividers = [];
		for (i = 1; i < lines; i++) {
			dividers.push(i);
		}

		while (dividers[0] < words.length - 1) {
			variations.push(computeVariant(dividers.slice()));
			x = lines - 2;
			for (j = x; j >= 0; j--) {
				dividers[j]++;
				if (j < lines - 1) {
					for (k = j + 1; k < lines - 1; k++) {
						dividers[k] = dividers[k - 1] + 1;
					}
				}
				if (dividers[j] <= words.length + 1 + j - lines) {
					break;
				}
			}
		}

		// j will contain the index of the most optimal variant
		for (x = Infinity, i = 0; i < variations.length; i++) {
			if (variations[i].diff < x) {
				x = variations[i].diff;
				j = i;
			}
		}

		$span.addClass(options.nonBreakableCls);
		var $lines = [$span];
		for (i = 1; i < lines; i++) {
			$ele.append(' ');
			$lines.push($('').addClass(options.nonBreakableCls).appendTo($ele));
		}

		for (x = 0, i = 0; i < lines; i++) {
			$lines[i].html(words.slice(x, variations[j].dividers[i]).join(' '));
			x = variations[j].dividers[i];
		}

	};

}));
<commit_msg>added jQuery plugin initializer<commit_after>/**
 * Pretty Header
 * @version 0.1.0
 * @author Vladimir Kuznetsov <mistakster@gmail.com>
 */
(function (root, factory) {
	if (typeof define === 'function' && define.amd) {
		define([], factory);
	} else if (typeof module === 'object' && module.exports) {
		module.exports = factory();
	} else if (typeof jQuery !== 'undefined' && typeof jQuery.fn !== 'undefined') {
		(function (makePrettyHeader) {
			jQuery.fn.prettyHeader = function (options) {
				return this.each(function () {
					makePrettyHeader(this, options);
				});
			};
		})(factory());
	} else {
		root.makePrettyHeader = factory();
	}
}(this, function () {

	return function makePrettyHeader($ele, options) {
		var i, j, k, x;
		var $span = $('');
		var text = $.trim($ele.text());
		$ele.html($span.text(text));
		var lineHeight = parseInt($span.css('line-height'));
		var height = parseInt($span.css('height'));
		var lines = Math.round(height / lineHeight);
		if (lines <= 1) {
			return;
		}

		var maxWidth = $ele.width();
		var words = text.split(/\s+/);
		$span.html('' + words.join('  ') + '');

		var wordSpans = $span.find('> span').addClass(options.nonBreakableCls);
		var wordWidths = $.map(wordSpans, function (wordSpan) {
			return $(wordSpan).width();
		});

		var variations = [];

		function sum(arr) {
			var result = 0;
			for (var i = 0; i < arr.length; i++) {
				result += arr[i];
			}
			return result;
		}

		function computeVariant(dividers) {
			var width = [];
			var i, x, lines;
			dividers.push(words.length);
			lines = dividers.length;
			for (x = 0, i = 0; i < lines; i++) {
				width.push(sum(wordWidths.slice(x, dividers[i])));
				x = dividers[i];
			}
			var diff = Math.max.apply(null, width) - Math.min.apply(null, width);
			return {
				dividers: dividers,
				diff: diff
			};
		}

		var dividers = [];
		for (i = 1; i < lines; i++) {
			dividers.push(i);
		}

		while (dividers[0] < words.length - 1) {
			variations.push(computeVariant(dividers.slice()));
			x = lines - 2;
			for (j = x; j >= 0; j--) {
				dividers[j]++;
				if (j < lines - 1) {
					for (k = j + 1; k < lines - 1; k++) {
						dividers[k] = dividers[k - 1] + 1;
					}
				}
				if (dividers[j] <= words.length + 1 + j - lines) {
					break;
				}
			}
		}

		// j will contain the index of the most optimal variant
		for (x = Infinity, i = 0; i < variations.length; i++) {
			if (variations[i].diff < x) {
				x = variations[i].diff;
				j = i;
			}
		}

		$span.addClass(options.nonBreakableCls);
		var $lines = [$span];
		for (i = 1; i < lines; i++) {
			$ele.append(' ');
			$lines.push($('').addClass(options.nonBreakableCls).appendTo($ele));
		}

		for (x = 0, i = 0; i < lines; i++) {
			$lines[i].html(words.slice(x, variations[j].dividers[i]).join(' '));
			x = variations[j].dividers[i];
		}

	};

}));
<|endoftext|>


<commit_before>const EventDispatcher = Jymfony.Component.EventDispatcher.EventDispatcher;
const FunctionControllerResolver = Jymfony.Component.HttpFoundation.Controller.FunctionControllerResolver;
const BadRequestException = Jymfony.Component.HttpFoundation.Exception.BadRequestException;
const ContentType = Jymfony.Component.HttpFoundation.Header.ContentType;
const Request = Jymfony.Component.HttpFoundation.Request;
const Response = Jymfony.Component.HttpFoundation.Response;
const Event = Jymfony.Component.HttpServer.Event;
const EventListener = Jymfony.Component.HttpServer.EventListener;
const RequestHandler = Jymfony.Component.HttpServer.RequestHandler;
const Router = Jymfony.Component.Routing.Router;
const FunctionLoader = Jymfony.Component.Routing.Loader.FunctionLoader;

const Readable = require('stream').Readable;
const url = require('url');

/**
 * @memberOf Jymfony.Component.HttpServer.Serverless
 */
class AwsLambdaHandler extends RequestHandler {
    /**
     * Creates a new Http server instance.
     *
     * @param {Jymfony.Component.Routing.RouteCollection} routes
     * @param {Jymfony.Component.Logger.LoggerInterface} logger
     *
     * @returns {Jymfony.Component.HttpServer.Serverless.AwsLambdaHandler}
     */
    static create(routes, logger = undefined) {
        const router = new Router(new FunctionLoader(), () => routes);
        const eventDispatcher = new EventDispatcher();
        eventDispatcher.addSubscriber(new EventListener.RouterListener(router));
        eventDispatcher.addSubscriber(new EventListener.ExceptionListener(
            (request) => {
                return new Response(JSON.stringify(request.attributes.get('exception')), Response.HTTP_OK, {
                    'content-type': 'application/json',
                });
            })
        );

        const server = new __self(eventDispatcher, new FunctionControllerResolver(logger));
        if (logger !== undefined) {
            server.setLogger(logger);
        }

        return server;
    }

    /**
     * Handles an incoming request from the http server.
     *
     * @param {APIGatewayProxyEvent|ALBEvent} event
     * @param {Context} context
     *
     * @returns {Promise<APIGatewayProxyResult|ALBResult>}
     */
    async handleEvent(event, context) {
        try {
            return await this._handleRequest(event, context);
        } catch (e) {
            this._logger.error('Error while processing request: ' + e.message, {
                exception: e,
                event,
                context,
            });

            const response = {
                statusCode: 500,
                headers: {
                    'Content-type': 'text/plain',
                },
                body: 'Unknown error while handling your request.',
            };

            if (event.requestContext.elb) {
                response.statusDescription = '500 Internal Server Error';
            }

            return response;
        }
    }

    /**
     * Converts an IncomingMessage to an HttpFoundation request
     * and sends it to the Kernel.
     *
     * @param {APIGatewayProxyEvent|ALBEvent} event
     * @param {Context} context
     *
     * @returns {Promise<APIGatewayProxyResult|ALBResult>}
     *
     * @protected
     */
    async _handleRequest(event, context) { // eslint-disable-line no-unused-vars
        const headers = new Jymfony.Component.HttpFoundation.HeaderBag(event.headers || {});
        const normalizedHeaders = headers.keys.reduce((res, key) => (res[key] = headers.get(key), res), {});
        const contentType = new ContentType(headers.get('content-type', 'application/x-www-form-urlencoded'));

        let requestParams, content;
        try {
            [ requestParams, content ] = await this._parseRequestContent(event, normalizedHeaders, contentType);
        } catch (e) {
            const response = {
                statusCode: 500,
                headers: {
                    'Content-type': 'text/plain',
                },
            };

            if (e instanceof BadRequestException) {
                response.statusCode = 400;
            }

            if (event.requestContext.elb) {
                response.statusDescription = response.statusCode + ' ' + (Response.statusTexts[response.statusCode] || 'Unknown');
            }

            return response;
        }

        const requestUrl = url.format({ pathname: event.path, query: event.queryStringParameters });
        const request = new Request(requestUrl, requestParams, {}, event.headers || {}, {
            'REQUEST_METHOD': event.httpMethod,
            'REMOTE_ADDR': event.requestContext.identity.sourceIp,
            'SCHEME': this._getScheme(headers.all),
            'SERVER_NAME': headers.get('Host'),
            'SERVER_PORT': headers.get('x-forwarded-port'),
            'SERVER_PROTOCOL': 'HTTP/1.1',
        }, content);

        let response = await this.handle(request);
        if (response instanceof Promise) {
            response = await response;
        }

        await response.prepare(request);

        content = null;
        if (! response.isEmpty && response.content) {
            content = isFunction(response.content) ? await response.content(new __jymfony.StreamBuffer()) : response.content;
        }

        const responseHeaders = {};
        for (const hdr of response.headers.keys) {
            responseHeaders[hdr] = response.headers.get(hdr);
        }

        const result = {
            statusCode: response.statusCode,
            headers: responseHeaders,
            body: content,
        };

        if (event.requestContext.elb) {
            result.statusDescription = result.statusCode + ' ' + (Response.statusTexts[result.statusCode] || 'Unknown');
        }

        const postResponseEvent = new Event.PostResponseEvent(this, request, response);
        await this._dispatcher.dispatch(Event.HttpServerEvents.TERMINATE, postResponseEvent);

        return result;
    }

    /**
     * @inheritdoc
     */
    async _parseRequestContent(request, headers, contentType) {
        if (headers['sec-websocket-key']) {
            return [ {}, undefined ];
        }

        const stream = new Readable();
        stream.push(request.isBase64Encoded ? atob(request.body) : request.body);
        stream.push(null);

        return await super._parseRequestContent(stream, headers, contentType);
    }

    /**
     * @inheritdoc
     */
    _getScheme(headers) {
        if (headers['cloudfront-forwarded-proto']) {
            return headers['cloudfront-forwarded-proto'][0];
        }

        return super._getScheme(headers);
    }
}

module.exports = AwsLambdaHandler;
<commit_msg>[HttpServer] compute content length if not passed by api gateway<commit_after>const EventDispatcher = Jymfony.Component.EventDispatcher.EventDispatcher;
const FunctionControllerResolver = Jymfony.Component.HttpFoundation.Controller.FunctionControllerResolver;
const BadRequestException = Jymfony.Component.HttpFoundation.Exception.BadRequestException;
const ContentType = Jymfony.Component.HttpFoundation.Header.ContentType;
const Request = Jymfony.Component.HttpFoundation.Request;
const Response = Jymfony.Component.HttpFoundation.Response;
const Event = Jymfony.Component.HttpServer.Event;
const EventListener = Jymfony.Component.HttpServer.EventListener;
const RequestHandler = Jymfony.Component.HttpServer.RequestHandler;
const Router = Jymfony.Component.Routing.Router;
const FunctionLoader = Jymfony.Component.Routing.Loader.FunctionLoader;

const Readable = require('stream').Readable;
const url = require('url');

/**
 * @memberOf Jymfony.Component.HttpServer.Serverless
 */
class AwsLambdaHandler extends RequestHandler {
    /**
     * Creates a new Http server instance.
     *
     * @param {Jymfony.Component.Routing.RouteCollection} routes
     * @param {Jymfony.Component.Logger.LoggerInterface} logger
     *
     * @returns {Jymfony.Component.HttpServer.Serverless.AwsLambdaHandler}
     */
    static create(routes, logger = undefined) {
        const router = new Router(new FunctionLoader(), () => routes);
        const eventDispatcher = new EventDispatcher();
        eventDispatcher.addSubscriber(new EventListener.RouterListener(router));
        eventDispatcher.addSubscriber(new EventListener.ExceptionListener(
            (request) => {
                return new Response(JSON.stringify(request.attributes.get('exception')), Response.HTTP_OK, {
                    'content-type': 'application/json',
                });
            })
        );

        const server = new __self(eventDispatcher, new FunctionControllerResolver(logger));
        if (logger !== undefined) {
            server.setLogger(logger);
        }

        return server;
    }

    /**
     * Handles an incoming request from the http server.
     *
     * @param {APIGatewayProxyEvent|ALBEvent} event
     * @param {Context} context
     *
     * @returns {Promise<APIGatewayProxyResult|ALBResult>}
     */
    async handleEvent(event, context) {
        try {
            return await this._handleRequest(event, context);
        } catch (e) {
            this._logger.error('Error while processing request: ' + e.message, {
                exception: e,
                event,
                context,
            });

            const response = {
                statusCode: 500,
                headers: {
                    'Content-type': 'text/plain',
                },
                body: 'Unknown error while handling your request.',
            };

            if (event.requestContext.elb) {
                response.statusDescription = '500 Internal Server Error';
            }

            return response;
        }
    }

    /**
     * Converts an IncomingMessage to an HttpFoundation request
     * and sends it to the Kernel.
     *
     * @param {APIGatewayProxyEvent|ALBEvent} event
     * @param {Context} context
     *
     * @returns {Promise<APIGatewayProxyResult|ALBResult>}
     *
     * @protected
     */
    async _handleRequest(event, context) { // eslint-disable-line no-unused-vars
        const headers = new Jymfony.Component.HttpFoundation.HeaderBag(event.headers || {});
        const normalizedHeaders = headers.keys.reduce((res, key) => (res[key] = headers.get(key), res), {});
        const contentType = new ContentType(headers.get('content-type', 'application/x-www-form-urlencoded'));

        let requestParams, content;
        try {
            [ requestParams, content ] = await this._parseRequestContent(event, normalizedHeaders, contentType);
        } catch (e) {
            const response = {
                statusCode: 500,
                headers: {
                    'Content-type': 'text/plain',
                },
            };

            if (e instanceof BadRequestException) {
                response.statusCode = 400;
            }

            if (event.requestContext.elb) {
                response.statusDescription = response.statusCode + ' ' + (Response.statusTexts[response.statusCode] || 'Unknown');
            }

            return response;
        }

        const requestUrl = url.format({ pathname: event.path, query: event.queryStringParameters });
        const request = new Request(requestUrl, requestParams, {}, event.headers || {}, {
            'REQUEST_METHOD': event.httpMethod,
            'REMOTE_ADDR': event.requestContext.identity.sourceIp,
            'SCHEME': this._getScheme(headers.all),
            'SERVER_NAME': headers.get('Host'),
            'SERVER_PORT': headers.get('x-forwarded-port'),
            'SERVER_PROTOCOL': 'HTTP/1.1',
        }, content);

        let response = await this.handle(request);
        if (response instanceof Promise) {
            response = await response;
        }

        await response.prepare(request);

        content = null;
        if (! response.isEmpty && response.content) {
            content = isFunction(response.content) ? await response.content(new __jymfony.StreamBuffer()) : response.content;
        }

        const responseHeaders = {};
        for (const hdr of response.headers.keys) {
            responseHeaders[hdr] = response.headers.get(hdr);
        }

        const result = {
            statusCode: response.statusCode,
            headers: responseHeaders,
            body: content,
        };

        if (event.requestContext.elb) {
            result.statusDescription = result.statusCode + ' ' + (Response.statusTexts[result.statusCode] || 'Unknown');
        }

        const postResponseEvent = new Event.PostResponseEvent(this, request, response);
        await this._dispatcher.dispatch(Event.HttpServerEvents.TERMINATE, postResponseEvent);

        return result;
    }

    /**
     * @inheritdoc
     */
    async _parseRequestContent(request, headers, contentType) {
        if (headers['sec-websocket-key']) {
            return [ {}, undefined ];
        }

        const body = request.isBase64Encoded ? atob(request.body) : request.body;
        headers['content-length'] = headers['content-length'] || body.length;

        const stream = new Readable();
        stream.push(body);
        stream.push(null);

        return await super._parseRequestContent(stream, headers, contentType);
    }

    /**
     * @inheritdoc
     */
    _getScheme(headers) {
        if (headers['cloudfront-forwarded-proto']) {
            return headers['cloudfront-forwarded-proto'][0];
        }

        return super._getScheme(headers);
    }
}

module.exports = AwsLambdaHandler;
<|endoftext|>


<commit_before>/*
 *
 * (c) Copyright Ascensio System SIA 2010-2020
 *
 * This program is a free software product. You can redistribute it and/or
 * modify it under the terms of the GNU Affero General Public License (AGPL)
 * version 3 as published by the Free Software Foundation. In accordance with
 * Section 7(a) of the GNU AGPL its Section 15 shall be amended to the effect
 * that Ascensio System SIA expressly excludes the warranty of non-infringement
 * of any third-party rights.
 *
 * This program is distributed WITHOUT ANY WARRANTY; without even the implied
 * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR  PURPOSE. For
 * details, see the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
 *
 * You can contact Ascensio System SIA at 20A-12 Ernesta Birznieka-Upisha
 * street, Riga, Latvia, EU, LV-1050.
 *
 * The  interactive user interfaces in modified source and object code versions
 * of the Program must display Appropriate Legal Notices, as required under
 * Section 5 of the GNU AGPL version 3.
 *
 * Pursuant to Section 7(b) of the License you must retain the original Product
 * logo when distributing the program. Pursuant to Section 7(e) we decline to
 * grant you any rights under trademark law for use of our trademarks.
 *
 * All the Product's GUI elements, including illustrations and icon sets, as
 * well as technical writing content are licensed under the terms of the
 * Creative Commons Attribution-ShareAlike 4.0 International. See the License
 * terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
 *
 */
/**
 *  TextareaField.js
 *
 *  Created by Julia Radzhabova on 29/09/20
 *  Copyright (c) 2020 Ascensio System SIA. All rights reserved.
 *
 */

if (Common === undefined)
    var Common = {};

define([
    'common/main/lib/component/BaseView',
    'common/main/lib/component/Tooltip'
], function () { 'use strict';

    Common.UI.TextareaField = Common.UI.BaseView.extend((function() {
        return {
            options : {
                id          : null,
                cls         : '',
                style       : '',
                value       : '',
                maxlength   : undefined,
                placeHolder : '',
                spellcheck  : false,
                disabled: false
            },

            template: _.template([
                '<div class="textarea-field" style="<%= style %>">',
                    '<textarea ',
                    'spellcheck="<%= spellcheck %>" ',
                    'class="form-control <%= cls %>" ',
                    'placeholder="<%= placeHolder %>" ',
                    '></textarea>',
                '
'
            ].join('')),

            initialize : function(options) {
                Common.UI.BaseView.prototype.initialize.call(this, options);

                var me = this;

                this.id             = me.options.id || Common.UI.getId();
                this.cls            = me.options.cls;
                this.style          = me.options.style;
                this.value          = me.options.value;
                this.placeHolder    = me.options.placeHolder;
                this.template       = me.options.template || me.template;
                this.disabled       = me.options.disabled;
                this.spellcheck     = me.options.spellcheck;
                this.maxLength      = me.options.maxLength;

                me.rendered         = me.options.rendered || false;

                if (me.options.el) {
                    me.render();
                }
            },

            render : function(parentEl) {
                var me = this;

                if (!me.rendered) {
                    this.cmpEl = $(this.template({
                        id          : this.id,
                        cls         : this.cls,
                        style       : this.style,
                        placeHolder : this.placeHolder,
                        spellcheck  : this.spellcheck,
                        scope       : me
                    }));

                    if (parentEl) {
                        this.setElement(parentEl, false);
                        parentEl.html(this.cmpEl);
                    } else {
                        this.$el.html(this.cmpEl);
                    }
                } else {
                    this.cmpEl = this.$el;
                }

                if (!me.rendered) {
                    var el = this.cmpEl;

                    this._input = this.cmpEl.find('textarea').addBack().filter('textarea');
                    this._input.on('blur',   _.bind(this.onInputChanged, this));
                    this._input.on('keydown',    _.bind(this.onKeyDown, this));
                    if (this.maxLength) this._input.attr('maxlength', this.maxLength);

                    if (this.disabled)
                        this.setDisabled(this.disabled);
                }

                me.rendered = true;

                return this;
            },

            _doChange: function(e, extra) {
                // skip processing for internally-generated synthetic event
                // to avoid double processing
                if (extra && extra.synthetic)
                    return;

                var newValue = $(e.target).val(),
                    oldValue = this.value;

                this.trigger('changed:before', this, newValue, oldValue, e);

                if (e.isDefaultPrevented())
                    return;

                this.value = newValue;

                // trigger changed event
                this.trigger('changed:after', this, newValue, oldValue, e);
            },

            onInputChanged: function(e, extra) {
                this._doChange(e, extra);
            },

            onKeyDown: function(e) {
                this.trigger('keydown:before', this, e);

                if (e.isDefaultPrevented())
                    return;

                if (e.keyCode === Common.UI.Keys.RETURN)
                    this._doChange(e);
                if (e.keyCode == Common.UI.Keys.ESC)
                    this.setValue(this.value);
                if (e.keyCode==Common.UI.Keys.RETURN || e.keyCode==Common.UI.Keys.ESC)
                    this.trigger('inputleave', this);
            },

            setDisabled: function(disabled) {
                this.disabled = disabled;
                $(this.el).toggleClass('disabled', disabled);
                disabled
                    ? this._input.attr('disabled', true)
                    : this._input.removeAttr('disabled');
            },

            isDisabled: function() {
                return this.disabled;
            },

            setValue: function(value) {
                this.value = value;

                if (this.rendered){
                    this._input.val(value);
                }
            },

            getValue: function() {
                return this.value;
            },

            focus: function() {
                this._input.focus();
            }
        }
    })());
});
<commit_msg>Don't apply settings when press Enter on textarea<commit_after>/*
 *
 * (c) Copyright Ascensio System SIA 2010-2020
 *
 * This program is a free software product. You can redistribute it and/or
 * modify it under the terms of the GNU Affero General Public License (AGPL)
 * version 3 as published by the Free Software Foundation. In accordance with
 * Section 7(a) of the GNU AGPL its Section 15 shall be amended to the effect
 * that Ascensio System SIA expressly excludes the warranty of non-infringement
 * of any third-party rights.
 *
 * This program is distributed WITHOUT ANY WARRANTY; without even the implied
 * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR  PURPOSE. For
 * details, see the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
 *
 * You can contact Ascensio System SIA at 20A-12 Ernesta Birznieka-Upisha
 * street, Riga, Latvia, EU, LV-1050.
 *
 * The  interactive user interfaces in modified source and object code versions
 * of the Program must display Appropriate Legal Notices, as required under
 * Section 5 of the GNU AGPL version 3.
 *
 * Pursuant to Section 7(b) of the License you must retain the original Product
 * logo when distributing the program. Pursuant to Section 7(e) we decline to
 * grant you any rights under trademark law for use of our trademarks.
 *
 * All the Product's GUI elements, including illustrations and icon sets, as
 * well as technical writing content are licensed under the terms of the
 * Creative Commons Attribution-ShareAlike 4.0 International. See the License
 * terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
 *
 */
/**
 *  TextareaField.js
 *
 *  Created by Julia Radzhabova on 29/09/20
 *  Copyright (c) 2020 Ascensio System SIA. All rights reserved.
 *
 */

if (Common === undefined)
    var Common = {};

define([
    'common/main/lib/component/BaseView',
    'common/main/lib/component/Tooltip'
], function () { 'use strict';

    Common.UI.TextareaField = Common.UI.BaseView.extend((function() {
        return {
            options : {
                id          : null,
                cls         : '',
                style       : '',
                value       : '',
                maxlength   : undefined,
                placeHolder : '',
                spellcheck  : false,
                disabled: false
            },

            template: _.template([
                '<div class="textarea-field" style="<%= style %>">',
                    '<textarea ',
                    'spellcheck="<%= spellcheck %>" ',
                    'class="form-control <%= cls %>" ',
                    'placeholder="<%= placeHolder %>" ',
                    '></textarea>',
                '
'
            ].join('')),

            initialize : function(options) {
                Common.UI.BaseView.prototype.initialize.call(this, options);

                var me = this;

                this.id             = me.options.id || Common.UI.getId();
                this.cls            = me.options.cls;
                this.style          = me.options.style;
                this.value          = me.options.value;
                this.placeHolder    = me.options.placeHolder;
                this.template       = me.options.template || me.template;
                this.disabled       = me.options.disabled;
                this.spellcheck     = me.options.spellcheck;
                this.maxLength      = me.options.maxLength;

                me.rendered         = me.options.rendered || false;

                if (me.options.el) {
                    me.render();
                }
            },

            render : function(parentEl) {
                var me = this;

                if (!me.rendered) {
                    this.cmpEl = $(this.template({
                        id          : this.id,
                        cls         : this.cls,
                        style       : this.style,
                        placeHolder : this.placeHolder,
                        spellcheck  : this.spellcheck,
                        scope       : me
                    }));

                    if (parentEl) {
                        this.setElement(parentEl, false);
                        parentEl.html(this.cmpEl);
                    } else {
                        this.$el.html(this.cmpEl);
                    }
                } else {
                    this.cmpEl = this.$el;
                }

                if (!me.rendered) {
                    var el = this.cmpEl;

                    this._input = this.cmpEl.find('textarea').addBack().filter('textarea');
                    this._input.on('blur',   _.bind(this.onInputChanged, this));
                    this._input.on('keydown',    _.bind(this.onKeyDown, this));
                    if (this.maxLength) this._input.attr('maxlength', this.maxLength);

                    if (this.disabled)
                        this.setDisabled(this.disabled);
                }

                me.rendered = true;

                return this;
            },

            _doChange: function(e, extra) {
                // skip processing for internally-generated synthetic event
                // to avoid double processing
                if (extra && extra.synthetic)
                    return;

                var newValue = $(e.target).val(),
                    oldValue = this.value;

                this.trigger('changed:before', this, newValue, oldValue, e);

                if (e.isDefaultPrevented())
                    return;

                this.value = newValue;

                // trigger changed event
                this.trigger('changed:after', this, newValue, oldValue, e);
            },

            onInputChanged: function(e, extra) {
                this._doChange(e, extra);
            },

            onKeyDown: function(e) {
                this.trigger('keydown:before', this, e);

                if (e.isDefaultPrevented())
                    return;

                if (e.keyCode === Common.UI.Keys.RETURN) {
                    e.stopPropagation();
                }
                if (e.keyCode == Common.UI.Keys.ESC)
                    this.setValue(this.value);
                if (e.keyCode==Common.UI.Keys.ESC)
                    this.trigger('inputleave', this);
            },

            setDisabled: function(disabled) {
                this.disabled = disabled;
                $(this.el).toggleClass('disabled', disabled);
                disabled
                    ? this._input.attr('disabled', true)
                    : this._input.removeAttr('disabled');
            },

            isDisabled: function() {
                return this.disabled;
            },

            setValue: function(value) {
                this.value = value;

                if (this.rendered){
                    this._input.val(value);
                }
            },

            getValue: function() {
                return this.value;
            },

            focus: function() {
                this._input.focus();
            }
        }
    })());
});
<|endoftext|>


<commit_before>import { GMusicNamespace } from 'gmusic.js';
import Album from './Structs/Album';
import Artist from './Structs/Artist';
import Playlist from './Structs/Playlist';

import { findContextPath } from './utils/context';
import scrollToPlaySong from './utils/scrollToPlaySong';

export default class LibraryNamespace extends GMusicNamespace {
  constructor(...args) {
    super(...args);
    this.tracks = [];
    this.path = findContextPath();

    if (this.path) {
      this._watchTracks();
    }

    this.addMethod('getTracks', this.getTracks.bind(this));
    this.addMethod('getAlbums', this.getAlbums.bind(this));
    this.addMethod('getArtists', this.getArtists.bind(this));
    this.addMethod('getLibrary', this.getLibrary.bind(this));
    this.addMethod('playAlbum', this.playAlbum.bind(this));
    this.addMethod('playTrackFromAlbum', this.playTrackFromAlbum.bind(this));
  }

  _watchTracks() {
    const that = this;

    let tracks = this.getTracks();
    window.APPCONTEXT[this.path[0]][this.path[1]][0].addEventListener('E', () => {
      const newTracks = this.getTracks();
      let changed = false;
      for (let i = 0; i < newTracks.length; i++) {
        if (!tracks[i]) {
          changed = true; break;
        }
        if (newTracks[i].id !== tracks[i].id) {
          changed = true; break;
        }
      }
      tracks = newTracks;
      if (changed) that.emit('change:library', this.getLibrary());
    });
  }

  getLibrary() {
    return {
      tracks: this.getTracks(),
      artists: this.getArtists(),
      albums: this.getAlbums(),
    };
  }

  getTracks() {
    return Playlist.fromPlaylistObject('_', window.APPCONTEXT[this.path[0]][this.path[1]][0][this.path[2]].all).tracks;
  }

  getArtists() {
    const artists = {};
    this.getAlbums().forEach((album) => {
      if (album.artist === 'Various Artists') return;
      const artistId = album.tracks[0].artistId;
      if (!artists[artistId]) artists[artistId] = new Artist(artistId, album.artist, album.tracks[0].artistImage);
      artists[artistId].albums.push(album);
    });
    return Object.keys(artists).map((artistId) => {
      artists[artistId].albums.sort((a, b) => b.name.localeCompare(a.name));
      return artists[artistId];
    });
  }

  getAlbums() {
    const albums = {};
    this.getTracks().forEach((track) => {
      const uniq = `${track.albumArt}_${track.album}`;
      albums[uniq] = albums[uniq] || new Album(track.albumId, track.album, track.albumArtist || track.artist, track.albumArt);
      if (track.albumArtist) {
        albums[uniq].isAlbumArtist = true;
      }
      if (!track.albumArtist && albums[uniq].artist !== track.artist) {
        albums[uniq].artist = 'Various Artists';
      }
      albums[uniq].tracks.push(track);
    });
    return Object.keys(albums).map((albumID) => {
      albums[albumID].tracks.sort((a, b) => b.index - a.index);
      return albums[albumID];
    });
  }

  _navigateToAlbum(album) {
    return new Promise((resolve, reject) => {
      let timer;
      let checker;
      const clearTimers = () => {
        clearTimeout(timer);
        clearInterval(checker);
      };
      timer = setTimeout(() => {
        clearTimers();
        reject(`Timed out while loading album - ${album.name}`);
      }, 4000);
      checker = setInterval(() => {
        const elem = document.querySelector('.material-album-container .title-text');
        if (elem && elem.innerText.trim() === album.name) {
          clearTimers();
          resolve();
        }
      }, 10);
      window.location.hash = `/album/${album.id}`;
    });
  }

  playAlbum(album) {
    return this._navigateToAlbum(album)
      .then(() => {
        const button = document.querySelector('.material-album-container [data-id=play]');
        if (button) button.click();
      });
  }

  playTrack(track) {
    return this._navigateToAlbum({
      id: track.albumId,
      name: track.album
    })
      .then(() => scrollToPlaySong(track));
  }
}
<commit_msg>Fix linting issues<commit_after>import { GMusicNamespace } from 'gmusic.js';
import Album from './Structs/Album';
import Artist from './Structs/Artist';
import Playlist from './Structs/Playlist';

import { findContextPath } from './utils/context';
import scrollToPlaySong from './utils/scrollToPlaySong';

export default class LibraryNamespace extends GMusicNamespace {
  constructor(...args) {
    super(...args);
    this.tracks = [];
    this.path = findContextPath();

    if (this.path) {
      this._watchTracks();
    }

    this.addMethod('getTracks', this.getTracks.bind(this));
    this.addMethod('getAlbums', this.getAlbums.bind(this));
    this.addMethod('getArtists', this.getArtists.bind(this));
    this.addMethod('getLibrary', this.getLibrary.bind(this));
    this.addMethod('playAlbum', this.playAlbum.bind(this));
    this.addMethod('playTrackFromAlbum', this.playTrackFromAlbum.bind(this));
  }

  _watchTracks() {
    const that = this;

    let tracks = this.getTracks();
    window.APPCONTEXT[this.path[0]][this.path[1]][0].addEventListener('E', () => {
      const newTracks = this.getTracks();
      let changed = false;
      for (let i = 0; i < newTracks.length; i++) {
        if (!tracks[i]) {
          changed = true; break;
        }
        if (newTracks[i].id !== tracks[i].id) {
          changed = true; break;
        }
      }
      tracks = newTracks;
      if (changed) that.emit('change:library', this.getLibrary());
    });
  }

  getLibrary() {
    return {
      tracks: this.getTracks(),
      artists: this.getArtists(),
      albums: this.getAlbums(),
    };
  }

  getTracks() {
    return Playlist.fromPlaylistObject('_', window.APPCONTEXT[this.path[0]][this.path[1]][0][this.path[2]].all).tracks;
  }

  getArtists() {
    const artists = {};
    this.getAlbums().forEach((album) => {
      if (album.artist === 'Various Artists') return;
      const artistId = album.tracks[0].artistId;
      if (!artists[artistId]) artists[artistId] = new Artist(artistId, album.artist, album.tracks[0].artistImage);
      artists[artistId].albums.push(album);
    });
    return Object.keys(artists).map((artistId) => {
      artists[artistId].albums.sort((a, b) => b.name.localeCompare(a.name));
      return artists[artistId];
    });
  }

  getAlbums() {
    const albums = {};
    this.getTracks().forEach((track) => {
      const uniq = `${track.albumArt}_${track.album}`;
      albums[uniq] = albums[uniq] || new Album(track.albumId, track.album, track.albumArtist || track.artist, track.albumArt);
      if (track.albumArtist) {
        albums[uniq].isAlbumArtist = true;
      }
      if (!track.albumArtist && albums[uniq].artist !== track.artist) {
        albums[uniq].artist = 'Various Artists';
      }
      albums[uniq].tracks.push(track);
    });
    return Object.keys(albums).map((albumID) => {
      albums[albumID].tracks.sort((a, b) => b.index - a.index);
      return albums[albumID];
    });
  }

  _navigateToAlbum(album) {
    return new Promise((resolve, reject) => {
      let timer;
      let checker;
      const clearTimers = () => {
        clearTimeout(timer);
        clearInterval(checker);
      };
      timer = setTimeout(() => {
        clearTimers();
        reject(`Timed out while loading album - ${album.name}`);
      }, 4000);
      checker = setInterval(() => {
        const elem = document.querySelector('.material-album-container .title-text');
        if (elem && elem.innerText.trim() === album.name) {
          clearTimers();
          resolve();
        }
      }, 10);
      window.location.hash = `/album/${album.id}`;
    });
  }

  playAlbum(album) {
    return this._navigateToAlbum(album)
      .then(() => {
        const button = document.querySelector('.material-album-container [data-id=play]');
        if (button) button.click();
      });
  }

  playTrack(track) {
    return this._navigateToAlbum({
      id: track.albumId,
      name: track.album,
    })
      .then(() => scrollToPlaySong(track));
  }
}
<|endoftext|>


<commit_before>// ==========================================================================
//                            DG.CellAxisView
//
//  Author:   William Finzer
//
//  Copyright (c) 2014 by The Concord Consortium, Inc. All rights reserved.
//
//  Licensed under the Apache License, Version 2.0 (the "License");
//  you may not use this file except in compliance with the License.
//  You may obtain a copy of the License at
//
//    http://www.apache.org/licenses/LICENSE-2.0
//
//  Unless required by applicable law or agreed to in writing, software
//  distributed under the License is distributed on an "AS IS" BASIS,
//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//  See the License for the specific language governing permissions and
//  limitations under the License.
// ==========================================================================

sc_require('components/graph/axes/axis_view');

/** @class  DG.CellAxisView - The view for a graph axis that displays cells.

 @extends DG.AxisView
 */
DG.CellAxisView = DG.AxisView.extend( (function() {
  var kTickLength = 4,
      kAxisGap = 2;

  return {
    /** @scope DG.CellAxisView.prototype */

    /**
     Number of pixels required from the axis line to the edge of the graph view
     @property { Number }
     */
    desiredExtent: function() {
      var tExtent = sc_super();
      if( !this.get('isNumeric')) { // Not yet handling numeric axis broken up by cells
        tExtent += kTickLength + kAxisGap + this.get('maxLabelExtent');
      }
      return tExtent;
    }.property('maxLabelExtent'),

    /**
     I'm not supposed to work with numbers.
     @property { Boolean }
     */
    isNumeric: false,

    /**
     * @property {Boolean}
     */
    centering: false,

    /**
     * @property { Object { cellBeingDragged: {Integer}, position: {Number}}}
     */
    dragInfo: null,

    /**
     @property{Number}
     */
    axisLineCoordinate: function() {
      var tCoord;
      switch( this.get('orientation')) {
        case 'vertical':
          tCoord = this.get('drawWidth');
          break;
        case 'vertical2':
        case 'horizontal':
          tCoord = 0;
          break;
      }
      return tCoord;
    }.property('drawWidth'),

    /**
     @property {Number} Each time we draw the axis, we set this property to the maximum width
     of the label strings. Note that this will change depending not only on the length of the
     strings but on the rotation of the labels.
     */
    maxLabelExtent: 0,

    /**
     This is the main backbone of the axis.
     @return {Raphael element}
     */
    renderAxisLine: function() {
      var tCoord = this.get('axisLineCoordinate'),
          tPixelMin = this.get('pixelMin'),
          tPixelMax = this.get('pixelMax'),
          tStart, tStop;
      switch( this.get('orientation')) {
        case 'vertical':
          tStart = { x: tCoord - 1, y: tPixelMin };
          tStop = { x: tCoord - 1, y: tPixelMax };
          break;
        case 'horizontal':
          tStart = { x: tPixelMin, y: tCoord + 1 };
          tStop = { x: tPixelMax, y: tCoord + 1 };
          break;
        case 'vertical2':
          tStart = { x: tCoord + 1, y: tPixelMin };
          tStop = { x: tCoord + 1, y: tPixelMax };
          break;
      }
      return this._paper.line( tStart.x, tStart.y, tStop.x, tStop.y)
        .attr( { stroke: DG.PlotUtilities.kAxisColor,
          strokeWidth: 2 });
    },

    /**
     The convention is that, unlike continuous axes, for a
     vertical axis, the low numbers are at the top.
     @param {Number} in screen coordinates
     @return {Number} the cell number corresponding to the given screen coordinate
     */
    whichCell: function( iCoord) {
      var tNumCells = this.getPath('model.numberOfCells'),
          tCell = 0;
      if( tNumCells > 1) {
        var tPixelMin = this.get('pixelMin'),
            tPixelMax = this.get('pixelMax');
        // In order to do the computation, we have to adjust for the coordinates of the ends
        // of the axes, knowing the thePixel is in plot view coordinates, not axis view coordinates.
        if( this.get('isVertical')) {
          iCoord = Math.min( Math.max( tPixelMax + 1, iCoord), tPixelMin - 1);
          tCell = Math.floor( (iCoord - tPixelMax) * tNumCells / ( tPixelMin - tPixelMax));
        }
        else {  // horizontal
          iCoord = Math.max( Math.min( tPixelMax - tPixelMin - 1, iCoord), 1.0);
          tCell = Math.floor( iCoord * tNumCells / ( tPixelMax - tPixelMin));
        }

        //      KCP_ASSERT( (tCell >= 0) && (tCell < tNumCells));
      }
      return tCell;
    },

    /**
     Override base class to account for drag in progress
     @return {Number} coordinate of the given cell.
     */
    cellToCoordinate: function (iCellNum, iIgnoreDragging) {
      var tDragInfo = this.get('dragInfo');
      if( !iIgnoreDragging && tDragInfo && tDragInfo.draggingInProgress && iCellNum === tDragInfo.cellBeingDragged) {
        return tDragInfo.position;
      }
      else {
        return sc_super();
      }
    },

    /**
     Since I'm a leaf class I implement doDraw.
     */
    doDraw: function doDraw() {
      var this_ = this,
          tModel = this.get('model'),
          tNumCells = tModel.get('numberOfCells'),
          tBaseline = this_.get('axisLineCoordinate'),
          tOrientation = this.get('orientation'),
          tRotation = (tOrientation === 'horizontal') ? 0 : -90, // default to parallel to axis
          tCursorClass = (tOrientation === 'horizontal') ? 'axis-cell-label-x' : 'axis-cell-label-y',
          tMaxHeight = DG.RenderingUtilities.kDefaultFontHeight,  // So there will be a default extent
          tCentering = this.get('centering'),
          tTickOffset = tCentering ? 0 : this.get('fullCellWidth') / 2,
          tAnchor = tCentering ? 'middle' : 'start',
          tMaxWidth = tMaxHeight,
          tLabelSpecs = this.get('labelSpecs') || [],
          tCollision = false,
          tPrevLabelEnd,
          tDragStartCoord, tCellBeingDragged, tOriginalCellIndex;

      var beginDrag = function ( iWindowX, iWindowY) {
            tOriginalCellIndex = tCellBeingDragged = this.cellNum;
            tDragStartCoord = DG.ViewUtilities.windowToViewCoordinates( { x: iWindowX, y: iWindowY }, this_);
            tDragStartCoord = (tOrientation === 'horizontal') ? tDragStartCoord.x : tDragStartCoord.y;
          },
          doDrag = function (iDeltaX, iDeltaY, iWindowX, iWindowY) {
            var tModel = this_.get('model'),
                tCurrentCoord = tDragStartCoord + ((tOrientation === 'horizontal') ? iDeltaX : iDeltaY),
                tCategoryInCurrentCell = this_.whichCell( tCurrentCoord);
            SC.run(function() {
              if( tCategoryInCurrentCell !== tCellBeingDragged) {
                tModel.swapCategoriesByIndex( tCellBeingDragged, tCategoryInCurrentCell);
                tCellBeingDragged = tCategoryInCurrentCell;
              }
              this_.set('dragInfo', {
                cellBeingDragged: tCellBeingDragged, position: tCurrentCoord,
                draggingInProgress: true
              });
              this_.displayDidChange();
              this_.propertyDidChange('categoriesDragged');
            });
          },
          endDrag = function ( iEvent) {
            SC.run(function() {
              this_.set('dragInfo', null);
              this_.displayDidChange();
              this_.propertyDidChange('categoriesDragged');
              this_.updateLayerIfNeeded();
            });
          };

      function measureOneCell( iCellNum, iCellName) {
        var tCoord = this_.cellToCoordinate(iCellNum);
        if( !isFinite( tCoord)) {
          return;
        }
        var tTextElement;
        if( !tLabelSpecs[iCellNum]) {
          tTextElement = this_._paper.text(0, 0, iCellName)
              .addClass('axis-tick-label')
              .addClass(tCursorClass)
              .drag(doDrag, beginDrag, endDrag);
        }
        else {
          tTextElement = tLabelSpecs[ iCellNum].element;
          tTextElement.attr('text', iCellName);
        }
        var tTextExtent = DG.RenderingUtilities.getExtentForTextElement(
                          tTextElement, DG.RenderingUtilities.kDefaultFontHeight, true /* compute with no transform */);

        tTextElement.cellNum = iCellNum;
        tLabelSpecs[iCellNum] = { element: tTextElement, coord: tCoord,
                      height: tTextExtent.height, width: tTextExtent.width };

        tMaxHeight = Math.max( tMaxHeight, tTextExtent.height);
        tMaxWidth = Math.max( tMaxWidth, tTextExtent.width);
        if(SC.none( tPrevLabelEnd))
          tCollision = tTextExtent.width > this_.get('fullCellWidth');
        else
          tCollision = tCollision || (tCoord - tTextExtent.width / 2 < tPrevLabelEnd);
        tPrevLabelEnd = (tCoord + tTextExtent.width / 2);
      } // measureOneCell

      // iLabelSpec has form { element: {Raphael element}, coord: {Number}, height: {Number}, width: {Number} }
      function drawOneCell( iLabelSpec, iIndex) {
        var tCoord = iLabelSpec.coord,
            tLabelX, tLabelY;
        switch( this_.get('orientation')) {
          case 'vertical':
          case 'vertical2':
            this_._elementsToClear.push(
              this_._paper.line( tBaseline, tCoord + tTickOffset, tBaseline - kTickLength, tCoord + tTickOffset)
                .attr( { stroke: DG.PlotUtilities.kAxisColor }));
            tLabelX = tBaseline - kTickLength - kAxisGap - iLabelSpec.height / 3;
            tLabelY = tCoord + tTickOffset;
            if( tRotation === 0) {
              tAnchor = 'end';
            }
            break;

          case 'horizontal':
            this_._elementsToClear.push(
              this_._paper.line( tCoord - tTickOffset, tBaseline, tCoord - tTickOffset, tBaseline + kTickLength)
                .attr( { stroke: DG.PlotUtilities.kAxisColor }));
            tLabelX = tCoord - tTickOffset + 1;
            tLabelY = tBaseline + kTickLength + kAxisGap + iLabelSpec.height / 3;
            if( tRotation === -90) {
              tAnchor = 'end';
              if( iIndex === 0)
                tLabelX += iLabelSpec.height / 3;
            }
            break;
        }
        iLabelSpec.element.attr( { x: tLabelX, y: tLabelY, 'text-anchor': tAnchor });
        DG.RenderingUtilities.rotateText( iLabelSpec.element, tRotation, tLabelX, tLabelY);
      } // drawOneCell

      //==========Main body of doDraw==================
      // Special case for short labels
      if( this.getPath('model.maxCellNameLength') <= 3)
        tRotation = 0;

      this._elementsToClear.push( this.renderAxisLine());

      tModel.forEachCellDo( measureOneCell);
      while( tLabelSpecs.length > tNumCells) {
        var tSpec = tLabelSpecs.pop();
        tSpec.element.remove();
      }
      if( tCollision) // labels must be perpendicular to axis
        tRotation = (tOrientation === 'horizontal') ? -90 : 0;
      tLabelSpecs.forEach( drawOneCell);

      this.set('labelSpecs', tLabelSpecs);

      this.renderLabel();
      // By changing maxLabelExtent we can trigger notification that causes the graph to re-layout
      // axes and plot if needed.
      this.setIfChanged('maxLabelExtent',
            (tOrientation === 'horizontal') ?
              ((tRotation === 0) ? tMaxHeight : tMaxWidth) :  // horizontal
              ((tRotation === 0) ? tMaxWidth : tMaxHeight));  // vertical
    }

  };
}())
);

<commit_msg>On iPad, and presumably any touch device, the window x and y coordinates provided during a drag are NaN. For now, prevent a crash.<commit_after>// ==========================================================================
//                            DG.CellAxisView
//
//  Author:   William Finzer
//
//  Copyright (c) 2014 by The Concord Consortium, Inc. All rights reserved.
//
//  Licensed under the Apache License, Version 2.0 (the "License");
//  you may not use this file except in compliance with the License.
//  You may obtain a copy of the License at
//
//    http://www.apache.org/licenses/LICENSE-2.0
//
//  Unless required by applicable law or agreed to in writing, software
//  distributed under the License is distributed on an "AS IS" BASIS,
//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//  See the License for the specific language governing permissions and
//  limitations under the License.
// ==========================================================================

sc_require('components/graph/axes/axis_view');

/** @class  DG.CellAxisView - The view for a graph axis that displays cells.

 @extends DG.AxisView
 */
DG.CellAxisView = DG.AxisView.extend( (function() {
  var kTickLength = 4,
      kAxisGap = 2;

  return {
    /** @scope DG.CellAxisView.prototype */

    /**
     Number of pixels required from the axis line to the edge of the graph view
     @property { Number }
     */
    desiredExtent: function() {
      var tExtent = sc_super();
      if( !this.get('isNumeric')) { // Not yet handling numeric axis broken up by cells
        tExtent += kTickLength + kAxisGap + this.get('maxLabelExtent');
      }
      return tExtent;
    }.property('maxLabelExtent'),

    /**
     I'm not supposed to work with numbers.
     @property { Boolean }
     */
    isNumeric: false,

    /**
     * @property {Boolean}
     */
    centering: false,

    /**
     * @property { Object { cellBeingDragged: {Integer}, position: {Number}}}
     */
    dragInfo: null,

    /**
     @property{Number}
     */
    axisLineCoordinate: function() {
      var tCoord;
      switch( this.get('orientation')) {
        case 'vertical':
          tCoord = this.get('drawWidth');
          break;
        case 'vertical2':
        case 'horizontal':
          tCoord = 0;
          break;
      }
      return tCoord;
    }.property('drawWidth'),

    /**
     @property {Number} Each time we draw the axis, we set this property to the maximum width
     of the label strings. Note that this will change depending not only on the length of the
     strings but on the rotation of the labels.
     */
    maxLabelExtent: 0,

    /**
     This is the main backbone of the axis.
     @return {Raphael element}
     */
    renderAxisLine: function() {
      var tCoord = this.get('axisLineCoordinate'),
          tPixelMin = this.get('pixelMin'),
          tPixelMax = this.get('pixelMax'),
          tStart, tStop;
      switch( this.get('orientation')) {
        case 'vertical':
          tStart = { x: tCoord - 1, y: tPixelMin };
          tStop = { x: tCoord - 1, y: tPixelMax };
          break;
        case 'horizontal':
          tStart = { x: tPixelMin, y: tCoord + 1 };
          tStop = { x: tPixelMax, y: tCoord + 1 };
          break;
        case 'vertical2':
          tStart = { x: tCoord + 1, y: tPixelMin };
          tStop = { x: tCoord + 1, y: tPixelMax };
          break;
      }
      return this._paper.line( tStart.x, tStart.y, tStop.x, tStop.y)
        .attr( { stroke: DG.PlotUtilities.kAxisColor,
          strokeWidth: 2 });
    },

    /**
     The convention is that, unlike continuous axes, for a
     vertical axis, the low numbers are at the top.
     @param {Number} in screen coordinates
     @return {Number} the cell number corresponding to the given screen coordinate
     */
    whichCell: function( iCoord) {
      var tNumCells = this.getPath('model.numberOfCells'),
          tCell = 0;
      if( tNumCells > 1) {
        var tPixelMin = this.get('pixelMin'),
            tPixelMax = this.get('pixelMax');
        // In order to do the computation, we have to adjust for the coordinates of the ends
        // of the axes, knowing the thePixel is in plot view coordinates, not axis view coordinates.
        if( this.get('isVertical')) {
          iCoord = Math.min( Math.max( tPixelMax + 1, iCoord), tPixelMin - 1);
          tCell = Math.floor( (iCoord - tPixelMax) * tNumCells / ( tPixelMin - tPixelMax));
        }
        else {  // horizontal
          iCoord = Math.max( Math.min( tPixelMax - tPixelMin - 1, iCoord), 1.0);
          tCell = Math.floor( iCoord * tNumCells / ( tPixelMax - tPixelMin));
        }

        //      KCP_ASSERT( (tCell >= 0) && (tCell < tNumCells));
      }
      return tCell;
    },

    /**
     Override base class to account for drag in progress
     @return {Number} coordinate of the given cell.
     */
    cellToCoordinate: function (iCellNum, iIgnoreDragging) {
      var tDragInfo = this.get('dragInfo');
      if( !iIgnoreDragging && tDragInfo && tDragInfo.draggingInProgress && iCellNum === tDragInfo.cellBeingDragged) {
        return tDragInfo.position;
      }
      else {
        return sc_super();
      }
    },

    /**
     Since I'm a leaf class I implement doDraw.
     */
    doDraw: function doDraw() {
      var this_ = this,
          tModel = this.get('model'),
          tNumCells = tModel.get('numberOfCells'),
          tBaseline = this_.get('axisLineCoordinate'),
          tOrientation = this.get('orientation'),
          tRotation = (tOrientation === 'horizontal') ? 0 : -90, // default to parallel to axis
          tCursorClass = (tOrientation === 'horizontal') ? 'axis-cell-label-x' : 'axis-cell-label-y',
          tMaxHeight = DG.RenderingUtilities.kDefaultFontHeight,  // So there will be a default extent
          tCentering = this.get('centering'),
          tTickOffset = tCentering ? 0 : this.get('fullCellWidth') / 2,
          tAnchor = tCentering ? 'middle' : 'start',
          tMaxWidth = tMaxHeight,
          tLabelSpecs = this.get('labelSpecs') || [],
          tCollision = false,
          tPrevLabelEnd,
          tDragStartCoord, tCellBeingDragged, tOriginalCellIndex;

      var beginDrag = function ( iWindowX, iWindowY) {
            tOriginalCellIndex = tCellBeingDragged = this.cellNum;
            tDragStartCoord = DG.ViewUtilities.windowToViewCoordinates( { x: iWindowX, y: iWindowY }, this_);
            tDragStartCoord = (tOrientation === 'horizontal') ? tDragStartCoord.x : tDragStartCoord.y;
          },
          doDrag = function (iDeltaX, iDeltaY, iWindowX, iWindowY) {
            var tModel = this_.get('model'),
                tCurrentCoord = tDragStartCoord + ((tOrientation === 'horizontal') ? iDeltaX : iDeltaY),
                tCategoryInCurrentCell = this_.whichCell( tCurrentCoord);
            // Todo Touch is currently returning NaN for window coordinates. Fix this
            if( isNaN(tCurrentCoord))
                return;
            SC.run(function() {
              if( tCategoryInCurrentCell !== tCellBeingDragged) {
                tModel.swapCategoriesByIndex( tCellBeingDragged, tCategoryInCurrentCell);
                tCellBeingDragged = tCategoryInCurrentCell;
              }
              this_.set('dragInfo', {
                cellBeingDragged: tCellBeingDragged, position: tCurrentCoord,
                draggingInProgress: true
              });
              this_.displayDidChange();
              this_.propertyDidChange('categoriesDragged');
            });
          },
          endDrag = function ( iEvent) {
            SC.run(function() {
              this_.set('dragInfo', null);
              this_.displayDidChange();
              this_.propertyDidChange('categoriesDragged');
              this_.updateLayerIfNeeded();
            });
          };

      function measureOneCell( iCellNum, iCellName) {
        var tCoord = this_.cellToCoordinate(iCellNum);
        if( !isFinite( tCoord)) {
          return;
        }
        var tTextElement;
        if( !tLabelSpecs[iCellNum]) {
          tTextElement = this_._paper.text(0, 0, iCellName)
              .addClass('axis-tick-label')
              .addClass(tCursorClass)
              .drag(doDrag, beginDrag, endDrag);
        }
        else {
          tTextElement = tLabelSpecs[ iCellNum].element;
          tTextElement.attr('text', iCellName);
        }
        var tTextExtent = DG.RenderingUtilities.getExtentForTextElement(
                          tTextElement, DG.RenderingUtilities.kDefaultFontHeight, true /* compute with no transform */);

        tTextElement.cellNum = iCellNum;
        tLabelSpecs[iCellNum] = { element: tTextElement, coord: tCoord,
                      height: tTextExtent.height, width: tTextExtent.width };

        tMaxHeight = Math.max( tMaxHeight, tTextExtent.height);
        tMaxWidth = Math.max( tMaxWidth, tTextExtent.width);
        if(SC.none( tPrevLabelEnd))
          tCollision = tTextExtent.width > this_.get('fullCellWidth');
        else
          tCollision = tCollision || (tCoord - tTextExtent.width / 2 < tPrevLabelEnd);
        tPrevLabelEnd = (tCoord + tTextExtent.width / 2);
      } // measureOneCell

      // iLabelSpec has form { element: {Raphael element}, coord: {Number}, height: {Number}, width: {Number} }
      function drawOneCell( iLabelSpec, iIndex) {
        var tCoord = iLabelSpec.coord,
            tLabelX, tLabelY;
        switch( this_.get('orientation')) {
          case 'vertical':
          case 'vertical2':
            this_._elementsToClear.push(
              this_._paper.line( tBaseline, tCoord + tTickOffset, tBaseline - kTickLength, tCoord + tTickOffset)
                .attr( { stroke: DG.PlotUtilities.kAxisColor }));
            tLabelX = tBaseline - kTickLength - kAxisGap - iLabelSpec.height / 3;
            tLabelY = tCoord + tTickOffset;
            if( tRotation === 0) {
              tAnchor = 'end';
            }
            break;

          case 'horizontal':
            this_._elementsToClear.push(
              this_._paper.line( tCoord - tTickOffset, tBaseline, tCoord - tTickOffset, tBaseline + kTickLength)
                .attr( { stroke: DG.PlotUtilities.kAxisColor }));
            tLabelX = tCoord - tTickOffset + 1;
            tLabelY = tBaseline + kTickLength + kAxisGap + iLabelSpec.height / 3;
            if( tRotation === -90) {
              tAnchor = 'end';
              if( iIndex === 0)
                tLabelX += iLabelSpec.height / 3;
            }
            break;
        }
        iLabelSpec.element.attr( { x: tLabelX, y: tLabelY, 'text-anchor': tAnchor });
        DG.RenderingUtilities.rotateText( iLabelSpec.element, tRotation, tLabelX, tLabelY);
      } // drawOneCell

      //==========Main body of doDraw==================
      // Special case for short labels
      if( this.getPath('model.maxCellNameLength') <= 3)
        tRotation = 0;

      this._elementsToClear.push( this.renderAxisLine());

      tModel.forEachCellDo( measureOneCell);
      while( tLabelSpecs.length > tNumCells) {
        var tSpec = tLabelSpecs.pop();
        tSpec.element.remove();
      }
      if( tCollision) // labels must be perpendicular to axis
        tRotation = (tOrientation === 'horizontal') ? -90 : 0;
      tLabelSpecs.forEach( drawOneCell);

      this.set('labelSpecs', tLabelSpecs);

      this.renderLabel();
      // By changing maxLabelExtent we can trigger notification that causes the graph to re-layout
      // axes and plot if needed.
      this.setIfChanged('maxLabelExtent',
            (tOrientation === 'horizontal') ?
              ((tRotation === 0) ? tMaxHeight : tMaxWidth) :  // horizontal
              ((tRotation === 0) ? tMaxWidth : tMaxHeight));  // vertical
    }

  };
}())
);

<|endoftext|>