<commit_before>;(function($){
/*
**
 * jqGrid addons using jQuery UI
 * Author: Mark Williams
 * Dual licensed under the MIT and GPL licenses:
 * http://www.opensource.org/licenses/mit-license.php
 * http://www.gnu.org/licenses/gpl.html
 * depends on jQuery UI sortable
**/
if ($.browser.msie && $.browser.version==8) {
	$.expr[":"].hidden = function(elem) {
		return elem.offsetWidth === 0 || elem.offsetHeight === 0 ||
			elem.style.display == "none";
	}
}

$.jgrid.extend({
	sortableColumns : function (tblrow)
	{
		return this.each(function (){
			ts = this;
			function start() {ts.p.disableClick = true;};
			var sortable_opts = {
				"tolerance" : "pointer",
				"axis" : "x",
				"items": '>th:not(:has(#jqgh_cb,#jqgh_rn,#jqgh_subgrid),:hidden)',
				"placeholder": {
					element: function(item) {
						var el = $(document.createElement(item[0].nodeName))
						.addClass(item[0].className+" ui-sortable-placeholder ui-state-highlight")
						.removeClass("ui-sortable-helper")[0];
						return el;
					},
					update: function(self, p) {
						p.height(self.currentItem.innerHeight() - parseInt(self.currentItem.css('paddingTop')||0, 10) - parseInt(self.currentItem.css('paddingBottom')||0, 10));
						p.width(self.currentItem.innerWidth() - parseInt(self.currentItem.css('paddingLeft')||0, 10) - parseInt(self.currentItem.css('paddingRight')||0, 10));
					}
				},
				"update": function(event, ui) {
					var p = $(ui.item).parent();
					var th = $(">th", p);
					var colModel = ts.p.colModel;
					var cmMap = {};
					$.each(colModel, function(i) { cmMap[this.name]=i });
					var permutation = [];
					th.each(function(i) {
						var id = $(">div", this).get(0).id.replace(/^jqgh_/, "");
							if (id in cmMap) {
								permutation.push(cmMap[id]);
							}
					});

					$(ts).jqGrid("remapColumns",permutation, true, true);
					if ($.isFunction(ts.p.sortable.update)) {
						ts.p.sortable.update(permutation);
					}
					setTimeout(function(){ts.p.disableClick=false}, 50);
				}
			};
			if (ts.p.sortable.options) {
				$.extend(sortable_opts, ts.p.sortable.options);
			} else if ($.isFunction(ts.p.sortable)) {
				ts.p.sortable = { "update" : ts.p.sortable };
			}
			if (sortable_opts.start) {
				var s = sortable_opts.start;
				sortable_opts.start = function(e,ui) {
					start();
					s.call(this,e,ui);
				}
			} else {
				sortable_opts.start = start;
			}
			if (ts.p.sortable.exclude) {
				sortable_opts.items += ":not("+ts.p.sortable.exclude+")";
			}
			tblrow.sortable(sortable_opts).data("sortable").floating = true;
		});
	},
    columnChooser : function(opts) {
        var self = this;
        var selector = $('

<select multiple="multiple"></select>

');
        var select = $('select', selector);

        opts = $.extend({
            "title" : "Select columns",
            "width" : 420,
            "height" : 240,
            "classname" : null,
            "done" : function(perm) { if (perm) self.jqGrid("remapColumns", perm, true) },
            "ok" : "Ok",
            "cancel" : "Cancel",
            /* msel is either the name of a ui widget class that
               extends a multiselect, or a function that supports
               creating a multiselect object (with no argument,
               or when passed an object), and destroying it (when
               passed the string "destroy"). */
            "msel" : "multiselect",
            /* "msel_opts" : {}, */

            /* dlog is either the name of a ui widget class that
               behaves in a dialog-like way, or a function, that
               supports creating a dialog (when passed dlog_opts)
               or destroying a dialog (when passed the string
               "destroy")
               */
            "dlog" : "dialog",

            /* dlog_opts is either an option object to be passed
               to "dlog", or (more likely) a function that creates
               the options object.
               The default produces a suitable options object for
               ui.dialog */
            "dlog_opts" : function(opts) {
                var buttons = {};
                buttons[opts.ok] = function() {
                    opts.apply_perm();
                    opts.cleanup(false);
                };
                buttons[opts.cancel] = function() {
                    opts.cleanup(true);
                };
                return {
                    "buttons": buttons,
                    "close": function() {
                        opts.cleanup(true);
                    },
                    "resizable": false,
                    "width": opts.width+20
                };
            },
            /* Function to get the permutation array, and pass it to the
               "done" function */
            "apply_perm" : function() {
                $('option',select).each(function(i) {
                    if (this.selected) {
                        self.jqGrid("showCol", colModel[this.value].name);
                    } else {
                        self.jqGrid("hideCol", colModel[this.value].name);
                    }
                });

                var perm = fixedCols.slice(0);
                $('option[selected]',select).each(function() { perm.push(parseInt(this.value)) });
                $.each(perm, function() { delete colMap[colModel[this].name] });
                $.each(colMap, function() { perm.push(this) });
                if (opts.done) {
                    opts.done.call(self, perm);
                }
            },
            /* Function to cleanup the dialog, and select. Also calls the
               done function with no permutation (to indicate that the
               columnChooser was aborted */
            "cleanup" : function(calldone) {
                call(opts.dlog, selector, 'destroy');
                call(opts.msel, select, 'destroy');
                selector.remove();
                if (calldone && opts.done) {
                    opts.done.call(self);
                }
            }
        }, opts || {});

        if (opts.title) {
            selector.attr("title", opts.title);
        }
        if (opts.classname) {
            selector.addClass(classname);
            select.addClass(classname);
        }
        if (opts.width) {
            $(">div",selector).css({"width": opts.width,"margin":"0 auto"});
            select.css("width", opts.width);
        }
        if (opts.height) {
            $(">div",selector).css("height", opts.height);
            select.css("height", opts.height - 10);
        }
        var colModel = self.jqGrid("getGridParam", "colModel");
        var colNames = self.jqGrid("getGridParam", "colNames");
        var colMap = {}, fixedCols = [];

        select.empty();
        $.each(colModel, function(i) {
            colMap[this.name] = i;
            if (this.name == "cb" || this.name == "rn" || this.name == "subgrid") {
                fixedCols.push(i);
                return;
            }
            if (this.hidedlg) return;

            var opt = "<option value='"+i+"'";
            if (!this.hidden) {
                opt += " selected";
            }
            opt += ">"+colNames[i]+"</option>";
            select.append(opt);
        });

        function call(fn, obj) {
            if (!fn) return;
            if (typeof fn == 'string') {
                if ($.fn[fn]) {
                    $.fn[fn].apply(obj, $.makeArray(arguments).slice(2));
                }
            } else if ($.isFunction(fn)) {
                fn.apply(obj, $.makeArray(arguments).slice(2));
            }
        }

        var dopts = $.isFunction(opts.dlog_opts) ? opts.dlog_opts.call(self, opts) : opts.dlog_opts;
        call(opts.dlog, selector, dopts);
        var mopts = $.isFunction(opts.msel_opts) ? opts.msel_opts.call(self, opts) : opts.msel_opts;
        call(opts.msel, select, opts.msel_opts);
    }
})
})(jQuery);<commit_msg>Fix bug handling hidedlg+visible columns<commit_after>;(function($){
/*
**
 * jqGrid addons using jQuery UI
 * Author: Mark Williams
 * Dual licensed under the MIT and GPL licenses:
 * http://www.opensource.org/licenses/mit-license.php
 * http://www.gnu.org/licenses/gpl.html
 * depends on jQuery UI sortable
**/
if ($.browser.msie && $.browser.version==8) {
	$.expr[":"].hidden = function(elem) {
		return elem.offsetWidth === 0 || elem.offsetHeight === 0 ||
			elem.style.display == "none";
	}
}

$.jgrid.extend({
	sortableColumns : function (tblrow)
	{
		return this.each(function (){
			ts = this;
			function start() {ts.p.disableClick = true;};
			var sortable_opts = {
				"tolerance" : "pointer",
				"axis" : "x",
				"items": '>th:not(:has(#jqgh_cb,#jqgh_rn,#jqgh_subgrid),:hidden)',
				"placeholder": {
					element: function(item) {
						var el = $(document.createElement(item[0].nodeName))
						.addClass(item[0].className+" ui-sortable-placeholder ui-state-highlight")
						.removeClass("ui-sortable-helper")[0];
						return el;
					},
					update: function(self, p) {
						p.height(self.currentItem.innerHeight() - parseInt(self.currentItem.css('paddingTop')||0, 10) - parseInt(self.currentItem.css('paddingBottom')||0, 10));
						p.width(self.currentItem.innerWidth() - parseInt(self.currentItem.css('paddingLeft')||0, 10) - parseInt(self.currentItem.css('paddingRight')||0, 10));
					}
				},
				"update": function(event, ui) {
					var p = $(ui.item).parent();
					var th = $(">th", p);
					var colModel = ts.p.colModel;
					var cmMap = {};
					$.each(colModel, function(i) { cmMap[this.name]=i });
					var permutation = [];
					th.each(function(i) {
						var id = $(">div", this).get(0).id.replace(/^jqgh_/, "");
							if (id in cmMap) {
								permutation.push(cmMap[id]);
							}
					});

					$(ts).jqGrid("remapColumns",permutation, true, true);
					if ($.isFunction(ts.p.sortable.update)) {
						ts.p.sortable.update(permutation);
					}
					setTimeout(function(){ts.p.disableClick=false}, 50);
				}
			};
			if (ts.p.sortable.options) {
				$.extend(sortable_opts, ts.p.sortable.options);
			} else if ($.isFunction(ts.p.sortable)) {
				ts.p.sortable = { "update" : ts.p.sortable };
			}
			if (sortable_opts.start) {
				var s = sortable_opts.start;
				sortable_opts.start = function(e,ui) {
					start();
					s.call(this,e,ui);
				}
			} else {
				sortable_opts.start = start;
			}
			if (ts.p.sortable.exclude) {
				sortable_opts.items += ":not("+ts.p.sortable.exclude+")";
			}
			tblrow.sortable(sortable_opts).data("sortable").floating = true;
		});
	},
    columnChooser : function(opts) {
        var self = this;
        var selector = $('

<select multiple="multiple"></select>

');
        var select = $('select', selector);

        opts = $.extend({
            "title" : "Select columns",
            "width" : 420,
            "height" : 240,
            "classname" : null,
            "done" : function(perm) { if (perm) self.jqGrid("remapColumns", perm, true) },
            "ok" : "Ok",
            "cancel" : "Cancel",
            /* msel is either the name of a ui widget class that
               extends a multiselect, or a function that supports
               creating a multiselect object (with no argument,
               or when passed an object), and destroying it (when
               passed the string "destroy"). */
            "msel" : "multiselect",
            /* "msel_opts" : {}, */

            /* dlog is either the name of a ui widget class that
               behaves in a dialog-like way, or a function, that
               supports creating a dialog (when passed dlog_opts)
               or destroying a dialog (when passed the string
               "destroy")
               */
            "dlog" : "dialog",

            /* dlog_opts is either an option object to be passed
               to "dlog", or (more likely) a function that creates
               the options object.
               The default produces a suitable options object for
               ui.dialog */
            "dlog_opts" : function(opts) {
                var buttons = {};
                buttons[opts.ok] = function() {
                    opts.apply_perm();
                    opts.cleanup(false);
                };
                buttons[opts.cancel] = function() {
                    opts.cleanup(true);
                };
                return {
                    "buttons": buttons,
                    "close": function() {
                        opts.cleanup(true);
                    },
                    "resizable": false,
                    "width": opts.width+20
                };
            },
            /* Function to get the permutation array, and pass it to the
               "done" function */
            "apply_perm" : function() {
                $('option',select).each(function(i) {
                    if (this.selected) {
                        self.jqGrid("showCol", colModel[this.value].name);
                    } else {
                        self.jqGrid("hideCol", colModel[this.value].name);
                    }
                });

                var perm = fixedCols.slice(0);
                $('option[selected]',select).each(function() { perm.push(parseInt(this.value)) });
                $.each(perm, function() { delete colMap[colModel[this].name] });
                $.each(colMap, function() { perm.push(this) });
                if (opts.done) {
                    opts.done.call(self, perm);
                }
            },
            /* Function to cleanup the dialog, and select. Also calls the
               done function with no permutation (to indicate that the
               columnChooser was aborted */
            "cleanup" : function(calldone) {
                call(opts.dlog, selector, 'destroy');
                call(opts.msel, select, 'destroy');
                selector.remove();
                if (calldone && opts.done) {
                    opts.done.call(self);
                }
            }
        }, opts || {});

        if (opts.title) {
            selector.attr("title", opts.title);
        }
        if (opts.classname) {
            selector.addClass(classname);
            select.addClass(classname);
        }
        if (opts.width) {
            $(">div",selector).css({"width": opts.width,"margin":"0 auto"});
            select.css("width", opts.width);
        }
        if (opts.height) {
            $(">div",selector).css("height", opts.height);
            select.css("height", opts.height - 10);
        }
        var colModel = self.jqGrid("getGridParam", "colModel");
        var colNames = self.jqGrid("getGridParam", "colNames");
        var colMap = {}, fixedCols = [];

        select.empty();
        $.each(colModel, function(i) {
            colMap[this.name] = i;
            if (this.hidedlg) {
                if (!this.hidden) {
                    fixedCols.push(i);
                }
                return;
            }

            var opt = "<option value='"+i+"'";
            if (!this.hidden) {
                opt += " selected";
            }
            opt += ">"+colNames[i]+"</option>";
            select.append(opt);
        });

        function call(fn, obj) {
            if (!fn) return;
            if (typeof fn == 'string') {
                if ($.fn[fn]) {
                    $.fn[fn].apply(obj, $.makeArray(arguments).slice(2));
                }
            } else if ($.isFunction(fn)) {
                fn.apply(obj, $.makeArray(arguments).slice(2));
            }
        }

        var dopts = $.isFunction(opts.dlog_opts) ? opts.dlog_opts.call(self, opts) : opts.dlog_opts;
        call(opts.dlog, selector, dopts);
        var mopts = $.isFunction(opts.msel_opts) ? opts.msel_opts.call(self, opts) : opts.msel_opts;
        call(opts.msel, select, opts.msel_opts);
    }
})
})(jQuery);<|endoftext|>


<commit_before>/* Copyright © 2009 Johan Kiviniemi
 *
 * Permission to use, copy, modify, and/or distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

/*global jQuery*/
(function ($) {

function debug (text) {
  $('
').prependTo ($('#content')).text (text);
}

function ce (name) {
  return $(document.createElement (name));
}

function HeroesScrapeError (message) {
  this.name = 'HeroesScrapeError';
  this.message = message;
}

HeroesScrapeError.prototype.toString = function () {
  return this.name + ': ' + this.message;
};

function pad (num, len) {
  var res = '' + num;

  while (res.length < len) {
    res = '0' + res;
  }

  return res;
}

// Progress indicator.
$(function () {
  $('body').
    ajaxStart (function () { $(this).css ('cursor', 'progress'); }).
    ajaxStop  (function () { $(this).css ('cursor', null);       });

  $('
Loading...
').
    css ({
      'position':    'absolute',
      'top':         0,
      'right':       0,
      'background':  'yellow',
      'color':       'black',
      'font-weight': 'bold'
    }).
    hide ().
    prependTo ($('body')).
    ajaxStart (function () { $(this).show (); }).
    ajaxStop  (function () { $(this).hide (); });
});

// The scraping.

function get_wikipedia_page (title, callback) {
  var uri = 'http://en.wikipedia.org/w/api.php?action=parse&page=' +
            title + '&prop=text&format=json&callback=?';

  $.getJSON (uri, function (data) {
    var tree = $('
').html (data.parse.text['*'])[0];
    callback (tree);
  });
}

var scraped_episodes = false,
    scraped_comics   = false,
    document_ready   = false,
    generated_table  = false,
    list             = [];

function add_item (type, id, title, date, other) {
  if (! /^(?:episode|webisode|comic)$/.exec (type) ||
      typeof id      !== 'string' ||
      typeof title   !== 'string' ||
      typeof date    !== 'string' ||
      ! /^[0-9]{4}-[0-9]{2}-[0-9]{2}$/.exec (date)) {
    throw new HeroesScrapeError ('add_item: Invalid parameters');
  }

  var item = {
    type:  type,
    id:    id,
    title: title,
    date:  date
  };
  $.extend (item, other);
  list.push (item);
}

var sort_type_map = {
  'episode':  0,
  'webisode': 1,
  'comic':    2
};

function sort_list () {
  list.sort (function (a, b) {
    if (a.date !== b.date) {
      return (a.date < b.date) ? -1 : 1;

    } else if (a.type !== b.type) {
      return (sort_type_map[a.type] < sort_type_map[b.type]) ? -1 : 1;

    } else if (a.id !== b.id) {
      return (a.id < b.id) ? -1 : 1;

    } else {
      return 0;
    }
  });

  list.reverse ();
}

function now () {
  var date = (function (offset) {
    var date = new Date (),
        utc  = date.getTime () + 60*1000 * date.getTimezoneOffset ();
    return new Date (utc + 60*60*1000 * offset);
  }) (-5);

  return pad (date.getFullYear (), 4) +
         '-' + pad (date.getMonth () + 1, 2) +
         '-' + pad (date.getDate (), 2);
}

function generate_table () {
  if (! scraped_episodes ||
      ! scraped_comics   ||
      ! document_ready   ||
      generated_table) {
    return;
  }

  generated_table = true;

  sort_list ();

  var today = now ();

  var table = $('
'),
      thead = $('
').appendTo (table),
      tbody = $('
').appendTo (table);

  $('
').
    appendTo (thead).
    append ($('Date	')).
    append ($('Type	')).
    append ($('ID	')).
    append ($('Title	')).
    append ($(' 	'));

  $('#heroes-table-container').
    prepend (table);

  // Generate the table in chunks to avoid hogging the browser for seconds.

  var interval = setInterval (function () {
    for (var i = 0; i < 20; i++) {
      var item = list.shift ();
      if (typeof item === 'undefined') {
        $('#heroes-table-loading').remove ();
        clearInterval (interval);
        return;
      }

      var tr = ce ('tr').appendTo (tbody);

      if (item.date === today) {
        tr.addClass ('today');
      } else if (item.date > today) {
        tr.addClass ('upcoming');
      }

      tr.addClass (item.type);

      var td_date  = ce ('td').addClass ('date').appendTo (tr),
          td_type  = ce ('td').addClass ('type').appendTo (tr),
          td_id    = ce ('td').addClass ('id').appendTo (tr),
          td_title = ce ('td').addClass ('title').appendTo (tr),
          td_misc  = ce ('td').addClass ('misc').appendTo (tr);

      td_date.text (item.date);

      ce ('img').
        attr ({
          'src':   'img/' + item.type + '.png',
          'alt':   item.type,
          'title': item.type,
          'class': 'icon'
        }).
        appendTo (td_type);

      td_id.text (item.id);

      if (item.uri) {
        ce ('a').
          text (item.title).
          attr ('href', item.uri).
          appendTo (td_title);

      } else {
        td_title.text (item.title);
      }

      if (item.wp_uri) {
        ce ('a').
          attr ('href', item.wp_uri).
          append (
            ce ('img').
              attr ({
                'src':   'img/wikipedia.png',
                'alt':   'Wikipedia',
                'title': "\u201c" + item.title + "\u201d in Wikipedia",
                'class': 'icon'
              })
          ).
          appendTo (td_misc);
      }
    }
  }, 1);
}

var month_map = {
  january:    1,
  february:   2,
  march:      3,
  april:      4,
  may:        5,
  june:       6,
  july:       7,
  august:     8,
  september:  9,
  october:   10,
  november:  11,
  december:  12
};

function parse_date (string) {
  var m = /^(\S+) ([0-9]+), ([0-9]+)/.exec (string);

  if (! m || ! month_map[m[1].toLowerCase ()]) {
    throw new HeroesScrapeError ('parse_date: Invalid parameters');
  }

  var year  = + m[3],
      month = month_map[m[1].toLowerCase ()],
      day   = + m[2];

  return pad (year, 4) + '-' + pad (month, 2) + '-' + pad (day, 2);
}

var webisode_id_map = {
  'Going Postal':    'gp',
  'Heroes: Destiny': 'hd',
  'The Recruit':     'tr',
  'Hard Knox':       'hk'
};

var webisode_re = /^(?:Going Postal|Heroes: Destiny|The Recruit|Hard Knox)$/;

function scrape_episodes (tree) {
  $(tree).find ('h3').each (function () {
    var text = $(this).find ('.mw-headline').text ();

    var type,
        id_prefix,
        title_prefix,
        date_col;

    var match = /^Season ([0-9]+)/.exec (text);
    if (match) {
      type         = 'episode';
      id_prefix    = match[1] + 'x'
      title_prefix = '';
      date_col     = 5;

    } else if (webisode_id_map[text]) {
      type         = 'webisode';
      id_prefix    = webisode_id_map[text];
      title_prefix = text + ' \u2013 ';
      date_col     = 4;

    } else {
      return;
    }

    // The first episode of a season.
    var first_ep = null;

    $(this).next ('table.wikitable').find ('tr').each (function () {
      var ep = $(this).find ('td:eq(0)').text ();

      if (! /^[0-9]+$/.exec (ep)) {
        return;
      }

      ep = + ep;
      if (first_ep === null) {
        first_ep = ep;
      }

      var ep_real = 1 + ep - first_ep,
          id      = id_prefix + pad (ep_real, 2),
          title   = title_prefix + $(this).find ('td:eq(1) b').text (),
          wp_uri  = $(this).find ('td:eq(1) b a').attr ('href'),
          date    = parse_date ($(this).find ('td:eq('+date_col+')').text ());

      if (wp_uri) {
        wp_uri = 'http://en.wikipedia.org' + wp_uri;
      }

      add_item (type, id, title, date, { wp_uri: wp_uri });
    });
  });

  scraped_episodes = true;
  generate_table ();
}

function scrape_comics (tree) {
  var bonus = 0;

  $(tree).find ('table.wikitable tr').each (function () {
    var id = $(this).find ('td:eq(0)').text ();

    if (/^[0-9]+$/.exec (id)) {
      // Proceed.

    } else if (/^(?:Bonus|Interactive Novel)/.exec (id)) {
      bonus++;
      id = 'b' + bonus;

    } else {
      return;
    }

    var title = $(this).find ('td:eq(1) a:first').text (),
        uri   = $(this).find ('td:eq(1) a:first').attr ('href'),
        date  = $(this).find ('td:eq(2)').text ();

    add_item ('comic', id, title, date, { uri: uri });
  });

  scraped_comics = true;
  generate_table ();
}

get_wikipedia_page ('List_of_Heroes_episodes',       scrape_episodes);
get_wikipedia_page ('List_of_Heroes_graphic_novels', scrape_comics);

$(function () {
  $('#heroes-table-container').
    empty ().
    append ($('
Loading...
'));

  document_ready = true;

  generate_table ();
});

}) (jQuery);

// vim:set et sw=2 sts=2:
<commit_msg>Missing semicolon<commit_after>/* Copyright © 2009 Johan Kiviniemi
 *
 * Permission to use, copy, modify, and/or distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

/*global jQuery*/
(function ($) {

function debug (text) {
  $('
').prependTo ($('#content')).text (text);
}

function ce (name) {
  return $(document.createElement (name));
}

function HeroesScrapeError (message) {
  this.name = 'HeroesScrapeError';
  this.message = message;
}

HeroesScrapeError.prototype.toString = function () {
  return this.name + ': ' + this.message;
};

function pad (num, len) {
  var res = '' + num;

  while (res.length < len) {
    res = '0' + res;
  }

  return res;
}

// Progress indicator.
$(function () {
  $('body').
    ajaxStart (function () { $(this).css ('cursor', 'progress'); }).
    ajaxStop  (function () { $(this).css ('cursor', null);       });

  $('
Loading...
').
    css ({
      'position':    'absolute',
      'top':         0,
      'right':       0,
      'background':  'yellow',
      'color':       'black',
      'font-weight': 'bold'
    }).
    hide ().
    prependTo ($('body')).
    ajaxStart (function () { $(this).show (); }).
    ajaxStop  (function () { $(this).hide (); });
});

// The scraping.

function get_wikipedia_page (title, callback) {
  var uri = 'http://en.wikipedia.org/w/api.php?action=parse&page=' +
            title + '&prop=text&format=json&callback=?';

  $.getJSON (uri, function (data) {
    var tree = $('
').html (data.parse.text['*'])[0];
    callback (tree);
  });
}

var scraped_episodes = false,
    scraped_comics   = false,
    document_ready   = false,
    generated_table  = false,
    list             = [];

function add_item (type, id, title, date, other) {
  if (! /^(?:episode|webisode|comic)$/.exec (type) ||
      typeof id      !== 'string' ||
      typeof title   !== 'string' ||
      typeof date    !== 'string' ||
      ! /^[0-9]{4}-[0-9]{2}-[0-9]{2}$/.exec (date)) {
    throw new HeroesScrapeError ('add_item: Invalid parameters');
  }

  var item = {
    type:  type,
    id:    id,
    title: title,
    date:  date
  };
  $.extend (item, other);
  list.push (item);
}

var sort_type_map = {
  'episode':  0,
  'webisode': 1,
  'comic':    2
};

function sort_list () {
  list.sort (function (a, b) {
    if (a.date !== b.date) {
      return (a.date < b.date) ? -1 : 1;

    } else if (a.type !== b.type) {
      return (sort_type_map[a.type] < sort_type_map[b.type]) ? -1 : 1;

    } else if (a.id !== b.id) {
      return (a.id < b.id) ? -1 : 1;

    } else {
      return 0;
    }
  });

  list.reverse ();
}

function now () {
  var date = (function (offset) {
    var date = new Date (),
        utc  = date.getTime () + 60*1000 * date.getTimezoneOffset ();
    return new Date (utc + 60*60*1000 * offset);
  }) (-5);

  return pad (date.getFullYear (), 4) +
         '-' + pad (date.getMonth () + 1, 2) +
         '-' + pad (date.getDate (), 2);
}

function generate_table () {
  if (! scraped_episodes ||
      ! scraped_comics   ||
      ! document_ready   ||
      generated_table) {
    return;
  }

  generated_table = true;

  sort_list ();

  var today = now ();

  var table = $('
'),
      thead = $('
').appendTo (table),
      tbody = $('
').appendTo (table);

  $('
').
    appendTo (thead).
    append ($('Date	')).
    append ($('Type	')).
    append ($('ID	')).
    append ($('Title	')).
    append ($(' 	'));

  $('#heroes-table-container').
    prepend (table);

  // Generate the table in chunks to avoid hogging the browser for seconds.

  var interval = setInterval (function () {
    for (var i = 0; i < 20; i++) {
      var item = list.shift ();
      if (typeof item === 'undefined') {
        $('#heroes-table-loading').remove ();
        clearInterval (interval);
        return;
      }

      var tr = ce ('tr').appendTo (tbody);

      if (item.date === today) {
        tr.addClass ('today');
      } else if (item.date > today) {
        tr.addClass ('upcoming');
      }

      tr.addClass (item.type);

      var td_date  = ce ('td').addClass ('date').appendTo (tr),
          td_type  = ce ('td').addClass ('type').appendTo (tr),
          td_id    = ce ('td').addClass ('id').appendTo (tr),
          td_title = ce ('td').addClass ('title').appendTo (tr),
          td_misc  = ce ('td').addClass ('misc').appendTo (tr);

      td_date.text (item.date);

      ce ('img').
        attr ({
          'src':   'img/' + item.type + '.png',
          'alt':   item.type,
          'title': item.type,
          'class': 'icon'
        }).
        appendTo (td_type);

      td_id.text (item.id);

      if (item.uri) {
        ce ('a').
          text (item.title).
          attr ('href', item.uri).
          appendTo (td_title);

      } else {
        td_title.text (item.title);
      }

      if (item.wp_uri) {
        ce ('a').
          attr ('href', item.wp_uri).
          append (
            ce ('img').
              attr ({
                'src':   'img/wikipedia.png',
                'alt':   'Wikipedia',
                'title': "\u201c" + item.title + "\u201d in Wikipedia",
                'class': 'icon'
              })
          ).
          appendTo (td_misc);
      }
    }
  }, 1);
}

var month_map = {
  january:    1,
  february:   2,
  march:      3,
  april:      4,
  may:        5,
  june:       6,
  july:       7,
  august:     8,
  september:  9,
  october:   10,
  november:  11,
  december:  12
};

function parse_date (string) {
  var m = /^(\S+) ([0-9]+), ([0-9]+)/.exec (string);

  if (! m || ! month_map[m[1].toLowerCase ()]) {
    throw new HeroesScrapeError ('parse_date: Invalid parameters');
  }

  var year  = + m[3],
      month = month_map[m[1].toLowerCase ()],
      day   = + m[2];

  return pad (year, 4) + '-' + pad (month, 2) + '-' + pad (day, 2);
}

var webisode_id_map = {
  'Going Postal':    'gp',
  'Heroes: Destiny': 'hd',
  'The Recruit':     'tr',
  'Hard Knox':       'hk'
};

var webisode_re = /^(?:Going Postal|Heroes: Destiny|The Recruit|Hard Knox)$/;

function scrape_episodes (tree) {
  $(tree).find ('h3').each (function () {
    var text = $(this).find ('.mw-headline').text ();

    var type,
        id_prefix,
        title_prefix,
        date_col;

    var match = /^Season ([0-9]+)/.exec (text);
    if (match) {
      type         = 'episode';
      id_prefix    = match[1] + 'x';
      title_prefix = '';
      date_col     = 5;

    } else if (webisode_id_map[text]) {
      type         = 'webisode';
      id_prefix    = webisode_id_map[text];
      title_prefix = text + ' \u2013 ';
      date_col     = 4;

    } else {
      return;
    }

    // The first episode of a season.
    var first_ep = null;

    $(this).next ('table.wikitable').find ('tr').each (function () {
      var ep = $(this).find ('td:eq(0)').text ();

      if (! /^[0-9]+$/.exec (ep)) {
        return;
      }

      ep = + ep;
      if (first_ep === null) {
        first_ep = ep;
      }

      var ep_real = 1 + ep - first_ep,
          id      = id_prefix + pad (ep_real, 2),
          title   = title_prefix + $(this).find ('td:eq(1) b').text (),
          wp_uri  = $(this).find ('td:eq(1) b a').attr ('href'),
          date    = parse_date ($(this).find ('td:eq('+date_col+')').text ());

      if (wp_uri) {
        wp_uri = 'http://en.wikipedia.org' + wp_uri;
      }

      add_item (type, id, title, date, { wp_uri: wp_uri });
    });
  });

  scraped_episodes = true;
  generate_table ();
}

function scrape_comics (tree) {
  var bonus = 0;

  $(tree).find ('table.wikitable tr').each (function () {
    var id = $(this).find ('td:eq(0)').text ();

    if (/^[0-9]+$/.exec (id)) {
      // Proceed.

    } else if (/^(?:Bonus|Interactive Novel)/.exec (id)) {
      bonus++;
      id = 'b' + bonus;

    } else {
      return;
    }

    var title = $(this).find ('td:eq(1) a:first').text (),
        uri   = $(this).find ('td:eq(1) a:first').attr ('href'),
        date  = $(this).find ('td:eq(2)').text ();

    add_item ('comic', id, title, date, { uri: uri });
  });

  scraped_comics = true;
  generate_table ();
}

get_wikipedia_page ('List_of_Heroes_episodes',       scrape_episodes);
get_wikipedia_page ('List_of_Heroes_graphic_novels', scrape_comics);

$(function () {
  $('#heroes-table-container').
    empty ().
    append ($('
Loading...
'));

  document_ready = true;

  generate_table ();
});

}) (jQuery);

// vim:set et sw=2 sts=2:
<|endoftext|>


<commit_before>/**
 * @author Daniel Sawatzky, Marius Runde
 */

// Global variables
	var wHeight = getWindowHeight();
	var wWidth = getWindowWidth();
	var pos = getScrollXY();

	var streetmode = false;
	var alerted = false;

// Screen resolution
//alert("Höhe: " + wHeight + "Breite: " + wWidth);

// Function to change between normal and analysis mode
function changeMode() {

	var mapWidth = document.getElementById('map-container').style.width;

	if (mapWidth == "50%") {
		document.getElementById('analyseModeBtn').value ="Aus";
		document.getElementById('analyseModeBtn').style.color ="#1D83C3";
		document.getElementById('analyseModeBtn').style.border ="1px solid #1D83C3";
		document.getElementById('analyseModeBtn').style.background ="#fff";

		document.getElementById('map-container').style.display = "block";
		document.getElementById('map-container').style.width = "100%";
		resizeMap();
		document.getElementById('analyser-panel').style.display = "none";
		document.getElementById('analyser-chart').style.display = "none";
		document.getElementById('analyser-table').style.display = "none";

		/*document.getElementById('header-nav').style.background = "#fff";*/

	} else {
		document.getElementById('analyseModeBtn').value ="An";
		document.getElementById('analyseModeBtn').style.color ="#fff";
		document.getElementById('analyseModeBtn').style.border ="#990000";
		document.getElementById('analyseModeBtn').style.background ="#990000";

		document.getElementById('map-container').style.width = "50%";
		resizeMap();

		document.getElementById('analyser-panel').style.display = "block";
		document.getElementById('analyser-chart').style.display = "block";
		document.getElementById('analyser-table').style.display = "block";

		/*document.getElementById('header-nav').style.background = "#DCE3E7";*/

	}
}

/**
 * jQuery-Function to select a time range
 * additonal: datepicker in german language and timepicker
 */
$(function() {
	var l = getParam('lang');
	if (l == "en") {
		$.timepicker.regional['en'] = {
			timeOnlyTitle : 'Choose time',
			timeText : 'Time',
			hourText : 'Houe',
			minuteText : 'Minute',
			secondText : 'Second',
			currentText : 'Now',
			closeText : 'Choose',
			monthNames : ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],
			monthNamesShort : ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Okt', 'Nov', 'Dec'],
			dayNames : ['Sunday', 'Monday', 'Thuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'],
			dayNamesShort : ['Sun', 'Mon', 'Thu', 'Wed', 'Thu', 'Fri', 'Sat'],
			dayNamesMin : ['Sun', 'Mon', 'Thu', 'Wed', 'Thu', 'Fri', 'Sat'],
			weekHeader : 'We',
			weekStatus : 'Week of the month',
			ampm : false
		};
		$.timepicker.setDefaults($.timepicker.regional['en']);
	} else {

		$.timepicker.regional['de'] = {
			timeOnlyTitle : 'Uhrzeit auswählen',
			timeText : 'Zeit',
			hourText : 'Stunde',
			minuteText : 'Minute',
			secondText : 'Sekunde',
			currentText : 'Jetzt',
			closeText : 'Auswählen',
			monthNames : ['Januar', 'Februar', 'März', 'April', 'Mai', 'Juni', 'Juli', 'August', 'September', 'Oktober', 'November', 'Dezember'],
			monthNamesShort : ['Jan', 'Feb', 'Mär', 'Apr', 'Mai', 'Jun', 'Jul', 'Aug', 'Sep', 'Okt', 'Nov', 'Dez'],
			dayNames : ['Sonntag', 'Montag', 'Dienstag', 'Mittwoch', 'Donnerstag', 'Freitag', 'Samstag'],
			dayNamesShort : ['So', 'Mo', 'Di', 'Mi', 'Do', 'Fr', 'Sa'],
			dayNamesMin : ['So', 'Mo', 'Di', 'Mi', 'Do', 'Fr', 'Sa'],
			weekHeader : 'Wo',
			weekStatus : 'Woche des Monats',
		ampm : false
		};
		$.timepicker.setDefaults($.timepicker.regional['de']);

	}

	$("#date-from").datetimepicker({

		minDate: new Date(2013,5,8),
		maxDate: "+0",
		changeMonth: false,
		changeYear: false,
		numberOfMonths: 3,
		showWeek: false,
		dateFormat: "dd-mm-yy"

	});

	$("#date-to").datetimepicker({

		changeMonth: false,
		changeYear: false,
		numberOfMonths: 3,
		showWeek: false,
		dateFormat: "dd-mm-yy",
		onClose: function( selectedDate ) {
			$("#date-from").datepicker("option", "maxDate", selectedDate);
		},
		beforeShow: function (selectedDate) {
			var min = $("#date-from").datepicker("getDate");
			$("#date-to").datepicker("option","minDate", min);
			$("#date-to").datepicker("option","maxDate", "+0");
		}
	});

});

/**
 * Window functions (height, width, scroll position)
 * Author: Daniel Sawatzky
 */
	function getWindowWidth(){
		var value;
		try {
			value = document.body.clientWidth;
		} catch(e){
			value = window.innerWidth;
		}
		return value; //window.innerWidth;
	}
	function getWindowHeight(){
		var value;
		try {
			value = document.body.clientHeight;
		} catch(e){
			value = window.innerHeight;
		}
		return value; //window.innerHeight;
	}

	function getScrollXY() {
		var scrOfX = 0, scrOfY = 0;
		if( typeof( window.pageYOffset ) == 'number' ) {
			//Netscape compliant
			scrOfY = window.pageYOffset;
			scrOfX = window.pageXOffset;
		} else if( document.body && ( document.body.scrollLeft || document.body.scrollTop ) ) {
			//DOM compliant
			scrOfY = document.body.scrollTop;
			scrOfX = document.body.scrollLeft;
		} else if( document.documentElement && ( document.documentElement.scrollLeft || document.documentElement.scrollTop ) ) {
			//IE6 standards compliant mode
			scrOfY = document.documentElement.scrollTop;
			scrOfX = document.documentElement.scrollLeft;
		}
		return [ scrOfX, scrOfY ];
	}

function streetMode(){
  	if(map.getMapTypeId()=="OSM"){
  		var l = getParam('lang');
			if (l == "en") {
				alert("Change map layer to a google map.");
			} else {
  				alert("Ändern Sie die Karte zu einer Google-Karte.");
  		    }
  	}else{
  		if(alerted==false){
  			var l = getParam('lang');
			if (l == "en") {
				alert("Remove the last points with 'rightclick' somewhere in the map, but not at a point!");
			} else {
  				alert("Entfernen Sie den letzten Punkt mir einem Rechtsklick an einem beliebigen Ort auf der Karte, aber nicht auf einem Punkt!");
  		}
  			alerted =true;
  		}
  		if(streetmode==false){

  			// Call enableStreetmode() function
  			enableStreetmode();
  		}else if(streetmode==true){
  			// Call disableStreetmode() function for clearing the overlay and removing Listener
  			disableStreetmode();
  		}
 }}

/* Click Handler Styles */
$(document).ready(function(){

	$("#boundingBoxBtn").click(function(){
		if (BoundingBox == true){
		  $(this).css("color","#fff");
		  $(this).css("background","#92C049");
		} else {
		  $(this).css("color","#92C049");
		  $(this).css("background","#fff");
		}
	});

	$("#streetSelectionBtn").click(function(){
		if(streetmode==true){
		  $(this).css("color","#fff");
		  $(this).css("background","#92C049");
		} else {
		  $(this).css("color","#92C049");
		  $(this).css("background","#fff");
		}
	});

});<commit_msg>Correction<commit_after>/**
 * @author Daniel Sawatzky, Marius Runde
 */

// Global variables
	var wHeight = getWindowHeight();
	var wWidth = getWindowWidth();
	var pos = getScrollXY();

	var streetmode = false;
	var alerted = false;

// Screen resolution
//alert("Höhe: " + wHeight + "Breite: " + wWidth);

// Function to change between normal and analysis mode
function changeMode() {

	var mapWidth = document.getElementById('map-container').style.width;

	if (mapWidth == "50%") {
		document.getElementById('analyseModeBtn').value ="Aus";
		document.getElementById('analyseModeBtn').style.color ="#1D83C3";
		document.getElementById('analyseModeBtn').style.border ="1px solid #1D83C3";
		document.getElementById('analyseModeBtn').style.background ="#fff";

		document.getElementById('map-container').style.display = "block";
		document.getElementById('map-container').style.width = "100%";
		resizeMap();
		document.getElementById('analyser-panel').style.display = "none";
		document.getElementById('analyser-chart').style.display = "none";
		document.getElementById('analyser-table').style.display = "none";

		/*document.getElementById('header-nav').style.background = "#fff";*/

	} else {
		document.getElementById('analyseModeBtn').value ="An";
		document.getElementById('analyseModeBtn').style.color ="#fff";
		document.getElementById('analyseModeBtn').style.border ="#990000";
		document.getElementById('analyseModeBtn').style.background ="#990000";

		document.getElementById('map-container').style.width = "50%";
		resizeMap();

		document.getElementById('analyser-panel').style.display = "block";
		document.getElementById('analyser-chart').style.display = "block";
		document.getElementById('analyser-table').style.display = "block";

		/*document.getElementById('header-nav').style.background = "#DCE3E7";*/

	}
}

/**
 * jQuery-Function to select a time range
 * additonal: datepicker in german language and timepicker
 */
$(function() {
	var l = getParam('lang');
	if (l == "en") {
		$.timepicker.regional['en'] = {
			timeOnlyTitle : 'Choose time',
			timeText : 'Time',
			hourText : 'Houe',
			minuteText : 'Minute',
			secondText : 'Second',
			currentText : 'Now',
			closeText : 'Choose',
			monthNames : ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],
			monthNamesShort : ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Okt', 'Nov', 'Dec'],
			dayNames : ['Sunday', 'Monday', 'Thuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'],
			dayNamesShort : ['Sun', 'Mon', 'Thu', 'Wed', 'Thu', 'Fri', 'Sat'],
			dayNamesMin : ['Sun', 'Mon', 'Thu', 'Wed', 'Thu', 'Fri', 'Sat'],
			weekHeader : 'We',
			weekStatus : 'Week of the month',
			ampm : false
		};
		$.timepicker.setDefaults($.timepicker.regional['en']);
	} else {

		$.timepicker.regional['de'] = {
			timeOnlyTitle : 'Uhrzeit auswählen',
			timeText : 'Zeit',
			hourText : 'Stunde',
			minuteText : 'Minute',
			secondText : 'Sekunde',
			currentText : 'Jetzt',
			closeText : 'Auswählen',
			monthNames : ['Januar', 'Februar', 'März', 'April', 'Mai', 'Juni', 'Juli', 'August', 'September', 'Oktober', 'November', 'Dezember'],
			monthNamesShort : ['Jan', 'Feb', 'Mär', 'Apr', 'Mai', 'Jun', 'Jul', 'Aug', 'Sep', 'Okt', 'Nov', 'Dez'],
			dayNames : ['Sonntag', 'Montag', 'Dienstag', 'Mittwoch', 'Donnerstag', 'Freitag', 'Samstag'],
			dayNamesShort : ['So', 'Mo', 'Di', 'Mi', 'Do', 'Fr', 'Sa'],
			dayNamesMin : ['So', 'Mo', 'Di', 'Mi', 'Do', 'Fr', 'Sa'],
			weekHeader : 'Wo',
			weekStatus : 'Woche des Monats',
		ampm : false
		};
		$.timepicker.setDefaults($.timepicker.regional['de']);

	}

	$("#date-from").datetimepicker({

		minDate: new Date(2013,5,8),
		maxDate: "+0",
		changeMonth: false,
		changeYear: false,
		numberOfMonths: 3,
		showWeek: false,
		dateFormat: "dd-mm-yy"

	});

	$("#date-to").datetimepicker({

		changeMonth: false,
		changeYear: false,
		numberOfMonths: 3,
		showWeek: false,
		dateFormat: "dd-mm-yy",
		onClose: function( selectedDate ) {
			$("#date-from").datepicker("option", "maxDate", selectedDate);
		},
		beforeShow: function (selectedDate) {
			var min = $("#date-from").datepicker("getDate");
			$("#date-to").datepicker("option","minDate", min);
			$("#date-to").datepicker("option","maxDate", "+0");
		}
	});

});

/**
 * Window functions (height, width, scroll position)
 * Author: Daniel Sawatzky
 */
	function getWindowWidth(){
		var value;
		try {
			value = document.body.clientWidth;
		} catch(e){
			value = window.innerWidth;
		}
		return value; //window.innerWidth;
	}
	function getWindowHeight(){
		var value;
		try {
			value = document.body.clientHeight;
		} catch(e){
			value = window.innerHeight;
		}
		return value; //window.innerHeight;
	}

	function getScrollXY() {
		var scrOfX = 0, scrOfY = 0;
		if( typeof( window.pageYOffset ) == 'number' ) {
			//Netscape compliant
			scrOfY = window.pageYOffset;
			scrOfX = window.pageXOffset;
		} else if( document.body && ( document.body.scrollLeft || document.body.scrollTop ) ) {
			//DOM compliant
			scrOfY = document.body.scrollTop;
			scrOfX = document.body.scrollLeft;
		} else if( document.documentElement && ( document.documentElement.scrollLeft || document.documentElement.scrollTop ) ) {
			//IE6 standards compliant mode
			scrOfY = document.documentElement.scrollTop;
			scrOfX = document.documentElement.scrollLeft;
		}
		return [ scrOfX, scrOfY ];
	}

function streetMode(){
  	if(map.getMapTypeId()=="OSM"){
  		var l = getParam('lang');
		if (l == "en") {
			alert("Change map layer to a google map.");
		} else {
			alert("Ändern Sie die Karte zu einer Google-Karte.");
	    }
  	}else{
  		if(alerted==false){
  			var l = getParam('lang');
			if (l == "en") {
				alert("Remove the last points with 'rightclick' somewhere in the map, but not at a point!");
			} else {
  				alert("Entfernen Sie den letzten Punkt mir einem Rechtsklick an einem beliebigen Ort auf der Karte, aber nicht auf einem Punkt!");
  		}
  			alerted =true;
  		}
  		if(streetmode==false){

  			// Call enableStreetmode() function
  			enableStreetmode();
  		}else if(streetmode==true){
  			// Call disableStreetmode() function for clearing the overlay and removing Listener
  			disableStreetmode();
  		}
 }}

/* Click Handler Styles */
$(document).ready(function(){

	$("#boundingBoxBtn").click(function(){
		if (BoundingBox == true){
		  $(this).css("color","#fff");
		  $(this).css("background","#92C049");
		} else {
		  $(this).css("color","#92C049");
		  $(this).css("background","#fff");
		}
	});

	$("#streetSelectionBtn").click(function(){
		if(streetmode==true){
		  $(this).css("color","#fff");
		  $(this).css("background","#92C049");
		} else {
		  $(this).css("color","#92C049");
		  $(this).css("background","#fff");
		}
	});

});<|endoftext|>


<commit_before>(function($) {
	$.fn.miller = function(settings) {
		var settings = $.extend({
					'url': function(id) { return id; },
					'tabindex': 0,
					'minWidth': 40,
					'panel': {
						'width': 100,
						'options': {}
					}
				},
				settings
			)
		;

		var hasFocus = false;

		this
			.addClass('miller')
			.attr('tabindex', settings.tabindex)
			.focus(function() { hasFocus = true; })
			.blur(function() { hasFocus = false; })
		;

		var path = $('<div>', { class: 'path' })
			.appendTo(this)
		;

		var columns = $('<div>', { class: 'columns' })
			.appendTo(this)
		;

		var currentLine = null;

		$(document).on('keydown', function(event) {
				if (hasFocus && currentLine && (event.which == 37 || event.which == 38 || event.which == 39 || event.which == 40)) {
					var newCurrentLine = [];

					switch (event.which) {
						case 37:
							newCurrentLine = currentLine.parent().prev().prev().find('li.parentSelected');
							break;

						case 38:
							newCurrentLine = currentLine.prev();
							break;

						case 39:
							newCurrentLine = currentLine.parent().next().next().find('li:first');
							break;

						case 40:
							newCurrentLine = currentLine.next();
							break;
					}

					if (newCurrentLine.length) {
						currentLine = newCurrentLine.click();
						currentLine.parent().scrollTop(currentLine.position().top);
					}

					return false;
				}
			}
		);

		var removeNextColumns = function() {
				var line = $(this);

				var column = line.parent();

				column
					.nextAll()
						.slice(1)
							.remove()
				;

				column
					.find('li')
						.removeClass('selected parentSelected')
				;

				line.addClass(line.hasClass('parent') ? 'parentSelected' : 'selected');

				var columnIndex = column.index();

				columnIndex -= (columnIndex - (columnIndex / 2));

				path
					.append($('<span>', { text: line.text(), css: { cursor: 'pointer' } })
						.on('click', function() {
								columns
									.children()
										.slice((($(this).index() * 2) + 4))
											.remove()
								;
								columns
									.children('ul:last')
										.find('li')
											.removeClass('parentSelected')
								;
								path
									.children()
										.slice($(this).index() + 1)
											.remove()
								;
							}
						)
					)
					.children()
						.slice(columnIndex, -1)
							.remove()
				;

			}
		;

		var buildColumn = function(lines) {
				if (lines == null) {
					$('li.parentLoading').remove();
				} else {
					var width = 0;

					var lastGrip = columns.children('div.grip:last')[0];

					if (lastGrip) {
						lastGrip = $(lastGrip);
						width = lastGrip.position().left + lastGrip.width() + columns.scrollLeft();
					}

					if (lines.length <= 0) {
						var line = $('li.parentLoading')
							.removeClass('parent')
							.addClass('selected')
						;

						var panel = $('<ul>')
							.css({ 'top': 0, 'left': width })
							.addClass('panel')
						;

						var id = line.data('id');

						$.each(settings['panel']['options'], function(key, callbackGenerator) {
								var option = $('<li>', { text: key })
									.on('click', callbackGenerator(id))
								;

								option.appendTo(panel);
							}
						);

						columns
							.append(panel)
							.scrollLeft(width + panel.width())
						;
					} else {
						$('li.parentLoading').addClass('parentSelected');

						var column = $('<ul>')
							.css({ 'top': 0, 'left': width })
						;

						$.each(lines, function(id, data) {
								var line = $('<li>', { text: data['name'], css: { cursor: 'pointer' } })
									.data('id', data['id'])
									.on('click', removeNextColumns)
									.on('click', getLines)
									.appendTo(column)
								;

								if (data['parent']) {
									line.addClass('parent');
								}
							}
						);

						columns
							.append(column)
							.scrollLeft(width += column.width())
							.append(
								$('<div>', { class: 'grip' })
									.css({ 'top': 0, 'left': width })
									.mousedown(function(event) {
											var x = event.pageX;
											var cursor = columns.css('cursor');

											columns
												.css('cursor', 'col-resize')
												.mousemove(function(event) {
														var delta = event.pageX - x;
														var newWidth = column.width() + delta;

														if (newWidth > settings['minWidth']) {
															column
																.width(newWidth)
																.nextAll()
																	.each(function() {
																			$(this).css('left', $(this).position().left + delta + columns.scrollLeft());
																		}
																	)
															;
														}

														x = event.pageX;
													}
												)
												.mouseup(function() {
														columns
															.off('mousemove')
															.css('cursor', cursor)
														;
													}
												)
											;
										}
									)
							)
						;
					}
				}
			}
		;

		var getLines = function(event) {
				currentLine = $(event.currentTarget)
					.removeClass('parentSelected')
					.addClass('parentLoading')
				;

				$.getJSON(settings['url']($(this).data('id')), buildColumn)
					.always(function() {
							currentLine
								.removeClass('parentLoading')
							;
						}
					)
					.fail(function() {})
				;

			}
		;

		$.getJSON(settings['url'](), buildColumn);

		return this;
	};
})(jQuery);
<commit_msg>Set tabindex only if it is undefined on the target.<commit_after>(function($) {
	$.fn.miller = function(settings) {
		var settings = $.extend({
					'url': function(id) { return id; },
					'tabindex': 0,
					'minWidth': 40,
					'panel': {
						'width': 100,
						'options': {}
					}
				},
				settings
			)
		;

		var hasFocus = false;

		this
			.addClass('miller')
			.focus(function() { hasFocus = true; })
			.blur(function() { hasFocus = false; })
		;

		if (!this.attr('tabindex')) {
			this.attr('tabindex', settings.tabindex);
		}

		var path = $('<div>', { class: 'path' })
			.appendTo(this)
		;

		var columns = $('<div>', { class: 'columns' })
			.appendTo(this)
		;

		var currentLine = null;

		$(document).on('keydown', function(event) {
				if (hasFocus && currentLine && (event.which == 37 || event.which == 38 || event.which == 39 || event.which == 40)) {
					var newCurrentLine = [];

					switch (event.which) {
						case 37:
							newCurrentLine = currentLine.parent().prev().prev().find('li.parentSelected');
							break;

						case 38:
							newCurrentLine = currentLine.prev();
							break;

						case 39:
							newCurrentLine = currentLine.parent().next().next().find('li:first');
							break;

						case 40:
							newCurrentLine = currentLine.next();
							break;
					}

					if (newCurrentLine.length) {
						currentLine = newCurrentLine.click();
						currentLine.parent().scrollTop(currentLine.position().top);
					}

					return false;
				}
			}
		);

		var removeNextColumns = function() {
				var line = $(this);

				var column = line.parent();

				column
					.nextAll()
						.slice(1)
							.remove()
				;

				column
					.find('li')
						.removeClass('selected parentSelected')
				;

				line.addClass(line.hasClass('parent') ? 'parentSelected' : 'selected');

				var columnIndex = column.index();

				columnIndex -= (columnIndex - (columnIndex / 2));

				path
					.append($('<span>', { text: line.text(), css: { cursor: 'pointer' } })
						.on('click', function() {
								columns
									.children()
										.slice((($(this).index() * 2) + 4))
											.remove()
								;
								columns
									.children('ul:last')
										.find('li')
											.removeClass('parentSelected')
								;
								path
									.children()
										.slice($(this).index() + 1)
											.remove()
								;
							}
						)
					)
					.children()
						.slice(columnIndex, -1)
							.remove()
				;

			}
		;

		var buildColumn = function(lines) {
				if (lines == null) {
					$('li.parentLoading').remove();
				} else {
					var width = 0;

					var lastGrip = columns.children('div.grip:last')[0];

					if (lastGrip) {
						lastGrip = $(lastGrip);
						width = lastGrip.position().left + lastGrip.width() + columns.scrollLeft();
					}

					if (lines.length <= 0) {
						var line = $('li.parentLoading')
							.removeClass('parent')
							.addClass('selected')
						;

						var panel = $('<ul>')
							.css({ 'top': 0, 'left': width })
							.addClass('panel')
						;

						var id = line.data('id');

						$.each(settings['panel']['options'], function(key, callbackGenerator) {
								var option = $('<li>', { text: key })
									.on('click', callbackGenerator(id))
								;

								option.appendTo(panel);
							}
						);

						columns
							.append(panel)
							.scrollLeft(width + panel.width())
						;
					} else {
						$('li.parentLoading').addClass('parentSelected');

						var column = $('<ul>')
							.css({ 'top': 0, 'left': width })
						;

						$.each(lines, function(id, data) {
								var line = $('<li>', { text: data['name'], css: { cursor: 'pointer' } })
									.data('id', data['id'])
									.on('click', removeNextColumns)
									.on('click', getLines)
									.appendTo(column)
								;

								if (data['parent']) {
									line.addClass('parent');
								}
							}
						);

						columns
							.append(column)
							.scrollLeft(width += column.width())
							.append(
								$('<div>', { class: 'grip' })
									.css({ 'top': 0, 'left': width })
									.mousedown(function(event) {
											var x = event.pageX;
											var cursor = columns.css('cursor');

											columns
												.css('cursor', 'col-resize')
												.mousemove(function(event) {
														var delta = event.pageX - x;
														var newWidth = column.width() + delta;

														if (newWidth > settings['minWidth']) {
															column
																.width(newWidth)
																.nextAll()
																	.each(function() {
																			$(this).css('left', $(this).position().left + delta + columns.scrollLeft());
																		}
																	)
															;
														}

														x = event.pageX;
													}
												)
												.mouseup(function() {
														columns
															.off('mousemove')
															.css('cursor', cursor)
														;
													}
												)
											;
										}
									)
							)
						;
					}
				}
			}
		;

		var getLines = function(event) {
				currentLine = $(event.currentTarget)
					.removeClass('parentSelected')
					.addClass('parentLoading')
				;

				$.getJSON(settings['url']($(this).data('id')), buildColumn)
					.always(function() {
							currentLine
								.removeClass('parentLoading')
							;
						}
					)
					.fail(function() {})
				;

			}
		;

		$.getJSON(settings['url'](), buildColumn);

		return this;
	};
})(jQuery);
<|endoftext|>


<commit_before>/*
 * tksweb - Web UI for timesheet entry
 *
 * Copyright (c) 2011 Grant McLean (grant@mclean.net.nz)
 *
 * Dual licensed under the MIT and GPL licenses:
 *   http://www.opensource.org/licenses/mit-license.php
 *   http://www.gnu.org/licenses/gpl.html
 *
 */

(function($) {

$.fn.tksweb = function(options) {
    return this.each(function() {
        $.fn.tksweb.build(this, options);
    });
};

$.fn.tksweb.build = function(app_el, options) {
    var app        = $.extend({}, $.fn.tksweb.defaults, options);
    var x_inc      = app.activity_width;
    var y_inc      = app.hour_height / 4;
    var x_max      = 6 * x_inc;
    var y_max      = (app.last_hour - app.first_hour + 0.75) * 4 * y_inc;
    var keyCode    = $.ui.keyCode;

    app.current_activity       = null;
    app.clipboard              = null;
    app.activity_dialog_active = false;

    register_templates();

    var week_view  = $('
');
    var week_hdr   = $('
');
    var hours      = $('
');
    var week_hours = $('
');
    var activities = $('
');
    var tooltip    = $('
').hide();
    var cursor     = add_cursor(activities);

    activities.append(tooltip);

    $(app.day_names).each(function(i, day) {
        week_hdr.append( $('
').text(day) )
    });

    for(var i = app.first_hour; i <= app.last_hour; i++) {
        var prefix = i > 9 ? '' : '0'
        hours.append( $('
').text(prefix + i + ':00') );
    }

    activities.height((app.last_hour - app.first_hour + 1) * app.hour_height);

    week_hours.append(hours, activities)
    week_view.append(week_hdr, week_hours);

    $(app_el).addClass('tksweb-app').append(week_view);

    if(app.scroll_to_hour >= 1) {
        week_hours.scrollTop((app.scroll_to_hour - 0.5) * app.hour_height);
    }

    var dlg_activity = add_activity_dialog($(app_el));

    week_hours.delegate('.activity', 'mouseenter', function() {
        init_draggable( $(this) );
    });

    $(document).keydown(function(e) { key_handler(e); });
    activities.dblclick(function(e) { edit_activity_at_cursor(e); })
    week_hours.mousedown(function(e) { mousedown_handler(e) });

set_cursor_pos(100, 10 * app.hour_height);
create_activity({
    description : "Do some stuff with the thing",
    hours       : 1.5,
    wr_number   : "12345"
});

set_cursor_pos(200, 9 * app.hour_height);
create_activity({
    description : "Do stuff to the other thing",
    hours       : 1.75,
    wr_number   : "54763"
});

set_cursor_pos(200, 10.75 * app.hour_height);
create_activity({
    description : "Undo that last thing",
    hours       : 0.75,
    wr_number   : "12345"
});

set_cursor_pos(300, 9 * app.hour_height);
create_activity({
    description : "Decommision obselete thing",
    hours       : 1,
    wr_number   : "44632"
});

    set_cursor_pos(0, app.scroll_to_hour * app.hour_height);

    function register_templates() {
        $.template(
            "tmpl_activity",
            '
'
            + '
${wr_number}: ${description}

'
        );
    }

    function init_draggable(activity) {
        // Initialise draggable+resizable behaviours on first mouse enter
        if(activity.is(':data(draggable)')) {
            return;
        }

        activity.draggable({
            grid: [x_inc, y_inc],
            containment: 'parent',
            cursor: 'move',
            zIndex:  100,
            start: function (event, ui) {
                cursor.hide();
                show_start_time_tooltip(activity);
            },
            drag: function (event, ui) {
                show_start_time_tooltip(activity);
            },
            stop: function () {
                var act_data = activity.tmplItem().data;
                p = activity.position()
                act_data.x = p.left;
                act_data.y = p.top;
                set_cursor_pos(p.left, p.top);
                hide_tooltip();
                cursor.show();
            }
        });

        activity.resizable({
            handles: 'n, s',
            grid: [0, y_inc],
            start: function (event, ui) {
                show_duration_tooltip(activity);
            },
            resize: function (event, ui) {
                show_duration_tooltip(activity);
            },
            stop: function (event, ui) {
                var act_data = activity.tmplItem().data;
                act_data.height = activity.height();
                act_data.hours  = (act_data.height + app.border_allowance) / (4 * y_inc);
                hide_tooltip();
                unselect_activity();
                select_activity(activity);
            }
        });

    }

    function add_cursor(parent) {
        var cursor = $('
');

        cursor.css({
            width  : (x_inc - app.border_allowance) + 'px',
            height : (y_inc - app.border_allowance) + 'px'
        });

        parent.append(cursor);
        return cursor;
    }

    function show_duration_tooltip(activity) {
        var size = (activity.height() + app.border_allowance) / y_inc;
        var min  = [ '00', '15', '30', '45' ];
        var h = Math.floor(size / 4);
        var m = size % 4;
        tooltip.text(h + ':' + min[m]).show();
        tooltip.position({
          my: "left bottom",
          at: "right bottom",
          of: activity
        });
    }

    function show_start_time_tooltip(activity) {
        var p = activity.position();
        var start = p.top / y_inc;
        var min  = [ '00', '15', '30', '45' ];
        var h = Math.floor(start / 4);
        var m = start % 4;
        tooltip.text(h + ':' + (min[m] || '00')).show();
        tooltip.position({
          my: "right top",
          at: "left top",
          of: activity
        });
    }

    function hide_tooltip() {
        tooltip.hide();
    }

    function set_cursor_pos(x, y) {
        cursor.css({ left: x + 'px', top: y + 'px' });
    }

    function mousedown_handler(e) {
        var target = e.target;
        if(target.nodeName == "P") { target = target.parentNode; }
        var activity = $(target).hasClass('activity') ? target : null;
        if(activity) {
            select_activity(activity);
        }
        else if(!$(target).hasClass('cursor')) {
            mouse_position_cursor(e);
        }
    }

    function mouse_position_cursor(e) {
        unselect_activity();
        var x = Math.floor((e.layerX || e.offsetX) / x_inc) * x_inc;
        var y = Math.floor((e.layerY || e.offsetY) / y_inc) * y_inc;
        set_cursor_pos(x, y);
    }

    function edit_activity_at_cursor(e) {
        dlg_activity.dialog('open');
        e.stopPropagation();
    }

    function current_activity_data() {
        if(app.current_activity) {
            return $(app.current_activity).tmplItem().data;
        }
        return null;
    }

    function default_activity_data() {
        return {
            wr_number   : '',
            hours       : app.default_duration,
            description : ''
        };
    }

    function key_handler(e) {
        if(app.activity_dialog_active) {
            return true;
        }
        var curr = null;
        if(app.current_activity) {
            curr = $(app.current_activity).tmplItem().data;
        }
        switch(e.keyCode) {
            case keyCode.LEFT:   move_cursor(-x_inc,  0);    break;
            case keyCode.RIGHT:  move_cursor( x_inc,  0);    break;
            case keyCode.ENTER:  edit_activity_at_cursor(e); break;
            case keyCode.UP:
                move_cursor(0, -y_inc);
                break;
            case keyCode.DOWN:
                move_cursor(0, curr ? curr.hours * 4 * y_inc : y_inc);
                break;
            case keyCode.DELETE: delete_activity();       break;
            default:
                if(e.ctrlKey && e.keyCode == 67) {  // Ctrl-C
                    copy_activity();
                }
                else if(e.ctrlKey && e.keyCode == 88) {  // Ctrl-X
                    copy_activity();
                    delete_activity();
                }
                else if(e.ctrlKey && e.keyCode == 86) {  // Ctrl-V
                    paste_activity();
                }
                else {
                    return;
                }
                break;
        }
        e.preventDefault();
    }

    function move_cursor(x_delta, y_delta) {
        var p = cursor.position();

        if(x_delta < 0 && p.left > 0    ) { p.left += x_delta; }
        if(x_delta > 0 && p.left < x_max) { p.left += x_delta; }
        if(y_delta < 0 && p.top  > 0    ) { p.top  += y_delta; }
        if(y_delta > 0 && p.top  < y_max) { p.top  += y_delta; }
        var activity = find_activity_at_cursor(p);
        if(activity) {
            select_activity(activity);
        }
        else {
            unselect_activity();
            set_cursor_pos(p.left, p.top);
        }
    }

    function find_activity_at_cursor(p) {
        return activities.find('.activity').filter(function() {
            var data = $(this).tmplItem().data;
            if(p.left != data.x) { return false; }
            if(p.top < data.y)  { return false; }
            if(p.top > (data.y + data.height))  {
                return false;
            }
            set_cursor_pos(data.x, data.y);
            return true;
        })[0];
    }

    function select_activity(activity) {
        if(app.current_activity == activity) {
            return;
        }
        unselect_activity();
        if(activity) {
            app.current_activity = activity;
            var $activity = $(activity);
            var p = $activity.position();
            set_cursor_pos(p.left, p.top);
            $activity.addClass('selected');
        }
    }

    function unselect_activity() {
        if(app.current_activity) {
            $(app.current_activity).removeClass('selected');
        }
        app.current_activity = null;
    }

    function delete_activity() {
        var activity = app.current_activity;
        if(!activity) {
            app_alert('You must select an activity first');
            return;
        }
        unselect_activity();
        $(activity).remove();
    }

    function copy_activity() {
        var data = current_activity_data();
        if(!data) {
            app_alert('You must select an activity first');
            return;
        }
        app.clipboard = $.extend({}, data);
    }

    function paste_activity() {
        var data = $.extend({}, app.clipboard);
        var activity = create_activity(data);
        select_activity( activity );
    }

    function save_activity(data) {
        if(app.current_activity) {
            update_activity(data);
        }
        else {
            create_activity(data);
        }
    }

    function update_activity(data) {
        var tmpl_item = $(app.current_activity).tmplItem();
        var tmpl_data = tmpl_item.data;
        tmpl_data.wr_number   = data.wr_number;
        tmpl_data.hours       = data.hours;
        tmpl_data.description = data.description;
        tmpl_data.height      = data.hours * 4 * y_inc - app.border_allowance;
        tmpl_item.update();
    }

    function create_activity(data) {
        var p = cursor.position();
        data.height = data.hours * 4 * y_inc - app.border_allowance;
        data.x      = p.left;
        data.y      = p.top;
        var activity = $.tmpl("tmpl_activity", data);
        activities.append(activity);
        return activity;
    }

    function app_alert(message) {
        alert(message);
        week_hours.focus();           // Work-around keyboard focus being lost
        week_hours.css({'outline': 'none'});
    }

    function add_activity_dialog($app_el) {
        var div      = $('
');
        var wr_inp   = $('<input type="text" />');
        var hr_inp   = $('<input type="text" />').val(1);
        var desc_inp = $('<input type="text" />');
        div.append(
            $('
WR Number
').append(wr_inp),
            $('
Hours
').append(hr_inp),
            $('
Activity Description
').append(desc_inp)
        );

        div.dialog({
            autoOpen:      false,
            title:         "Activity Detail",
            resizable:     false,
            closeOnEscape: true,
            width:         360,
            height:        230,
            modal:         true,
            open: function() {
                app.activity_dialog_active = true;
                var data = current_activity_data() || default_activity_data();
                wr_inp.val(data.wr_number);
                hr_inp.val(data.hours);
                desc_inp.val(data.description);
                if(data.x) {
                    dlg_activity.dialog('option', 'title', 'Edit Activity');
                    desc_inp.focus();
                }
                else {
                    dlg_activity.dialog('option', 'title', 'Create Activity');
                    wr_inp.focus();
                }
            },
            close: function() {
                app.activity_dialog_active = false;
            },
            buttons:       {
                "Ok":  function() {
                    var data = {
                        wr_number   : wr_inp.val(),
                        hours       : hr_inp.val(),
                        description : desc_inp.val()
                    };
                    save_activity(data);
                    div.dialog("close");
                },
                "Cancel": function() { $(this).dialog("close"); }
            }
        });

        div.find('input').keydown(function(e) {
            if(e.keyCode == keyCode.ENTER) {
                div.parent().find('.ui-dialog-buttonpane button:first').click();
                e.stopPropagation();  // Don't allow Enter to re-launch dialog
            }
        });

        return div;
    }

};

$.fn.tksweb.defaults = {
    day_names        : [ 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun' ],
    first_hour       : 0,
    last_hour        : 23,
    scroll_to_hour   : 9,
    hour_height      : 48,  // must be a multiple of 4 and match bg-image bands
    activity_width   : 100,
    border_allowance : 2,   // top border thickness + bottom border
    default_duration : 1    // default number of hours for a new activity
};

})(jQuery);
<commit_msg>fix spelling error<commit_after>/*
 * tksweb - Web UI for timesheet entry
 *
 * Copyright (c) 2011 Grant McLean (grant@mclean.net.nz)
 *
 * Dual licensed under the MIT and GPL licenses:
 *   http://www.opensource.org/licenses/mit-license.php
 *   http://www.gnu.org/licenses/gpl.html
 *
 */

(function($) {

$.fn.tksweb = function(options) {
    return this.each(function() {
        $.fn.tksweb.build(this, options);
    });
};

$.fn.tksweb.build = function(app_el, options) {
    var app        = $.extend({}, $.fn.tksweb.defaults, options);
    var x_inc      = app.activity_width;
    var y_inc      = app.hour_height / 4;
    var x_max      = 6 * x_inc;
    var y_max      = (app.last_hour - app.first_hour + 0.75) * 4 * y_inc;
    var keyCode    = $.ui.keyCode;

    app.current_activity       = null;
    app.clipboard              = null;
    app.activity_dialog_active = false;

    register_templates();

    var week_view  = $('
');
    var week_hdr   = $('
');
    var hours      = $('
');
    var week_hours = $('
');
    var activities = $('
');
    var tooltip    = $('
').hide();
    var cursor     = add_cursor(activities);

    activities.append(tooltip);

    $(app.day_names).each(function(i, day) {
        week_hdr.append( $('
').text(day) )
    });

    for(var i = app.first_hour; i <= app.last_hour; i++) {
        var prefix = i > 9 ? '' : '0'
        hours.append( $('
').text(prefix + i + ':00') );
    }

    activities.height((app.last_hour - app.first_hour + 1) * app.hour_height);

    week_hours.append(hours, activities)
    week_view.append(week_hdr, week_hours);

    $(app_el).addClass('tksweb-app').append(week_view);

    if(app.scroll_to_hour >= 1) {
        week_hours.scrollTop((app.scroll_to_hour - 0.5) * app.hour_height);
    }

    var dlg_activity = add_activity_dialog($(app_el));

    week_hours.delegate('.activity', 'mouseenter', function() {
        init_draggable( $(this) );
    });

    $(document).keydown(function(e) { key_handler(e); });
    activities.dblclick(function(e) { edit_activity_at_cursor(e); })
    week_hours.mousedown(function(e) { mousedown_handler(e) });

set_cursor_pos(100, 10 * app.hour_height);
create_activity({
    description : "Do some stuff with the thing",
    hours       : 1.5,
    wr_number   : "12345"
});

set_cursor_pos(200, 9 * app.hour_height);
create_activity({
    description : "Do stuff to the other thing",
    hours       : 1.75,
    wr_number   : "54763"
});

set_cursor_pos(200, 10.75 * app.hour_height);
create_activity({
    description : "Undo that last thing",
    hours       : 0.75,
    wr_number   : "12345"
});

set_cursor_pos(300, 9 * app.hour_height);
create_activity({
    description : "Decommission obselete thing",
    hours       : 1,
    wr_number   : "44632"
});

    set_cursor_pos(0, app.scroll_to_hour * app.hour_height);

    function register_templates() {
        $.template(
            "tmpl_activity",
            '
'
            + '
${wr_number}: ${description}

'
        );
    }

    function init_draggable(activity) {
        // Initialise draggable+resizable behaviours on first mouse enter
        if(activity.is(':data(draggable)')) {
            return;
        }

        activity.draggable({
            grid: [x_inc, y_inc],
            containment: 'parent',
            cursor: 'move',
            zIndex:  100,
            start: function (event, ui) {
                cursor.hide();
                show_start_time_tooltip(activity);
            },
            drag: function (event, ui) {
                show_start_time_tooltip(activity);
            },
            stop: function () {
                var act_data = activity.tmplItem().data;
                p = activity.position()
                act_data.x = p.left;
                act_data.y = p.top;
                set_cursor_pos(p.left, p.top);
                hide_tooltip();
                cursor.show();
            }
        });

        activity.resizable({
            handles: 'n, s',
            grid: [0, y_inc],
            start: function (event, ui) {
                show_duration_tooltip(activity);
            },
            resize: function (event, ui) {
                show_duration_tooltip(activity);
            },
            stop: function (event, ui) {
                var act_data = activity.tmplItem().data;
                act_data.height = activity.height();
                act_data.hours  = (act_data.height + app.border_allowance) / (4 * y_inc);
                hide_tooltip();
                unselect_activity();
                select_activity(activity);
            }
        });

    }

    function add_cursor(parent) {
        var cursor = $('
');

        cursor.css({
            width  : (x_inc - app.border_allowance) + 'px',
            height : (y_inc - app.border_allowance) + 'px'
        });

        parent.append(cursor);
        return cursor;
    }

    function show_duration_tooltip(activity) {
        var size = (activity.height() + app.border_allowance) / y_inc;
        var min  = [ '00', '15', '30', '45' ];
        var h = Math.floor(size / 4);
        var m = size % 4;
        tooltip.text(h + ':' + min[m]).show();
        tooltip.position({
          my: "left bottom",
          at: "right bottom",
          of: activity
        });
    }

    function show_start_time_tooltip(activity) {
        var p = activity.position();
        var start = p.top / y_inc;
        var min  = [ '00', '15', '30', '45' ];
        var h = Math.floor(start / 4);
        var m = start % 4;
        tooltip.text(h + ':' + (min[m] || '00')).show();
        tooltip.position({
          my: "right top",
          at: "left top",
          of: activity
        });
    }

    function hide_tooltip() {
        tooltip.hide();
    }

    function set_cursor_pos(x, y) {
        cursor.css({ left: x + 'px', top: y + 'px' });
    }

    function mousedown_handler(e) {
        var target = e.target;
        if(target.nodeName == "P") { target = target.parentNode; }
        var activity = $(target).hasClass('activity') ? target : null;
        if(activity) {
            select_activity(activity);
        }
        else if(!$(target).hasClass('cursor')) {
            mouse_position_cursor(e);
        }
    }

    function mouse_position_cursor(e) {
        unselect_activity();
        var x = Math.floor((e.layerX || e.offsetX) / x_inc) * x_inc;
        var y = Math.floor((e.layerY || e.offsetY) / y_inc) * y_inc;
        set_cursor_pos(x, y);
    }

    function edit_activity_at_cursor(e) {
        dlg_activity.dialog('open');
        e.stopPropagation();
    }

    function current_activity_data() {
        if(app.current_activity) {
            return $(app.current_activity).tmplItem().data;
        }
        return null;
    }

    function default_activity_data() {
        return {
            wr_number   : '',
            hours       : app.default_duration,
            description : ''
        };
    }

    function key_handler(e) {
        if(app.activity_dialog_active) {
            return true;
        }
        var curr = null;
        if(app.current_activity) {
            curr = $(app.current_activity).tmplItem().data;
        }
        switch(e.keyCode) {
            case keyCode.LEFT:   move_cursor(-x_inc,  0);    break;
            case keyCode.RIGHT:  move_cursor( x_inc,  0);    break;
            case keyCode.ENTER:  edit_activity_at_cursor(e); break;
            case keyCode.UP:
                move_cursor(0, -y_inc);
                break;
            case keyCode.DOWN:
                move_cursor(0, curr ? curr.hours * 4 * y_inc : y_inc);
                break;
            case keyCode.DELETE: delete_activity();       break;
            default:
                if(e.ctrlKey && e.keyCode == 67) {  // Ctrl-C
                    copy_activity();
                }
                else if(e.ctrlKey && e.keyCode == 88) {  // Ctrl-X
                    copy_activity();
                    delete_activity();
                }
                else if(e.ctrlKey && e.keyCode == 86) {  // Ctrl-V
                    paste_activity();
                }
                else {
                    return;
                }
                break;
        }
        e.preventDefault();
    }

    function move_cursor(x_delta, y_delta) {
        var p = cursor.position();

        if(x_delta < 0 && p.left > 0    ) { p.left += x_delta; }
        if(x_delta > 0 && p.left < x_max) { p.left += x_delta; }
        if(y_delta < 0 && p.top  > 0    ) { p.top  += y_delta; }
        if(y_delta > 0 && p.top  < y_max) { p.top  += y_delta; }
        var activity = find_activity_at_cursor(p);
        if(activity) {
            select_activity(activity);
        }
        else {
            unselect_activity();
            set_cursor_pos(p.left, p.top);
        }
    }

    function find_activity_at_cursor(p) {
        return activities.find('.activity').filter(function() {
            var data = $(this).tmplItem().data;
            if(p.left != data.x) { return false; }
            if(p.top < data.y)  { return false; }
            if(p.top > (data.y + data.height))  {
                return false;
            }
            set_cursor_pos(data.x, data.y);
            return true;
        })[0];
    }

    function select_activity(activity) {
        if(app.current_activity == activity) {
            return;
        }
        unselect_activity();
        if(activity) {
            app.current_activity = activity;
            var $activity = $(activity);
            var p = $activity.position();
            set_cursor_pos(p.left, p.top);
            $activity.addClass('selected');
        }
    }

    function unselect_activity() {
        if(app.current_activity) {
            $(app.current_activity).removeClass('selected');
        }
        app.current_activity = null;
    }

    function delete_activity() {
        var activity = app.current_activity;
        if(!activity) {
            app_alert('You must select an activity first');
            return;
        }
        unselect_activity();
        $(activity).remove();
    }

    function copy_activity() {
        var data = current_activity_data();
        if(!data) {
            app_alert('You must select an activity first');
            return;
        }
        app.clipboard = $.extend({}, data);
    }

    function paste_activity() {
        var data = $.extend({}, app.clipboard);
        var activity = create_activity(data);
        select_activity( activity );
    }

    function save_activity(data) {
        if(app.current_activity) {
            update_activity(data);
        }
        else {
            create_activity(data);
        }
    }

    function update_activity(data) {
        var tmpl_item = $(app.current_activity).tmplItem();
        var tmpl_data = tmpl_item.data;
        tmpl_data.wr_number   = data.wr_number;
        tmpl_data.hours       = data.hours;
        tmpl_data.description = data.description;
        tmpl_data.height      = data.hours * 4 * y_inc - app.border_allowance;
        tmpl_item.update();
    }

    function create_activity(data) {
        var p = cursor.position();
        data.height = data.hours * 4 * y_inc - app.border_allowance;
        data.x      = p.left;
        data.y      = p.top;
        var activity = $.tmpl("tmpl_activity", data);
        activities.append(activity);
        return activity;
    }

    function app_alert(message) {
        alert(message);
        week_hours.focus();           // Work-around keyboard focus being lost
        week_hours.css({'outline': 'none'});
    }

    function add_activity_dialog($app_el) {
        var div      = $('
');
        var wr_inp   = $('<input type="text" />');
        var hr_inp   = $('<input type="text" />').val(1);
        var desc_inp = $('<input type="text" />');
        div.append(
            $('
WR Number
').append(wr_inp),
            $('
Hours
').append(hr_inp),
            $('
Activity Description
').append(desc_inp)
        );

        div.dialog({
            autoOpen:      false,
            title:         "Activity Detail",
            resizable:     false,
            closeOnEscape: true,
            width:         360,
            height:        230,
            modal:         true,
            open: function() {
                app.activity_dialog_active = true;
                var data = current_activity_data() || default_activity_data();
                wr_inp.val(data.wr_number);
                hr_inp.val(data.hours);
                desc_inp.val(data.description);
                if(data.x) {
                    dlg_activity.dialog('option', 'title', 'Edit Activity');
                    desc_inp.focus();
                }
                else {
                    dlg_activity.dialog('option', 'title', 'Create Activity');
                    wr_inp.focus();
                }
            },
            close: function() {
                app.activity_dialog_active = false;
            },
            buttons:       {
                "Ok":  function() {
                    var data = {
                        wr_number   : wr_inp.val(),
                        hours       : hr_inp.val(),
                        description : desc_inp.val()
                    };
                    save_activity(data);
                    div.dialog("close");
                },
                "Cancel": function() { $(this).dialog("close"); }
            }
        });

        div.find('input').keydown(function(e) {
            if(e.keyCode == keyCode.ENTER) {
                div.parent().find('.ui-dialog-buttonpane button:first').click();
                e.stopPropagation();  // Don't allow Enter to re-launch dialog
            }
        });

        return div;
    }

};

$.fn.tksweb.defaults = {
    day_names        : [ 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun' ],
    first_hour       : 0,
    last_hour        : 23,
    scroll_to_hour   : 9,
    hour_height      : 48,  // must be a multiple of 4 and match bg-image bands
    activity_width   : 100,
    border_allowance : 2,   // top border thickness + bottom border
    default_duration : 1    // default number of hours for a new activity
};

})(jQuery);
<|endoftext|>