<commit_before>// CalendarDateSelect - a small prototype based date picker
// Version 1.1
// Questions, comments, bugs? - email the Author - Tim Harper <"timseeharper@gmail.seeom".gsub("see", "c")>
Element.addMethods({
  purgeChildren: function(element) {
    $A(element.childNodes).each(function(e){$(e).remove();});
  },
  build: function(element, type, options, style) {
    newElement = Element.build(type, options, style);
    element.appendChild(newElement);
    return newElement;
  }
});

Element.build = function(type, options, style)
{
  e = $(document.createElement(type));

  $H(options).each(function(pair) {
    eval("e." + pair.key + " = pair.value" );
  });

  if (style) $H(style).each(function(pair) {
    eval("e.style." + pair.key + " = pair.value" );
  });

  return e;
};
nil=null;

CalendarDateSelect = Class.create();
CalendarDateSelect.weekdays = ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa'];
CalendarDateSelect.months = $w("January February March April May June July August September October November December" );
CalendarDateSelect.padded2 = function(hour) { padded2 = hour.toString(); if (parseInt(hour) < 10) padded2="0" + padded2; return padded2; }
CalendarDateSelect.ampm = function(hour) { return (hour < 12) ? "AM" : "PM"; }
CalendarDateSelect.ampm_hour = function(hour) { return (hour == 0) ? 12 : (hour > 12 ? hour - 12 : hour ) }
Date.one_day = 24*60*60*1000;
Date.prototype.strip_time = function() { this.setHours(0); this.setMinutes(0); this.setSeconds(0); this.setMilliseconds(0); return this;};
Date.prototype.days_distance = function(compare_date) { return (compare_date - this) / Date.one_day; };

window.f_height = function() { return([window.innerHeight ? window.innerHeight : null, document.documentElement ? document.documentElement.clientHeight : null, document.body ? document.body.clientHeight : null].compact().first()); }
window.f_scrollTop = function() { return ([window.pageYOffset ? window.pageYOffset : null, document.documentElement ? document.documentElement.scrollTop : null, document.body ? document.body.scrollTop : null].compact().first() ); }

CalendarDateSelect.date_string = function(date, include_time){
  if (! date) return "";
  str = CalendarDateSelect.months[date.getMonth()] + " " + date.getDate().toString() + ", " + date.getFullYear().toString();

  if (include_time) { hour=date.getHours(); str += " " + CalendarDateSelect.ampm_hour(hour).toString() + ":" + CalendarDateSelect.padded2( date.getMinutes() ) + " " + CalendarDateSelect.ampm(hour) }
  return str;
}

CalendarDateSelect.prototype = {
  initialize: function(target_element, options) {
    // initialize the date control
    this.options = $H({
      embedded: false,
      time: false,
      year_range: 10,
      calendar_div: nil,
      close_on_click: nil,
      minute_interval: 5
    }).merge(options || {});

    this.target_element = $(target_element); // make sure it's an element, not a string
    this.selection_made = $F(this.target_element)!="";

    if (this.target_element.calendar_date_select)
    {
      this.target_element.calendar_date_select.close();
      return false;
    }
    this.target_element.calendar_date_select = this;

    this.calendar_div = $(this.options['calendar_div']);
    if (!this.target_element) { alert("Target element " + target_element + " not found!"); return false;}

    this.parse_date();

    // by default, stick it by the target element (if embedded, that's where we'll want it to show up)
    if (this.calendar_div == nil) { this.calendar_div = $( this.options.embedded ? this.target_element.parentNode : document.body ).build('div'); }
    if (!this.options.embedded) {
      this.calendar_div.style.position = "absolute";
      this.position_calendar_div();
    }

    this.calendar_div.addClassName("calendar_date_select");

    if (this.options["embedded"]) this.options["close_on_click"]=false;
    // logic for close on click
    if (this.options['close_on_click']===nil )
    {
      if (this.options['time'])
        this.options["close_on_click"] = false;
      else
        this.options['close_on_click'] = true;
    }

    // set the click handler to check if a user has clicked away from the document
    if(!this.options["embedded"]) Event.observe(document.body, "mousedown", this.body_click_handler=this.body_click.bindAsEventListener(this));

    this.init_frame();
    if(!this.options["embedded"]) setTimeout(function(){
      if (( parseInt(this.calendar_div.style.top) + this.calendar_div.getDimensions().height ) > (window.f_scrollTop() + window.f_height()))
        this.position_calendar_div(true);
      }.bindAsEventListener(this), 1);
  },
  position_calendar_div: function(above) {
    pos = Position.cumulativeOffset(this.target_element);

    this.calendar_div.style.left = pos[0].toString() + "px";
    if (above)
      this.calendar_div.style.top = (pos[1] - this.calendar_div.getDimensions().height ).toString() + "px";
    else
      this.calendar_div.style.top = (pos[1] + this.target_element.getDimensions().height ).toString() + "px";
  },
  init_frame: function() {
    that=this;
    // create the divs
    $w("header body time buttons footer").each(function(name) {
      eval(name + "_div = that." + name + "_div = that.calendar_div.build('div', { className: '"+name+"' }, { clear: 'left'} ); ");
    });

    this.init_time_div();
    this.init_buttons_div();

    this.update_footer(" ");
    // make the header buttons
    this.prev_month_button = header_div.build("input", { type: "button", value : "<", className: "button" });

    this.month_select = header_div.build("select");
    this.year_select = header_div.build("select");
    this.next_month_button = header_div.build("input", { type: "button", value : ">", className: "button" });

    // make the month selector
    for(x=0; x<12; x++)
      this.month_select.options[x]=new Option(CalendarDateSelect.months[x],x);
    Event.observe(this.prev_month_button, 'mousedown', function () { this.nav_month(-1) }.bindAsEventListener(this));
    Event.observe(this.next_month_button, 'mousedown', (function () { this.nav_month(1) }).bindAsEventListener(this));
    Event.observe(this.month_select, 'change', (function () { this.set_month($F(this.month_select)) }).bindAsEventListener(this));
    Event.observe(this.year_select, 'change', (function () { this.set_year($F(this.year_select)) }).bindAsEventListener(this));

    // build the calendar day grid
    this.calendar_day_grid = [];
    days_table = body_div.build("table", { cellPadding: "0px", cellSpacing: "0px", width: "100%" }).build("tbody");

    // make the weekdays!
    weekdays_row = days_table.build("tr", {className: "weekdays"});
    CalendarDateSelect.weekdays.each( function(weekday) {
      weekdays_row.build("td", {innerHTML: weekday});
    });

    // Make the days!
    for(cell_index=0; cell_index<42; cell_index++)
    {
      if ( cell_index %7==0 ) days_row = days_table.build("tr", {className: "days"});
      (this.calendar_day_grid[cell_index] = days_row.build("td", {
          calendar_date_select: this,
          onmouseover: function () { this.calendar_date_select.day_hover(this); },
          onmouseout: function () { this.calendar_date_select.day_hover_out(this) },
          onclick: function() { this.calendar_date_select.update_selected_date(this); }
        },
        { cursor: "pointer" }
      )).build("div");
      this.calendar_day_grid[cell_index];
    }
    this.refresh();
  },
  init_buttons_div: function()
  {
    buttons_div = this.buttons_div;

    buttons_div.build("input", {
      value: (this.options["time"] ? "Now" : "Today" ),
      onclick: this.today.bindAsEventListener(this),
      type: "button"
    });
    if (this.allow_close_buttons())
    {
      buttons_div.build("input", {
        value: "Ok",
        onclick: this.ok.bindAsEventListener(this),
        type: "button"
      });
      buttons_div.build("input", {
        value: "Cancel",
        onclick: this.close.bindAsEventListener(this),
        type: "button"
      });
    }
  },
  init_time_div: function()
  {
    time_div = this.time_div;
    // make the time div
    if (this.options["time"])
    {
      time_div.build("span", {innerHTML:" @ "})
      this.hour_select = time_div.build("select", {
        calendar_date_select: this,
        onchange: function() { this.calendar_date_select.update_selected_date( { hour: this.value });}
      });
      time_div.build("span", {innerHTML:"  : "});
      this.minute_select = time_div.build("select", {
        calendar_date_select: this,
        onchange: function() { this.calendar_date_select.update_selected_date( {minute: this.value }) }
      });

      // populate hours
      for(x=0;x<=23;x++) { this.hour_select.options[x] = new Option( CalendarDateSelect.ampm_hour(x).toString() + " " + CalendarDateSelect.ampm(x), x ) }
      // populate minutes
      x=0; for(m=0;m<=60;m+=this.options["minute_interval"]) { this.minute_select.options[x++] = new Option( CalendarDateSelect.padded2(m), m ) }
    } else (time_div.remove());
  },
  allow_close_buttons: function() { return ( !this.options["embedded"]); },
  nav_month: function(dir) {
    this.set_month(this.date.getMonth() + dir )
  },
  date_string: function() {
    return (this.selection_made) ? CalendarDateSelect.date_string(this.selectedDate, this.options['time']) : " ";
  },
  set_month: function(month) {
    prev_day = this.date.getDate();
    this.date.setMonth(month);

    this.refresh();
  },
  set_year: function(year) {
    this.date.setYear(year);
    this.refresh();
  },
  refresh: function ()
  {
    // set the month
    this.month_select.selectedIndex = this.date.getMonth();

    // set the year,
    range=this.options["year_range"];
    this.year_select.purgeChildren();
    for( x=0; x<=range*2; x++)
    {
      year = x+(this.date.getFullYear() - range);
      this.year_select.options[x]=new Option(year,year);
    }

    this.year_select.selectedIndex = range;

    // populate the calendar_day_grid
    iterator = new Date(this.date);
    iterator.setDate(1);
    pre_days = iterator.getDay() // draw some days before the fact
    if (pre_days < 3) pre_days+=7;

    iterator.strip_time().setDate(1 - pre_days);
    beginning_date = new Date(iterator);
    today = new Date().strip_time();
    this_month = this.date.getMonth();
    for (cell_index=0;cell_index<42; cell_index++)
    {
      day = iterator.getDate(); month = iterator.getMonth();
      cell = this.calendar_day_grid[cell_index];
      Element.remove(cell.childNodes[0]); cell.build("div", {innerHTML:day});
      cell.day=day; cell.month = month; cell.year = iterator.getFullYear();
      cell.className = month==this_month ? "" : "other"; //clear the class

      iterator.setDate( day + 1);
    }
    if ( $R(0,42).include(days_until = beginning_date.days_distance(today)) ) this.calendar_day_grid[days_until].addClassName("today");

    // set the time
    if (this.options["time"]) {
      this.hour_select.selectedIndex = this.selectedDate.getHours();
      this.minute_select.selectedIndex = this.selectedDate.getMinutes() / this.options["minute_interval"];

      this.hour_select.onchange();
      this.minute_select.onchange();
    }

    this.set_selected_class();
    this.update_footer();
  },
  day_hover: function(element) {
    element.addClassName("hover");
    hover_date = new Date(this.selectedDate);
    hover_date.setYear(element.year); hover_date.setMonth(element.month); hover_date.setDate(element.day);
    this.update_footer(CalendarDateSelect.date_string(hover_date, this.options['time']));
  },
  day_hover_out: function(element) { element.removeClassName("hover"); this.update_footer(); },
  set_selected_class: function() {
    // clear selection
    this.body_div.getElementsBySelector(".selected").each(function(e) { e.removeClassName("selected")});
    if (!this.selection_made) return;
    day = this.selectedDate.getDate(); month = this.selectedDate.getMonth();
    this.body_div.getElementsBySelector("td").each(function(e) { if ((e.day == day) && (e.month == month)) {e.addClassName("selected")} } );
  },
  reparse: function() { this.parse_date(); this.refresh(); },
  parse_date: function()
  {
    this.date = new Date(this.options['date'] || $F(this.target_element));
    if (isNaN(this.date.getDate())) this.date = new Date();
    this.selectedDate = new Date(this.date);
    this.date.setDate(1);
  },
  update_footer:function(text) { if (!text) text=this.date_string(); this.footer_div.purgeChildren(); this.footer_div.build("text", {innerHTML: text }); },
  update_selected_date:function(parts) {
    if (parts.day) {
      this.selectedDate.setDate(parts.day);
      this.selectedDate.setMonth(parts.month);
      this.selectedDate.setYear(parts.year);
      this.selection_made = true;
    }

    if (parts.hour) this.selectedDate.setHours(parts.hour);
    if (parts.minute) this.selectedDate.setMinutes(parts.minute);

    if (this.options.embedded) { this.target_element.value = this.date_string(); }
    if (this.options.close_on_click) { this.ok(); }
    this.update_footer();
    this.set_selected_class();
  },
  ok: function() {
    this.target_element.value = this.date_string();
    if (this.options.onok) this.options.onok(this);
    this.close();
  },
  today: function() {
    this.selection_made=true;
    this.selectedDate = new Date();
    this.date = new Date();
    this.refresh();
  },
  close: function() {
    this.target_element.calendar_date_select = nil;
    Event.stopObserving(document.body, "mousedown", this.body_click_handler);
    this.calendar_div.remove();
  },
  body_click: function(e) { // checks to see if somewhere other than calendar date select grid was clicked.  in which case, close
    if (! $(Event.element(e)).descendantOf(this.calendar_div) ) this.close();
  }
}<commit_msg><commit_after>// CalendarDateSelect - a small prototype based date picker
// Version 1.3
// Questions, comments, bugs? - email the Author - Tim Harper <"timseeharper@gmail.seeom".gsub("see", "c")>
Element.addMethods({
  purgeChildren: function(element) {
    $A(element.childNodes).each(function(e){$(e).remove();});
  },
  build: function(element, type, options, style) {
    newElement = Element.build(type, options, style);
    element.appendChild(newElement);
    return newElement;
  }
});

Element.build = function(type, options, style)
{
  e = $(document.createElement(type));

  $H(options).each(function(pair) {
    eval("e." + pair.key + " = pair.value" );
  });

  if (style) $H(style).each(function(pair) {
    eval("e.style." + pair.key + " = pair.value" );
  });

  return e;
};
nil=null;

CalendarDateSelect = Class.create();
CalendarDateSelect.weekdays = ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa'];
CalendarDateSelect.months = $w("January February March April May June July August September October November December" );
CalendarDateSelect.padded2 = function(hour) { padded2 = hour.toString(); if (parseInt(hour) < 10) padded2="0" + padded2; return padded2; }
CalendarDateSelect.ampm = function(hour) { return (hour < 12) ? "AM" : "PM"; }
CalendarDateSelect.ampm_hour = function(hour) { return (hour == 0) ? 12 : (hour > 12 ? hour - 12 : hour ) }
Date.one_day = 24*60*60*1000;
Date.prototype.strip_time = function() { this.setHours(0); this.setMinutes(0); this.setSeconds(0); this.setMilliseconds(0); return this;};
Date.prototype.days_distance = function(compare_date) { return (compare_date - this) / Date.one_day; };

window.f_height = function() { return([window.innerHeight ? window.innerHeight : null, document.documentElement ? document.documentElement.clientHeight : null, document.body ? document.body.clientHeight : null].compact().first()); }
window.f_scrollTop = function() { return ([window.pageYOffset ? window.pageYOffset : null, document.documentElement ? document.documentElement.scrollTop : null, document.body ? document.body.scrollTop : null].compact().first() ); }

CalendarDateSelect.date_string = function(date, include_time){
  if (! date) return "";
  str = CalendarDateSelect.months[date.getMonth()] + " " + date.getDate().toString() + ", " + date.getFullYear().toString();

  if (include_time) { hour=date.getHours(); str += " " + CalendarDateSelect.ampm_hour(hour).toString() + ":" + CalendarDateSelect.padded2( date.getMinutes() ) + " " + CalendarDateSelect.ampm(hour) }
  return str;
}

CalendarDateSelect.prototype = {
  initialize: function(target_element, options) {
    // initialize the date control
    this.options = $H({
      embedded: false,
      time: false,
      year_range: 10,
      calendar_div: nil,
      close_on_click: nil,
      minute_interval: 5
    }).merge(options || {});

    this.target_element = $(target_element); // make sure it's an element, not a string
    this.selection_made = $F(this.target_element)!="";

    if (this.target_element.calendar_date_select)
    {
      this.target_element.calendar_date_select.close();
      return false;
    }
    this.target_element.calendar_date_select = this;

    this.calendar_div = $(this.options['calendar_div']);
    if (!this.target_element) { alert("Target element " + target_element + " not found!"); return false;}

    this.parse_date();

    // by default, stick it by the target element (if embedded, that's where we'll want it to show up)
    if (this.calendar_div == nil) { this.calendar_div = $( this.options.embedded ? this.target_element.parentNode : document.body ).build('div'); }
    if (!this.options.embedded) {
      this.calendar_div.style.position = "absolute";
      this.position_calendar_div();
    }

    this.calendar_div.addClassName("calendar_date_select");

    if (this.options["embedded"]) this.options["close_on_click"]=false;
    // logic for close on click
    if (this.options['close_on_click']===nil )
    {
      if (this.options['time'])
        this.options["close_on_click"] = false;
      else
        this.options['close_on_click'] = true;
    }

    // set the click handler to check if a user has clicked away from the document
    if(!this.options["embedded"]) Event.observe(document.body, "mousedown", this.body_click_handler=this.body_click.bindAsEventListener(this));

    this.init_frame();
    if(!this.options["embedded"]) setTimeout(function(){
      if (( parseInt(this.calendar_div.style.top) + this.calendar_div.getDimensions().height ) > (window.f_scrollTop() + window.f_height()))
        this.position_calendar_div(true);
      }.bindAsEventListener(this), 1);
  },
  position_calendar_div: function(above) {
    pos = Position.cumulativeOffset(this.target_element);

    this.calendar_div.style.left = pos[0].toString() + "px";
    if (above)
      this.calendar_div.style.top = (pos[1] - this.calendar_div.getDimensions().height ).toString() + "px";
    else
      this.calendar_div.style.top = (pos[1] + this.target_element.getDimensions().height ).toString() + "px";
  },
  init_frame: function() {
    that=this;
    // create the divs
    $w("header body time buttons footer").each(function(name) {
      eval(name + "_div = that." + name + "_div = that.calendar_div.build('div', { className: '"+name+"' }, { clear: 'left'} ); ");
    });

    this.init_time_div();
    this.init_buttons_div();

    this.update_footer(" ");
    // make the header buttons
    this.prev_month_button = header_div.build("input", { type: "button", value : "<", className: "button" });

    this.month_select = header_div.build("select");
    this.year_select = header_div.build("select");
    this.next_month_button = header_div.build("input", { type: "button", value : ">", className: "button" });

    // make the month selector
    for(x=0; x<12; x++)
      this.month_select.options[x]=new Option(CalendarDateSelect.months[x],x);
    Event.observe(this.prev_month_button, 'mousedown', function () { this.nav_month(-1) }.bindAsEventListener(this));
    Event.observe(this.next_month_button, 'mousedown', (function () { this.nav_month(1) }).bindAsEventListener(this));
    Event.observe(this.month_select, 'change', (function () { this.set_month($F(this.month_select)) }).bindAsEventListener(this));
    Event.observe(this.year_select, 'change', (function () { this.set_year($F(this.year_select)) }).bindAsEventListener(this));

    // build the calendar day grid
    this.calendar_day_grid = [];
    days_table = body_div.build("table", { cellPadding: "0px", cellSpacing: "0px", width: "100%" }).build("tbody");

    // make the weekdays!
    weekdays_row = days_table.build("tr", {className: "weekdays"});
    CalendarDateSelect.weekdays.each( function(weekday) {
      weekdays_row.build("td", {innerHTML: weekday});
    });

    // Make the days!
    for(cell_index=0; cell_index<42; cell_index++)
    {
      if ( cell_index %7==0 ) days_row = days_table.build("tr", {className: "days"});
      (this.calendar_day_grid[cell_index] = days_row.build("td", {
          calendar_date_select: this,
          onmouseover: function () { this.calendar_date_select.day_hover(this); },
          onmouseout: function () { this.calendar_date_select.day_hover_out(this) },
          onclick: function() { this.calendar_date_select.update_selected_date(this); }
        },
        { cursor: "pointer" }
      )).build("div");
      this.calendar_day_grid[cell_index];
    }
    this.refresh();
  },
  init_buttons_div: function()
  {
    buttons_div = this.buttons_div;

    buttons_div.build("input", {
      value: (this.options["time"] ? "Now" : "Today" ),
      onclick: this.today.bindAsEventListener(this),
      type: "button"
    });
    if (this.allow_close_buttons())
    {
      buttons_div.build("input", {
        value: "Ok",
        onclick: this.ok.bindAsEventListener(this),
        type: "button"
      });
      buttons_div.build("input", {
        value: "Cancel",
        onclick: this.close.bindAsEventListener(this),
        type: "button"
      });
    }
  },
  init_time_div: function()
  {
    time_div = this.time_div;
    // make the time div
    if (this.options["time"])
    {
      time_div.build("span", {innerHTML:" @ "})
      this.hour_select = time_div.build("select", {
        calendar_date_select: this,
        onchange: function() { this.calendar_date_select.update_selected_date( { hour: this.value });}
      });
      time_div.build("span", {innerHTML:"  : "});
      this.minute_select = time_div.build("select", {
        calendar_date_select: this,
        onchange: function() { this.calendar_date_select.update_selected_date( {minute: this.value }) }
      });

      // populate hours
      for(x=0;x<=23;x++) { this.hour_select.options[x] = new Option( CalendarDateSelect.ampm_hour(x).toString() + " " + CalendarDateSelect.ampm(x), x ) }
      // populate minutes
      x=0; for(m=0;m<=60;m+=this.options["minute_interval"]) { this.minute_select.options[x++] = new Option( CalendarDateSelect.padded2(m), m ) }
    } else (time_div.remove());
  },
  allow_close_buttons: function() { return ( !this.options["embedded"]); },
  nav_month: function(dir) {
    this.set_month(this.date.getMonth() + dir )
  },
  date_string: function() {
    return (this.selection_made) ? CalendarDateSelect.date_string(this.selectedDate, this.options['time']) : " ";
  },
  set_month: function(month) {
    prev_day = this.date.getDate();
    this.date.setMonth(month);

    this.refresh();
  },
  set_year: function(year) {
    this.date.setYear(year);
    this.refresh();
  },
  refresh: function ()
  {
    // set the month
    this.month_select.selectedIndex = this.date.getMonth();

    // set the year,
    range=this.options["year_range"];
    this.year_select.purgeChildren();
    for( x=0; x<=range*2; x++)
    {
      year = x+(this.date.getFullYear() - range);
      this.year_select.options[x]=new Option(year,year);
    }

    this.year_select.selectedIndex = range;

    // populate the calendar_day_grid
    iterator = new Date(this.date);
    iterator.setDate(1);
    pre_days = iterator.getDay() // draw some days before the fact
    if (pre_days < 3) pre_days+=7;

    iterator.strip_time().setDate(1 - pre_days);
    beginning_date = new Date(iterator);
    today = new Date().strip_time();
    this_month = this.date.getMonth();
    for (cell_index=0;cell_index<42; cell_index++)
    {
      day = iterator.getDate(); month = iterator.getMonth();
      cell = this.calendar_day_grid[cell_index];
      Element.remove(cell.childNodes[0]); cell.build("div", {innerHTML:day});
      cell.day=day; cell.month = month; cell.year = iterator.getFullYear();
      cell.className = month==this_month ? "" : "other"; //clear the class

      iterator.setDate( day + 1);
    }
    if ( $R(0,42).include(days_until = beginning_date.days_distance(today)) ) this.calendar_day_grid[days_until].addClassName("today");

    // set the time
    if (this.options["time"]) {
      this.hour_select.selectedIndex = this.selectedDate.getHours();
      this.minute_select.selectedIndex = this.selectedDate.getMinutes() / this.options["minute_interval"];

      this.hour_select.onchange();
      this.minute_select.onchange();
    }

    this.set_selected_class();
    this.update_footer();
  },
  day_hover: function(element) {
    element.addClassName("hover");
    hover_date = new Date(this.selectedDate);
    hover_date.setYear(element.year); hover_date.setMonth(element.month); hover_date.setDate(element.day);
    this.update_footer(CalendarDateSelect.date_string(hover_date, this.options['time']));
  },
  day_hover_out: function(element) { element.removeClassName("hover"); this.update_footer(); },
  set_selected_class: function() {
    // clear selection
    this.body_div.getElementsBySelector(".selected").each(function(e) { e.removeClassName("selected")});
    if (!this.selection_made) return;
    day = this.selectedDate.getDate(); month = this.selectedDate.getMonth();
    this.body_div.getElementsBySelector("td").each(function(e) { if ((e.day == day) && (e.month == month)) {e.addClassName("selected")} } );
  },
  reparse: function() { this.parse_date(); this.refresh(); },
  parse_date: function()
  {
    this.date = new Date(this.options['date'] || $F(this.target_element));
    if (isNaN(this.date.getDate())) this.date = new Date();
    this.selectedDate = new Date(this.date);
    this.date.setDate(1);
  },
  update_footer:function(text) { if (!text) text=this.date_string(); this.footer_div.purgeChildren(); this.footer_div.build("text", {innerHTML: text }); },
  update_selected_date:function(parts) {
    if (parts.day) {
      this.selectedDate.setDate(parts.day);
      this.selectedDate.setMonth(parts.month);
      this.selectedDate.setYear(parts.year);
      this.selection_made = true;
    }

    if (parts.hour) this.selectedDate.setHours(parts.hour);
    if (parts.minute) this.selectedDate.setMinutes(parts.minute);

    if (this.options.embedded) { this.target_element.value = this.date_string(); }
    if (this.options.close_on_click) { this.ok(); }
    this.update_footer();
    this.set_selected_class();
  },
  ok: function() {
    this.target_element.value = this.date_string();
    if (this.options.onok) this.options.onok(this);
    this.close();
  },
  today: function() {
    this.selection_made=true;
    this.selectedDate = new Date();
    this.date = new Date();
    this.refresh();
  },
  close: function() {
    this.target_element.calendar_date_select = nil;
    Event.stopObserving(document.body, "mousedown", this.body_click_handler);
    this.calendar_div.remove();
  },
  body_click: function(e) { // checks to see if somewhere other than calendar date select grid was clicked.  in which case, close
    if (! $(Event.element(e)).descendantOf(this.calendar_div) ) this.close();
  }
}<|endoftext|>


<commit_before>
var ObjectGraph = require('../util/ObjectGraph'),
	Navigator = require('./Navigator'),
	RequestLocals = require('../util/RequestLocalStorage').getNamespace(),
	Q = require('q'),
	TritonAgent = require('../util/TritonAgent'),
	config = require('../config'),
	logger = require('../logging').getLogger(__LOGGER__);

// TODO FIXME
var REFERRER_DOMAIN = "http://node.redfintest.com";

class RequestContext {

	constructor (routes, defaultHeaders, extraOpts) {

		// don't include headers client-side (browser has them already)
		if (!SERVER_SIDE || !defaultHeaders) {
			defaultHeaders = {};
		}

		if (SERVER_SIDE && defaultHeaders) {
			// stored in RequestLocalStorage
			TritonAgent.plugRequest(function (dataRequest) {
				dataRequest.set(defaultHeaders);
			});
		}

		var apiServerPrefix = SERVER_SIDE
				? config().internal.apiServerPrefix
				: config().apiServerPrefix;
		logger.debug(`Using API server prefix: ${apiServerPrefix}`);
		TritonAgent.plugRequest(function (dataRequest) {
			dataRequest.setUrlPrefix(apiServerPrefix);
		});

		this.navigator = new Navigator(this, routes);

		this._navigateListeners = [];

		RequestLocals().instance = this;
	}

	static getCurrentRequestContext () {
		return RequestLocals().instance;
	}

	setDataLoadWait (ms) {
		this.dataLoadWait = ms
		return this
	}

	getDataLoadWait (ms) {
		return this.dataLoadWait
	}

	setServerStash (stash) {
		this.serverStash = stash;
		return this;
	}

	getServerStash () {
		return this.serverStash;
	}

	onNavigate (callback) {
		this.navigator.on('navigateDone', callback);
	}

	onNavigateStart (callback) {
		this.navigator.on('navigateStart', callback);
	}

	navigate (request, type) {
		this.navigator.navigate(request, type);
	}

	dehydrate () {
		return {
			'TritonAgent.cache': TritonAgent.cache().dehydrate()
		}
	}

	rehydrate (state) {
		TritonAgent.cache().rehydrate(state['TritonAgent.cache']);
	}

}

class RequestContextBuilder {

	constructor () {
		this.defaultHeaders = {};
	}

	setRoutes(routes) {
		this.routes = routes;
		return this;
	}

	setDefaultXhrHeadersFromRequest (req) {
		var defaultHeaders = {};
		if (req) {
			defaultHeaders['Cookie'] = req.get('cookie');
			defaultHeaders['Referer'] = REFERRER_DOMAIN;
		}
		this.defaultHeaders = defaultHeaders;
		return this;
	}

	create (extraOpts) {

		return new RequestContext(this.routes, this.defaultHeaders, extraOpts);
	}

}

module.exports = RequestContext;
module.exports.Builder = RequestContextBuilder;

<commit_msg>Don't set cookie unless present<commit_after>
var ObjectGraph = require('../util/ObjectGraph'),
	Navigator = require('./Navigator'),
	RequestLocals = require('../util/RequestLocalStorage').getNamespace(),
	Q = require('q'),
	TritonAgent = require('../util/TritonAgent'),
	config = require('../config'),
	logger = require('../logging').getLogger(__LOGGER__);

// TODO FIXME
var REFERRER_DOMAIN = "http://node.redfintest.com";

class RequestContext {

	constructor (routes, defaultHeaders, extraOpts) {

		// don't include headers client-side (browser has them already)
		if (!SERVER_SIDE || !defaultHeaders) {
			defaultHeaders = {};
		}

		if (SERVER_SIDE && defaultHeaders) {
			// stored in RequestLocalStorage
			TritonAgent.plugRequest(function (dataRequest) {
				dataRequest.set(defaultHeaders);
			});
		}

		var apiServerPrefix = SERVER_SIDE
				? config().internal.apiServerPrefix
				: config().apiServerPrefix;
		logger.debug(`Using API server prefix: ${apiServerPrefix}`);
		TritonAgent.plugRequest(function (dataRequest) {
			dataRequest.setUrlPrefix(apiServerPrefix);
		});

		this.navigator = new Navigator(this, routes);

		this._navigateListeners = [];

		RequestLocals().instance = this;
	}

	static getCurrentRequestContext () {
		return RequestLocals().instance;
	}

	setDataLoadWait (ms) {
		this.dataLoadWait = ms
		return this
	}

	getDataLoadWait (ms) {
		return this.dataLoadWait
	}

	setServerStash (stash) {
		this.serverStash = stash;
		return this;
	}

	getServerStash () {
		return this.serverStash;
	}

	onNavigate (callback) {
		this.navigator.on('navigateDone', callback);
	}

	onNavigateStart (callback) {
		this.navigator.on('navigateStart', callback);
	}

	navigate (request, type) {
		this.navigator.navigate(request, type);
	}

	dehydrate () {
		return {
			'TritonAgent.cache': TritonAgent.cache().dehydrate()
		}
	}

	rehydrate (state) {
		TritonAgent.cache().rehydrate(state['TritonAgent.cache']);
	}

}

class RequestContextBuilder {

	constructor () {
		this.defaultHeaders = {};
	}

	setRoutes(routes) {
		this.routes = routes;
		return this;
	}

	setDefaultXhrHeadersFromRequest (req) {
		var defaultHeaders = {};
		if (req) {
			// Only want to set this if it's present.  Newer
			// versions of node (rightly) dislike empty header
			// values.
			if (req.get('cookie')){
				defaultHeaders['Cookie'] = req.get('cookie');
			}
			defaultHeaders['Referer'] = REFERRER_DOMAIN;
		}
		this.defaultHeaders = defaultHeaders;
		return this;
	}

	create (extraOpts) {

		return new RequestContext(this.routes, this.defaultHeaders, extraOpts);
	}

}

module.exports = RequestContext;
module.exports.Builder = RequestContextBuilder;

<|endoftext|>


<commit_before>/*
 * grunt-contrib-copy
 * http://gruntjs.com/
 *
 * Copyright (c) 2016 Chris Talkington, contributors
 * Licensed under the MIT license.
 * https://github.com/gruntjs/grunt-contrib-copy/blob/master/LICENSE-MIT
 */

module.exports = function(grunt) {
  'use strict';

  var path = require('path');
  var fs = require('fs');
  var chalk = require('chalk');
  var fileSyncCmp = require('file-sync-cmp');

  grunt.registerMultiTask('copy', 'Copy files.', function() {

    var options = this.options({
      encoding: grunt.file.defaultEncoding,
      // processContent/processContentExclude deprecated renamed to process/noProcess
      processContent: false,
      processContentExclude: [],
      timestamp: false,
      mode: false,
    });

    var copyOptions = {
      encoding: options.encoding,
      process: options.process || options.processContent,
      noProcess: options.noProcess || options.processContentExclude,
    };

    var isExpandedPair;
    var dirs = {};
    var tally = {
      dirs: 0,
      files: 0,
    };

    this.files.forEach(function(filePair) {
      isExpandedPair = filePair.orig.expand || false;

      filePair.src.forEach(function(src) {
        src = unixifyPath(src);
        var dest = unixifyPath(filePair.dest);

        if (detectDestType(dest) === 'directory') {
          dest = (isExpandedPair) ? dest : path.join(dest, src);
        }

        if (grunt.file.isDir(src)) {
          grunt.verbose.writeln('Creating ' + chalk.cyan(dest));
          grunt.file.mkdir(dest);
          if (options.mode !== false) {
            fs.chmodSync(dest, (options.mode === true) ? fs.lstatSync(src).mode : options.mode);
          }

          if (options.timestamp) {
            dirs[dest] = src;
          }

          tally.dirs++;
        } else {
          grunt.verbose.writeln('Copying ' + chalk.cyan(src) + ' -> ' + chalk.cyan(dest));
          grunt.file.copy(src, dest, copyOptions);
          syncTimestamp(src, dest);
          if (options.mode !== false) {
            fs.chmodSync(dest, (options.mode === true) ? fs.lstatSync(src).mode : options.mode);
          }
          tally.files++;
        }
      });
    });

    if (options.timestamp) {
      Object.keys(dirs).sort(function (a, b) {
        return b.length - a.length;
      }).forEach(function (dest) {
        syncTimestamp(dirs[dest], dest);
      });
    }

    if (tally.dirs) {
      grunt.log.write('Created ' + chalk.cyan(tally.dirs.toString()) + (tally.dirs === 1 ? ' directory' : ' directories'));
    }

    if (tally.files) {
      grunt.log.write((tally.dirs ? ', copied ' : 'Copied ') + chalk.cyan(tally.files.toString()) + (tally.files === 1 ? ' file' : ' files'));
    }

    grunt.log.writeln();
  });

  var detectDestType = function(dest) {
    if (grunt.util._.endsWith(dest, '/')) {
      return 'directory';
    } else {
      return 'file';
    }
  };

  var unixifyPath = function(filepath) {
    if (process.platform === 'win32') {
      return filepath.replace(/\\/g, '/');
    } else {
      return filepath;
    }
  };

  var syncTimestamp = function (src, dest) {
    var stat = fs.lstatSync(src);
    if (path.basename(src) !== path.basename(dest)) {
      return;
    }

    if (stat.isFile() && !fileSyncCmp.equalFiles(src, dest)) {
      return;
    }

    fs.utimesSync(dest, stat.atime, stat.mtime);
  };
};
<commit_msg>Minor lint tweaks.<commit_after>/*
 * grunt-contrib-copy
 * http://gruntjs.com/
 *
 * Copyright (c) 2016 Chris Talkington, contributors
 * Licensed under the MIT license.
 * https://github.com/gruntjs/grunt-contrib-copy/blob/master/LICENSE-MIT
 */

'use strict';

module.exports = function(grunt) {
  var path = require('path');
  var fs = require('fs');
  var chalk = require('chalk');
  var fileSyncCmp = require('file-sync-cmp');

  grunt.registerMultiTask('copy', 'Copy files.', function() {

    var options = this.options({
      encoding: grunt.file.defaultEncoding,
      // processContent/processContentExclude deprecated renamed to process/noProcess
      processContent: false,
      processContentExclude: [],
      timestamp: false,
      mode: false
    });

    var copyOptions = {
      encoding: options.encoding,
      process: options.process || options.processContent,
      noProcess: options.noProcess || options.processContentExclude
    };

    var isExpandedPair;
    var dirs = {};
    var tally = {
      dirs: 0,
      files: 0
    };

    this.files.forEach(function(filePair) {
      isExpandedPair = filePair.orig.expand || false;

      filePair.src.forEach(function(src) {
        src = unixifyPath(src);
        var dest = unixifyPath(filePair.dest);

        if (detectDestType(dest) === 'directory') {
          dest = isExpandedPair ? dest : path.join(dest, src);
        }

        if (grunt.file.isDir(src)) {
          grunt.verbose.writeln('Creating ' + chalk.cyan(dest));
          grunt.file.mkdir(dest);
          if (options.mode !== false) {
            fs.chmodSync(dest, (options.mode === true) ? fs.lstatSync(src).mode : options.mode);
          }

          if (options.timestamp) {
            dirs[dest] = src;
          }

          tally.dirs++;
        } else {
          grunt.verbose.writeln('Copying ' + chalk.cyan(src) + ' -> ' + chalk.cyan(dest));
          grunt.file.copy(src, dest, copyOptions);
          syncTimestamp(src, dest);
          if (options.mode !== false) {
            fs.chmodSync(dest, (options.mode === true) ? fs.lstatSync(src).mode : options.mode);
          }
          tally.files++;
        }
      });
    });

    if (options.timestamp) {
      Object.keys(dirs).sort(function (a, b) {
        return b.length - a.length;
      }).forEach(function (dest) {
        syncTimestamp(dirs[dest], dest);
      });
    }

    if (tally.dirs) {
      grunt.log.write('Created ' + chalk.cyan(tally.dirs.toString()) + (tally.dirs === 1 ? ' directory' : ' directories'));
    }

    if (tally.files) {
      grunt.log.write((tally.dirs ? ', copied ' : 'Copied ') + chalk.cyan(tally.files.toString()) + (tally.files === 1 ? ' file' : ' files'));
    }

    grunt.log.writeln();
  });

  var detectDestType = function(dest) {
    if (grunt.util._.endsWith(dest, '/')) {
      return 'directory';
    } else {
      return 'file';
    }
  };

  var unixifyPath = function(filepath) {
    if (process.platform === 'win32') {
      return filepath.replace(/\\/g, '/');
    } else {
      return filepath;
    }
  };

  var syncTimestamp = function (src, dest) {
    var stat = fs.lstatSync(src);
    if (path.basename(src) !== path.basename(dest)) {
      return;
    }

    if (stat.isFile() && !fileSyncCmp.equalFiles(src, dest)) {
      return;
    }

    fs.utimesSync(dest, stat.atime, stat.mtime);
  };
};
<|endoftext|>


<commit_before>// Only initiate once when the file is load twice or more
/*if (typeof gc2map === "undefined") {
 var gc2map;
 gc2map = (function () {
 "use strict";
 window.__ = function (string) {
 if (typeof gc2i18n !== 'undefined') {
 if (gc2i18n.dict[string]) {
 return gc2i18n.dict[string];
 }
 }
 return string;
 };
 var init, js, maps = [],
 scriptSource = (function () {
 var scripts = document.getElementsByTagName('script'),
 script = scripts[scripts.length - 1];
 if (script.getAttribute.length !== undefined) {
 return script.src;
 }
 return script.getAttribute('src', -1);
 }()),
 scriptHost, host;
 if (typeof window.geocloud_host === "undefined") {
 window.geocloud_host = (scriptSource.charAt(0) === "/") ? "" : scriptSource.split("/")[0] + "//" + scriptSource.split("/")[2];
 }
 scriptHost = host = window.geocloud_host;
 if (typeof $ === "undefined") {
 document.write("


<commit_before>'use strict';

const electron        = require('electron'),
    windowStateKeeper = require('electron-window-state'),
    path              = require('path'),
    openUrl           = require('open');

const {app, BrowserWindow, Menu, Tray, protocol} = electron;

let argv = require('minimist')(process.argv.slice(1)),
    win  = null,
    appHelper;

// Show command line help
if (argv.help || argv.h) {
    console.log('Usage: laverna [options]\r\n');
    console.log('--dev ', 'Show developer tools automatically on start');
    console.log('--tray', 'Hide to tray on start');

    return app.quit();
}

appHelper = {

    // The page which will be loaded in the window
    page: 'http://localhost:9000/',

    // Icon of the app
    icon: path.join(__dirname, '/dist/images/icon/icon-120x120.png'),

    // The main menu
    menu: [
        {
            label   : 'File',
            submenu : [
                {
                    label       : 'Quit',
                    accelerator : 'CmdOrCtrl+Q',
                    click       : function() {
                        app.quit();
                    }
                }
            ]
        },
        {
            label: 'Edit',
            submenu: [
                {
                    label       : 'Undo',
                    accelerator : 'CmdOrCtrl+Z',
                    role        : 'undo'
                },
                {
                    label       : 'Redo',
                    accelerator : 'Shift+CmdOrCtrl+Z',
                    role        : 'redo'
                },
                {type: 'separator'},
                {
                    label       : 'Cut',
                    accelerator : 'CmdOrCtrl+X',
                    role        : 'cut'
                },
                {
                    label       : 'Copy',
                    accelerator : 'CmdOrCtrl+C',
                    role        : 'copy'
                },
                {
                    label       : 'Paste',
                    accelerator : 'CmdOrCtrl+V',
                    role        : 'paste'
                },
                {type: 'separator'},
                {
                    label       : 'Select All',
                    accelerator : 'CmdOrCtrl+A',
                    role        : 'selectall'
                },
            ]
        },
        {
            label: 'View',
            submenu: [
                {
                    label       : 'Reload',
                    accelerator : 'CmdOrCtrl+R',
                    click       : function(item, focusedWindow) {
                        if (focusedWindow) { focusedWindow.reload(); }
                    }
                },
                {
                    label       : 'Toggle Developer Tools',
                    accelerator : process.platform === 'darwin' ? 'Alt+Command+I' : 'Ctrl+Shift+I',
                    click       : function(item, focusedWindow) {
                        if (focusedWindow) {
                            focusedWindow.webContents.toggleDevTools();
                        }
                    }
                }
            ]
        },
    ],

    // Tray menu (shown on right click)
    menuTray: [
        {
            label: 'Quit',
            click: function() {
                app.quit();
            }
        }
    ],

    /**
     * Electron is ready.
     */
    onReady: function() {
        this
        .interceptProtocol()
        .createWindow()
        .createMenu()
        .createTray()
        .registerEvents();
    },

    /**
     * Override HTTP protocol. The reason:
     * In order to make oAuth authentifications to Dropbox/RemoteStorage,
     * we need to serve the app from https? protocol and have relative paths.
     */
    interceptProtocol: function() {
        protocol.interceptHttpProtocol('http', function(req, callback) {

            if (req.url.search(appHelper.page) > - 1) {

                // Remove the domain, path, and hash location
                req.url = req.url.replace(appHelper.page, '');
                req.url = req.url.split('#')[0] || 'index.html';

                // Serve the resource from the file system
                req.url = 'file:///' + path.normalize(__dirname + '/dist/' + (req.url || 'index.html'));
            }

            callback(req);

        });

        return this;
    },

    /**
     * Create the main window.
     */
    createWindow: function() {

        // Recover window state (width, height, and x&y position)
        this.state = windowStateKeeper('main', {
            width  : 1000,
            height : 600
        });

        // Create new browser window
        win = new BrowserWindow({
            width  : this.state.width,
            height : this.state.height,
            x      : this.state.x,
            y      : this.state.y,

            title           : 'Laverna',
            icon            : this.icon,
            autoHideMenuBar : true,
            backgroundColor : '#00a693',

            webPreferences      : {
                nodeIntegration : true,
                preload         : path.resolve(path.join(__dirname, 'preload.js')),
            },
        });

        if (this.state.isMaximized) {
            win.maximize();
        }

        // Load the app
        win.loadURL(this.page);

        // Show development tools
        if (process.env.NODE_ENV === 'dev' || argv.dev) {
            win.webContents.openDevTools();
        }

        return this;
    },

    /**
     * Create the main menu.
     */
    createMenu: function() {

        // Slightly different menu on OS X
        if (process.platform === 'darwin') {
            this.menu[0] = {
                label   : 'Laverna',
                submenu : this.menu[0].submenu
            };
        }

        let menu = Menu.buildFromTemplate(this.menu);
        Menu.setApplicationMenu(menu);

        return this;
    },

    /**
     * Create tray icon.
     */
    createTray: function() {
        let icon = new Tray(this.icon),
            menu = Menu.buildFromTemplate(this.menuTray);

        icon.setToolTip('Laverna');
        icon.setContextMenu(menu);

        // Auto hide to tray on start
        if (argv.tray) {
            win.hide();
        }

        // Hide the window into tray on click
        icon.on('click', function() {
            if (win.isVisible()) {
                return win.hide();
            }
            win.show();
        });

        return this;
    },

    /**
     * Listen to window events.
     */
    registerEvents: function() {

        // Save window state (width, height, and x&y position)
        win.on('close', function() {
            this.state.saveState(win);
        }.bind(appHelper));

        win.on('closed', function() {
            win = null;
        });

        win.webContents.on('will-navigate', appHelper.onNavigate.bind(appHelper));
        win.webContents.on('new-window', appHelper.onNavigate.bind(appHelper));

        // Disable nodeIntegration
        // win.webContents.on('new-window', appHelper.onNewWindow.bind(appHelper));
    },

    /**
     * Open URLs in an external browser.
     */
    onNavigate: function(e, url) {
        if (url.search(this.page) === -1 &&
            url.search(/(oauth|sign_in)/) === -1 &&
            url.search(/^blob:/) === -1) {

            e.preventDefault();
            return openUrl(url);
        }
    },

    /**
     * Disable nodeIntegration on all pages except for app's.
     */
    onNewWindow: function(e, url) {
        if (url.search(this.page) === -1 &&
            url.search(/^blob:/) === -1) {

            e.preventDefault();

            let extWin = new BrowserWindow({
                width  : 600,
                height : 600,
                autoHideMenuBar     : true,
                webPreferences      : {
                    nodeIntegration : false,
                },
            });

            // Load the app
            extWin.loadURL(url);
            extWin.on('closed', function() { extWin = null; });
        }
    },

};

// Create browser window when Electron is ready
app.on('ready', appHelper.onReady.bind(appHelper));

// Quit when all windows are closed.
app.on('window-all-closed', function() {
    /*
     * On OS X it is common for applications and their menu bar
     * to stay active until the user quits explicitly with Cmd + Q
     */
    if (process.platform !== 'darwin') {
        app.quit();
    }
});

app.on('activate', function() {
    /*
     * On OS X it's common to re-create a window in the app when the
     * dock icon is clicked and there are no other windows open.
     */
    if (win === null) {
        appHelper.onReady();
    }
});
<commit_msg>Allow to change the directory where data is stored<commit_after>'use strict';

const electron        = require('electron'),
    windowStateKeeper = require('electron-window-state'),
    path              = require('path'),
    openUrl           = require('open');

const {app, BrowserWindow, Menu, Tray, protocol} = electron;

let argv = require('minimist')(process.argv.slice(1)),
    win  = null,
    appHelper;

// Show command line help
if (argv.help || argv.h) {
    console.log('Usage: laverna [options]\r\n');
    console.log('--help', 'Show this help');
    console.log('--dev ', 'Show developer tools automatically on start');
    console.log('--tray', 'Hide to tray on start');
    console.log(
        '--data-dir',
        'Directory where data is stored.',
        'Example: laverna --data-dir=../data'
    );

    return app.quit();
}

// Allow to change the directory where the data is stored
if (argv['data-dir'] && argv['data-dir'].trim()) {
    let dataDir = argv['data-dir'].trim();

    // The same or parent directory
    if (dataDir.search(/^\./) > - 1) {

        // Always store data in a separate directory
        dataDir += (dataDir.search(/^\.{1,2}$/) > -1 ? '/laverna-data' : '');

        dataDir = path.join(__dirname, dataDir);
    }

    app.setPath('userData', path.normalize(dataDir));
}

appHelper = {

    // The page which will be loaded in the window
    page: 'http://localhost:9000/',

    // Icon of the app
    icon: path.join(__dirname, '/dist/images/icon/icon-120x120.png'),

    // The main menu
    menu: [
        {
            label   : 'File',
            submenu : [
                {
                    label       : 'Quit',
                    accelerator : 'CmdOrCtrl+Q',
                    click       : function() {
                        app.quit();
                    }
                }
            ]
        },
        {
            label: 'Edit',
            submenu: [
                {
                    label       : 'Undo',
                    accelerator : 'CmdOrCtrl+Z',
                    role        : 'undo'
                },
                {
                    label       : 'Redo',
                    accelerator : 'Shift+CmdOrCtrl+Z',
                    role        : 'redo'
                },
                {type: 'separator'},
                {
                    label       : 'Cut',
                    accelerator : 'CmdOrCtrl+X',
                    role        : 'cut'
                },
                {
                    label       : 'Copy',
                    accelerator : 'CmdOrCtrl+C',
                    role        : 'copy'
                },
                {
                    label       : 'Paste',
                    accelerator : 'CmdOrCtrl+V',
                    role        : 'paste'
                },
                {type: 'separator'},
                {
                    label       : 'Select All',
                    accelerator : 'CmdOrCtrl+A',
                    role        : 'selectall'
                },
            ]
        },
        {
            label: 'View',
            submenu: [
                {
                    label       : 'Reload',
                    accelerator : 'CmdOrCtrl+R',
                    click       : function(item, focusedWindow) {
                        if (focusedWindow) { focusedWindow.reload(); }
                    }
                },
                {
                    label       : 'Toggle Developer Tools',
                    accelerator : process.platform === 'darwin' ? 'Alt+Command+I' : 'Ctrl+Shift+I',
                    click       : function(item, focusedWindow) {
                        if (focusedWindow) {
                            focusedWindow.webContents.toggleDevTools();
                        }
                    }
                }
            ]
        },
    ],

    // Tray menu (shown on right click)
    menuTray: [
        {
            label: 'Quit',
            click: function() {
                app.quit();
            }
        }
    ],

    /**
     * Electron is ready.
     */
    onReady: function() {
        this
        .interceptProtocol()
        .createWindow()
        .createMenu()
        .createTray()
        .registerEvents();
    },

    /**
     * Override HTTP protocol. The reason:
     * In order to make oAuth authentifications to Dropbox/RemoteStorage,
     * we need to serve the app from https? protocol and have relative paths.
     */
    interceptProtocol: function() {
        protocol.interceptHttpProtocol('http', function(req, callback) {

            if (req.url.search(appHelper.page) > - 1) {

                // Remove the domain, path, and hash location
                req.url = req.url.replace(appHelper.page, '');
                req.url = req.url.split('#')[0] || 'index.html';

                // Serve the resource from the file system
                req.url = 'file:///' + path.normalize(__dirname + '/dist/' + (req.url || 'index.html'));
            }

            callback(req);

        });

        return this;
    },

    /**
     * Create the main window.
     */
    createWindow: function() {

        // Recover window state (width, height, and x&y position)
        this.state = windowStateKeeper('main', {
            width  : 1000,
            height : 600
        });

        // Create new browser window
        win = new BrowserWindow({
            width  : this.state.width,
            height : this.state.height,
            x      : this.state.x,
            y      : this.state.y,

            title           : 'Laverna',
            icon            : this.icon,
            autoHideMenuBar : true,
            backgroundColor : '#00a693',

            webPreferences      : {
                nodeIntegration : true,
                preload         : path.resolve(path.join(__dirname, 'preload.js')),
            },
        });

        if (this.state.isMaximized) {
            win.maximize();
        }

        // Load the app
        win.loadURL(this.page);

        // Show development tools
        if (process.env.NODE_ENV === 'dev' || argv.dev) {
            win.webContents.openDevTools();
        }

        return this;
    },

    /**
     * Create the main menu.
     */
    createMenu: function() {

        // Slightly different menu on OS X
        if (process.platform === 'darwin') {
            this.menu[0] = {
                label   : 'Laverna',
                submenu : this.menu[0].submenu
            };
        }

        let menu = Menu.buildFromTemplate(this.menu);
        Menu.setApplicationMenu(menu);

        return this;
    },

    /**
     * Create tray icon.
     */
    createTray: function() {
        let icon = new Tray(this.icon),
            menu = Menu.buildFromTemplate(this.menuTray);

        icon.setToolTip('Laverna');
        icon.setContextMenu(menu);

        // Auto hide to tray on start
        if (argv.tray) {
            win.hide();
        }

        // Hide the window into tray on click
        icon.on('click', function() {
            if (win.isVisible()) {
                return win.hide();
            }
            win.show();
        });

        return this;
    },

    /**
     * Listen to window events.
     */
    registerEvents: function() {

        // Save window state (width, height, and x&y position)
        win.on('close', function() {
            this.state.saveState(win);
        }.bind(appHelper));

        win.on('closed', function() {
            win = null;
        });

        win.webContents.on('will-navigate', appHelper.onNavigate.bind(appHelper));
        win.webContents.on('new-window', appHelper.onNavigate.bind(appHelper));

        // Disable nodeIntegration
        // win.webContents.on('new-window', appHelper.onNewWindow.bind(appHelper));
    },

    /**
     * Open URLs in an external browser.
     */
    onNavigate: function(e, url) {
        if (url.search(this.page) === -1 &&
            url.search(/(oauth|sign_in)/) === -1 &&
            url.search(/^blob:/) === -1) {

            e.preventDefault();
            return openUrl(url);
        }
    },

    /**
     * Disable nodeIntegration on all pages except for app's.
     */
    onNewWindow: function(e, url) {
        if (url.search(this.page) === -1 &&
            url.search(/^blob:/) === -1) {

            e.preventDefault();

            let extWin = new BrowserWindow({
                width  : 600,
                height : 600,
                autoHideMenuBar     : true,
                webPreferences      : {
                    nodeIntegration : false,
                },
            });

            // Load the app
            extWin.loadURL(url);
            extWin.on('closed', function() { extWin = null; });
        }
    },

};

// Create browser window when Electron is ready
app.on('ready', appHelper.onReady.bind(appHelper));

// Quit when all windows are closed.
app.on('window-all-closed', function() {
    /*
     * On OS X it is common for applications and their menu bar
     * to stay active until the user quits explicitly with Cmd + Q
     */
    if (process.platform !== 'darwin') {
        app.quit();
    }
});

app.on('activate', function() {
    /*
     * On OS X it's common to re-create a window in the app when the
     * dock icon is clicked and there are no other windows open.
     */
    if (win === null) {
        appHelper.onReady();
    }
});
<|endoftext|>


<commit_before>"use strict";
var https = require('https'),
    Q = require('q'),
    querystring = require('querystring'),
    Methods = {
        GET: "GET",
        POST: "POST"
    },
    ChangeTipException                    = require('./changetip-exception'),
    CHANGETIP_DEFAULT_VERSION             = "2",
    CHANGETIP_DEFAULT_HOST                = "api.changetip.com",
    CHANGETIP_DEFAULT_AUTHENTICATION_TYPE = "access_token",
    instance;

/**
 * @typedef ChangeTipConfig
 * @type {object}
 * @property {string} api_key_or_access_token API Key for ChangeTip
 * @property {string} host Host URL for all remote API requests (defaults to CHANGETIP_DEFAULT_HOST)
 * @property {number} api_version Base URL for all remote API requests (defaults to CHANGETIP_DEFAULT_VERSION)
 * @property {string} authentication_type Choose to pass either an API Key or an Access Token (defaults to CHANGETIP_DEFAULT_AUTHENTICATION_TYPE)
 */

/**
 * ChangeTip API
 * @param {ChangeTipConfig} [config]
 * @constructor
 */
var ChangeTip = function (config) {
    // If information was passed to constructor auto init
    if (config) {
        this.init(config);
    }
};

ChangeTip.prototype = {
    /**
     * API Key from ChangeTip. Request one from the {@link htps://www.changetip.com/api|ChangeTip API} website.'
     * @public
     * @property {string} api_key_or_access_token
     */
    set api_key_or_access_token(value) {
        this._api_key_or_access_token = value;
    },

    get api_key_or_access_token() {
        return this._api_key_or_access_token;
    },

    /**
     * Authentication Type to configure ChangeTip requests.
     * @public
     * @property {string} authentication_type access_token|api_key
     */
    set authentication_type(value) {
        this._authentication_type = value;

    },

    get authentication_type () {
        return this._authentication_type || CHANGETIP_DEFAULT_AUTHENTICATION_TYPE;
    },

    /**
     * Host used for all ChangeTip remote requests (api.changetip.com)
     * @public
     * @property {string} host
     */
    set host(value) {
        this._host = value;
    },

    get host() {
        return this._host || CHANGETIP_DEFAULT_HOST;
    },

    /**
     * API Version to use for all ChangeTip remote requests (v1)
     * @public
     * @property {number} api_version
     */
    set api_version(value) {
        this._api_version = value;
    },

    get api_version() {
        return this._api_version || CHANGETIP_DEFAULT_VERSION;
    },

    set dev_mode(value) {
        this._dev_mode = !!value;
    },

    get dev_mode() {
        return this._dev_mode || false
    },

    /**
     * Initializes the class for remote API Calls
     * @param {ChangeTipConfig} config
     * @returns {ChangeTip}
     */
    init: function (config) {
        config                   = config || {};
        this.api_key_or_access_token    = config.api_key_or_access_token || undefined;
        this.authentication_type = config.authentication_type || undefined;
        this.host                = config.host || undefined;
        this.api_version         = config.api_version || undefined;
        this.dev_mode            = config.dev_mode || false;
        return this;
    },

    /**
     * Sends a tip via the ChangeTip API
     * @param context_uid {number|string} Unique ID for this tip
     * @param {number|string} sender username/identifier for the tip sender
     * @param {number|string} receiver username/identifier for the tip receiever
     * @param {string} channel Origin channel for this tip (twitter, github, slack, etc)
     * @param {string} message Tip message. Includes amount or moniker.
     * @param [meta] {Object} optional object to be sent back in result set
     * @returns {promise.promise|jQuery.promise|promise|Q.promise|jQuery.ready.promise|l.promise}
     */
    send_tip: function (context_uid, sender, receiver, channel, message, meta) {
        if (!this.api_key_or_access_token) throw new ChangeTipException(300);
        if (!channel) throw new ChangeTipException(301);

        var deferred = Q.defer(),
            data;

        data = {
            context_uid : context_uid,
            sender      : sender,
            receiver    : receiver,
            channel     : channel,
            message     : message,
            meta        : meta
        };

        this._send_request(data, 'tips', null, Methods.POST, deferred);
        return deferred.promise;
    },

    /**
     * List of Monikers.
     * @param {number} pageNumber
     * @returns {promise.promise|jQuery.promise|promise|Q.promise|jQuery.ready.promise|l.promise}
     */
    monikers: function (pageNumber) {
        if (!this.api_key_or_access_token) throw new ChangeTipException(300);
        if (this.api_version === CHANGETIP_DEFAULT_VERSION) throw new ChangeTipException(400);

        var deferred = Q.defer(),
            params;

        params = {
            page: pageNumber || 1
        };

        this._send_request({}, 'monikers', params, Methods.GET, deferred);
        return deferred.promise;
    },

    /**
     * List of Currencies.
     * @param {number} pageNumber
     * @returns {promise.promise|jQuery.promise|promise|Q.promise|jQuery.ready.promise|l.promise}
     */
    currencies: function (pageNumber) {
        if (!this.api_key_or_access_token) throw new ChangeTipException(300);
        if (this.api_version === CHANGETIP_DEFAULT_VERSION) throw new ChangeTipException(400);

        var deferred = Q.defer(),
            params;

        params = {
            page: pageNumber || 1
        };

        this._send_request({}, 'currencies', params, Methods.GET, deferred);
        return deferred.promise;
    },

    /**
     * Get User.
     * @param {number} userId
     * @param {boolean} full
     * @returns {promise.promise|jQuery.promise|promise|Q.promise|jQuery.ready.promise|l.promise}
     */
    user: function (userId, full) {
        if (!this.api_key_or_access_token) throw new ChangeTipException(300);
        if (this.api_version === CHANGETIP_DEFAULT_VERSION) throw new ChangeTipException(400);
        if (!userId) throw new ChangeTipException(401)

        var deferred = Q.defer(),
            params;

        params = {
            full: full || false
        };

        this._send_request({}, 'users/' + userId, params, Methods.GET, deferred);
        return deferred.promise;
    },

    /**
     * List of Users.
     * @param {number} pageNumber
     * @returns {promise.promise|jQuery.promise|promise|Q.promise|jQuery.ready.promise|l.promise}
     */
    users: function (pageNumber) {
        if (!this.api_key_or_access_token) throw new ChangeTipException(300);
        if (this.api_version === CHANGETIP_DEFAULT_VERSION) throw new ChangeTipException(400);

        var deferred = Q.defer(),
            params;

        params = {
            page: pageNumber || 1
        };

        this._send_request({}, 'users', params, Methods.GET, deferred);
        return deferred.promise;
    },

    /**
     * List of Transactions.
     * @param {number} pageNumber
     * @returns {promise.promise|jQuery.promise|promise|Q.promise|jQuery.ready.promise|l.promise}
     */
    transactions: function (pageNumber) {
        if (!this.api_key_or_access_token) throw new ChangeTipException(300);
        if (this.api_version === CHANGETIP_DEFAULT_VERSION) throw new ChangeTipException(400);

        var deferred = Q.defer(),
            params;

        params = {
            page: pageNumber || 1
        };

        this._send_request({}, 'transactions', params, Methods.GET, deferred);
        return deferred.promise;
    },

    /**
     * Create Tip URL.
     * @param {number} text_amount
     * @param {string} moniker
     * @param {string} message
     * @returns {promise.promise|jQuery.promise|promise|Q.promise|jQuery.ready.promise|l.promise}
     */
    tip_url: function (text_amount, moniker, message) {
        if (!this.api_key_or_access_token) throw new ChangeTipException(300);
        if (!text_amount) throw new ChangeTipException(402);
        if (!message) throw new ChangeTipException(402);
        if (this.api_version === CHANGETIP_DEFAULT_VERSION) throw new ChangeTipException(400);

        var deferred = Q.defer(),
            data;

        data = {
            text_amount : text_amount,
            message     : message
        };

        this._send_request(data, 'tip-url', null, Methods.POST, deferred);
        return deferred.promise;
    },

    /**
     * Get Wallet Withdrawals
     * @returns {promise.promise|jQuery.promise|promise|Q.promise|jQuery.ready.promise|l.promise}
     */
    get_wallet_withdrawals: function() {
        if (!this.api_key_or_access_token) throw new ChangeTipException(300);
        if (this.api_version === CHANGETIP_DEFAULT_VERSION) throw new ChangeTipException(400);

        var deferred = Q.defer();

        this._send_request({}, 'wallet/withdrawals', null, Methods.GET, deferred);
        return deferred.promise;
    },

    /**
     * Post Wallet Withdrawals
     * @param {number} amount
     * @param {string} address
     * @returns {promise.promise|jQuery.promise|promise|Q.promise|jQuery.ready.promise|l.promise}
     */
    post_wallet_withdrawals: function(amount, address) {
        if (!this.api_key_or_access_token) throw new ChangeTipException(300);
        if (this.api_version === CHANGETIP_DEFAULT_VERSION) throw new ChangeTipException(400);
        if (!amount) throw new ChangeTipException(403);
        if (!address) throw new ChangeTipException(403);

        var deferred = Q.defer(),
            data;

        data = {
            amount  : amount,
            address : address
        };

        this._send_request(data, 'wallet/withdrawals', null, Methods.POST, deferred);
        return deferred.promise;
    },

    /**
     * Retrieve wallet balance.
     * @returns {promise.promise|jQuery.promise|promise|Q.promise|jQuery.ready.promise|l.promise}
     */
    get_wallet_balance: function () {
        if (!this.api_key_or_access_token) throw new ChangeTipException(300);
        if (this.api_version === CHANGETIP_DEFAULT_VERSION) throw new ChangeTipException(400);

        var deferred = Q.defer();

        this._send_request({}, 'wallet/balance', null, Methods.GET, deferred);
        return deferred.promise;
    },

    /**
     * Retrieve wallet address of user.
     * @param {string} username
     * @returns {promise.promise|jQuery.promise|promise|Q.promise|jQuery.ready.promise|l.promise}
     */
    get_wallet_address: function (username) {
        if (!this.api_key_or_access_token) throw new ChangeTipException(300);
        if (this.api_version === CHANGETIP_DEFAULT_VERSION) throw new ChangeTipException(400);

        var deferred = Q.defer(),
            params;

        params = {
            username: username
        };

        this._send_request({}, 'wallet/address', params, Methods.GET, deferred);
        return deferred.promise;
    },

    /**
     * Retrieve tips from the ChangeTip API
     * @param {string|string[]} tips Single or collection of tip identifiers to retrieve
     * @param {string} [channel] Channel to filter results. (github, twitter, slack, etc)
     * @returns {promise.promise|jQuery.promise|promise|Q.promise|jQuery.ready.promise|l.promise}
     */
    get_tip: function (tips, channel) {
        if (!this.api_key_or_access_token) throw new ChangeTipException(300);

        var deferred = Q.defer(),
            params;

        params = {
            tips    : tips instanceof Array ? tips.join(",") : tips,
            channel : channel || ''
        };

        this._send_request({}, 'tips', params, Methods.GET, deferred);
        return deferred.promise;
    },

    /**
     * Sends a request
     * @param {Object} data JSON Object with data payload
     * @param {String} path API Path
     * @param {Object} params Query Parameters to be sent along with this request
     * @param {Methods} method HTTP Method
     * @param deferred Deferred Object
     * @param deferred.promise Deferred Promise Object
     * @param deferred.resolve Deferred success function
     * @param deferred.reject Deferred rejection function
     * @private
     */
    _send_request: function (data, path, params, method, deferred) {
        var options, query_params, req,
            dataString = JSON.stringify(data);

        query_params = querystring.stringify(params);

        options = {
            host    : this.host,
            port    : 443,
            path    : '/v' + this.api_version + '/' + path + '/?' + this.authentication_type + '=' + this.api_key_or_access_token + (query_params ? ('&' + query_params) : ''),
            method  : method,
            headers : {
                'Content-Type'   : 'application/json',
                'Content-Length' : dataString.length
            }
        };

        if (!this.dev_mode) {
            req = https.request(options, function (res) {
                res.setEncoding('utf-8');

                var response = '', result;

                res.on('data', function (response_data) {
                    response += response_data;
                });

                res.on('end', function () {
                    result = JSON.parse(response);
                    deferred.resolve(result);
                });
            });

            req.write(dataString);
            req.end();
        } else {
            deferred.resolve({
                status : "dev_mode",
                data   : data,
                params : params,
                path   : options.path
            });
        }
    }
};

/**
 * Accessor for ChangeTip Singleton
 * @returns {ChangeTip}
 */
ChangeTip.get_instance = function () {
    if (!instance) {
        instance = new ChangeTip();
    }
    return instance;
};

/** ChangeTip Class */
module.exports = ChangeTip;
<commit_msg>Safeguard Q require.<commit_after>"use strict";

try
    Q = require('q')
catch e
    Q = require('Q')

var https = require('https'),
    querystring = require('querystring'),
    Methods = {
        GET: "GET",
        POST: "POST"
    },
    ChangeTipException                    = require('./changetip-exception'),
    CHANGETIP_DEFAULT_VERSION             = "2",
    CHANGETIP_DEFAULT_HOST                = "api.changetip.com",
    CHANGETIP_DEFAULT_AUTHENTICATION_TYPE = "access_token",
    instance;

/**
 * @typedef ChangeTipConfig
 * @type {object}
 * @property {string} api_key_or_access_token API Key for ChangeTip
 * @property {string} host Host URL for all remote API requests (defaults to CHANGETIP_DEFAULT_HOST)
 * @property {number} api_version Base URL for all remote API requests (defaults to CHANGETIP_DEFAULT_VERSION)
 * @property {string} authentication_type Choose to pass either an API Key or an Access Token (defaults to CHANGETIP_DEFAULT_AUTHENTICATION_TYPE)
 */

/**
 * ChangeTip API
 * @param {ChangeTipConfig} [config]
 * @constructor
 */
var ChangeTip = function (config) {
    // If information was passed to constructor auto init
    if (config) {
        this.init(config);
    }
};

ChangeTip.prototype = {
    /**
     * API Key from ChangeTip. Request one from the {@link htps://www.changetip.com/api|ChangeTip API} website.'
     * @public
     * @property {string} api_key_or_access_token
     */
    set api_key_or_access_token(value) {
        this._api_key_or_access_token = value;
    },

    get api_key_or_access_token() {
        return this._api_key_or_access_token;
    },

    /**
     * Authentication Type to configure ChangeTip requests.
     * @public
     * @property {string} authentication_type access_token|api_key
     */
    set authentication_type(value) {
        this._authentication_type = value;

    },

    get authentication_type () {
        return this._authentication_type || CHANGETIP_DEFAULT_AUTHENTICATION_TYPE;
    },

    /**
     * Host used for all ChangeTip remote requests (api.changetip.com)
     * @public
     * @property {string} host
     */
    set host(value) {
        this._host = value;
    },

    get host() {
        return this._host || CHANGETIP_DEFAULT_HOST;
    },

    /**
     * API Version to use for all ChangeTip remote requests (v1)
     * @public
     * @property {number} api_version
     */
    set api_version(value) {
        this._api_version = value;
    },

    get api_version() {
        return this._api_version || CHANGETIP_DEFAULT_VERSION;
    },

    set dev_mode(value) {
        this._dev_mode = !!value;
    },

    get dev_mode() {
        return this._dev_mode || false
    },

    /**
     * Initializes the class for remote API Calls
     * @param {ChangeTipConfig} config
     * @returns {ChangeTip}
     */
    init: function (config) {
        config                   = config || {};
        this.api_key_or_access_token    = config.api_key_or_access_token || undefined;
        this.authentication_type = config.authentication_type || undefined;
        this.host                = config.host || undefined;
        this.api_version         = config.api_version || undefined;
        this.dev_mode            = config.dev_mode || false;
        return this;
    },

    /**
     * Sends a tip via the ChangeTip API
     * @param context_uid {number|string} Unique ID for this tip
     * @param {number|string} sender username/identifier for the tip sender
     * @param {number|string} receiver username/identifier for the tip receiever
     * @param {string} channel Origin channel for this tip (twitter, github, slack, etc)
     * @param {string} message Tip message. Includes amount or moniker.
     * @param [meta] {Object} optional object to be sent back in result set
     * @returns {promise.promise|jQuery.promise|promise|Q.promise|jQuery.ready.promise|l.promise}
     */
    send_tip: function (context_uid, sender, receiver, channel, message, meta) {
        if (!this.api_key_or_access_token) throw new ChangeTipException(300);
        if (!channel) throw new ChangeTipException(301);

        var deferred = Q.defer(),
            data;

        data = {
            context_uid : context_uid,
            sender      : sender,
            receiver    : receiver,
            channel     : channel,
            message     : message,
            meta        : meta
        };

        this._send_request(data, 'tips', null, Methods.POST, deferred);
        return deferred.promise;
    },

    /**
     * List of Monikers.
     * @param {number} pageNumber
     * @returns {promise.promise|jQuery.promise|promise|Q.promise|jQuery.ready.promise|l.promise}
     */
    monikers: function (pageNumber) {
        if (!this.api_key_or_access_token) throw new ChangeTipException(300);
        if (this.api_version === CHANGETIP_DEFAULT_VERSION) throw new ChangeTipException(400);

        var deferred = Q.defer(),
            params;

        params = {
            page: pageNumber || 1
        };

        this._send_request({}, 'monikers', params, Methods.GET, deferred);
        return deferred.promise;
    },

    /**
     * List of Currencies.
     * @param {number} pageNumber
     * @returns {promise.promise|jQuery.promise|promise|Q.promise|jQuery.ready.promise|l.promise}
     */
    currencies: function (pageNumber) {
        if (!this.api_key_or_access_token) throw new ChangeTipException(300);
        if (this.api_version === CHANGETIP_DEFAULT_VERSION) throw new ChangeTipException(400);

        var deferred = Q.defer(),
            params;

        params = {
            page: pageNumber || 1
        };

        this._send_request({}, 'currencies', params, Methods.GET, deferred);
        return deferred.promise;
    },

    /**
     * Get User.
     * @param {number} userId
     * @param {boolean} full
     * @returns {promise.promise|jQuery.promise|promise|Q.promise|jQuery.ready.promise|l.promise}
     */
    user: function (userId, full) {
        if (!this.api_key_or_access_token) throw new ChangeTipException(300);
        if (this.api_version === CHANGETIP_DEFAULT_VERSION) throw new ChangeTipException(400);
        if (!userId) throw new ChangeTipException(401)

        var deferred = Q.defer(),
            params;

        params = {
            full: full || false
        };

        this._send_request({}, 'users/' + userId, params, Methods.GET, deferred);
        return deferred.promise;
    },

    /**
     * List of Users.
     * @param {number} pageNumber
     * @returns {promise.promise|jQuery.promise|promise|Q.promise|jQuery.ready.promise|l.promise}
     */
    users: function (pageNumber) {
        if (!this.api_key_or_access_token) throw new ChangeTipException(300);
        if (this.api_version === CHANGETIP_DEFAULT_VERSION) throw new ChangeTipException(400);

        var deferred = Q.defer(),
            params;

        params = {
            page: pageNumber || 1
        };

        this._send_request({}, 'users', params, Methods.GET, deferred);
        return deferred.promise;
    },

    /**
     * List of Transactions.
     * @param {number} pageNumber
     * @returns {promise.promise|jQuery.promise|promise|Q.promise|jQuery.ready.promise|l.promise}
     */
    transactions: function (pageNumber) {
        if (!this.api_key_or_access_token) throw new ChangeTipException(300);
        if (this.api_version === CHANGETIP_DEFAULT_VERSION) throw new ChangeTipException(400);

        var deferred = Q.defer(),
            params;

        params = {
            page: pageNumber || 1
        };

        this._send_request({}, 'transactions', params, Methods.GET, deferred);
        return deferred.promise;
    },

    /**
     * Create Tip URL.
     * @param {number} text_amount
     * @param {string} moniker
     * @param {string} message
     * @returns {promise.promise|jQuery.promise|promise|Q.promise|jQuery.ready.promise|l.promise}
     */
    tip_url: function (text_amount, moniker, message) {
        if (!this.api_key_or_access_token) throw new ChangeTipException(300);
        if (!text_amount) throw new ChangeTipException(402);
        if (!message) throw new ChangeTipException(402);
        if (this.api_version === CHANGETIP_DEFAULT_VERSION) throw new ChangeTipException(400);

        var deferred = Q.defer(),
            data;

        data = {
            text_amount : text_amount,
            message     : message
        };

        this._send_request(data, 'tip-url', null, Methods.POST, deferred);
        return deferred.promise;
    },

    /**
     * Get Wallet Withdrawals
     * @returns {promise.promise|jQuery.promise|promise|Q.promise|jQuery.ready.promise|l.promise}
     */
    get_wallet_withdrawals: function() {
        if (!this.api_key_or_access_token) throw new ChangeTipException(300);
        if (this.api_version === CHANGETIP_DEFAULT_VERSION) throw new ChangeTipException(400);

        var deferred = Q.defer();

        this._send_request({}, 'wallet/withdrawals', null, Methods.GET, deferred);
        return deferred.promise;
    },

    /**
     * Post Wallet Withdrawals
     * @param {number} amount
     * @param {string} address
     * @returns {promise.promise|jQuery.promise|promise|Q.promise|jQuery.ready.promise|l.promise}
     */
    post_wallet_withdrawals: function(amount, address) {
        if (!this.api_key_or_access_token) throw new ChangeTipException(300);
        if (this.api_version === CHANGETIP_DEFAULT_VERSION) throw new ChangeTipException(400);
        if (!amount) throw new ChangeTipException(403);
        if (!address) throw new ChangeTipException(403);

        var deferred = Q.defer(),
            data;

        data = {
            amount  : amount,
            address : address
        };

        this._send_request(data, 'wallet/withdrawals', null, Methods.POST, deferred);
        return deferred.promise;
    },

    /**
     * Retrieve wallet balance.
     * @returns {promise.promise|jQuery.promise|promise|Q.promise|jQuery.ready.promise|l.promise}
     */
    get_wallet_balance: function () {
        if (!this.api_key_or_access_token) throw new ChangeTipException(300);
        if (this.api_version === CHANGETIP_DEFAULT_VERSION) throw new ChangeTipException(400);

        var deferred = Q.defer();

        this._send_request({}, 'wallet/balance', null, Methods.GET, deferred);
        return deferred.promise;
    },

    /**
     * Retrieve wallet address of user.
     * @param {string} username
     * @returns {promise.promise|jQuery.promise|promise|Q.promise|jQuery.ready.promise|l.promise}
     */
    get_wallet_address: function (username) {
        if (!this.api_key_or_access_token) throw new ChangeTipException(300);
        if (this.api_version === CHANGETIP_DEFAULT_VERSION) throw new ChangeTipException(400);

        var deferred = Q.defer(),
            params;

        params = {
            username: username
        };

        this._send_request({}, 'wallet/address', params, Methods.GET, deferred);
        return deferred.promise;
    },

    /**
     * Retrieve tips from the ChangeTip API
     * @param {string|string[]} tips Single or collection of tip identifiers to retrieve
     * @param {string} [channel] Channel to filter results. (github, twitter, slack, etc)
     * @returns {promise.promise|jQuery.promise|promise|Q.promise|jQuery.ready.promise|l.promise}
     */
    get_tip: function (tips, channel) {
        if (!this.api_key_or_access_token) throw new ChangeTipException(300);

        var deferred = Q.defer(),
            params;

        params = {
            tips    : tips instanceof Array ? tips.join(",") : tips,
            channel : channel || ''
        };

        this._send_request({}, 'tips', params, Methods.GET, deferred);
        return deferred.promise;
    },

    /**
     * Sends a request
     * @param {Object} data JSON Object with data payload
     * @param {String} path API Path
     * @param {Object} params Query Parameters to be sent along with this request
     * @param {Methods} method HTTP Method
     * @param deferred Deferred Object
     * @param deferred.promise Deferred Promise Object
     * @param deferred.resolve Deferred success function
     * @param deferred.reject Deferred rejection function
     * @private
     */
    _send_request: function (data, path, params, method, deferred) {
        var options, query_params, req,
            dataString = JSON.stringify(data);

        query_params = querystring.stringify(params);

        options = {
            host    : this.host,
            port    : 443,
            path    : '/v' + this.api_version + '/' + path + '/?' + this.authentication_type + '=' + this.api_key_or_access_token + (query_params ? ('&' + query_params) : ''),
            method  : method,
            headers : {
                'Content-Type'   : 'application/json',
                'Content-Length' : dataString.length
            }
        };

        if (!this.dev_mode) {
            req = https.request(options, function (res) {
                res.setEncoding('utf-8');

                var response = '', result;

                res.on('data', function (response_data) {
                    response += response_data;
                });

                res.on('end', function () {
                    result = JSON.parse(response);
                    deferred.resolve(result);
                });
            });

            req.write(dataString);
            req.end();
        } else {
            deferred.resolve({
                status : "dev_mode",
                data   : data,
                params : params,
                path   : options.path
            });
        }
    }
};

/**
 * Accessor for ChangeTip Singleton
 * @returns {ChangeTip}
 */
ChangeTip.get_instance = function () {
    if (!instance) {
        instance = new ChangeTip();
    }
    return instance;
};

/** ChangeTip Class */
module.exports = ChangeTip;
<|endoftext|>


<commit_before>'use strict';

import chemspider from './chemspider';
import chemexper from './chemexper';
import _ from 'lodash';

import ui from 'src/util/ui';

const defaultOptions = {
    chemexper: true,
    chemspider: true
};

module.exports = {
    chemspider: chemspider,
    chemexper: chemexper,
    choose(term, options) {
        options = Object.assign({}, defaultOptions, options);
        const sources = [];
        if(options.chemspider) sources.push({promise: chemspider.search(term)});
        if(options.chemexper) sources.push({promise: chemexper.search(term)});
        if(options.roc) {
            const roc = options.roc;
            const rocPromise =  roc.view('entryByKindAndId', {
                startkey: ['sample', [term]],
                endkey: ['sample', [term + '\ufff0', {}]]
            }).then(data => {
                data.forEach(d => {

                    const name = (d.$content.general.name || [];
                    d.id = d._id;
                    d.source = 'sample';
                    d.names = _.uniq([
                        d.$id.join(' '),
                        d.$content.general.description,
                        ...name.filter(n => n.language === undefined || n.language.match(/^(en)|(EN)$/)).map(n => n.value)
                    ]);
                });
                return data;
            });
            sources.push({promise: rocPromise});
        }
        return ui.choose(sources, {
            autoSelect: options.autoSelect,
            asynchronous: true,
            noConfirmation: true,
            returnRow: true,
            dialog: {
                width: 1000,
                height: 800
            },
            columns: [
                {
                    id: 'names',
                    name: 'names',
                    jpath: [],
                    rendererOptions: {
                        forceType: 'object',
                        twig: `

                        {% for n in names %}

{{ n }}

                        {% endfor %}

                    `
                    }
                },
                {
                    id: 'cas',
                    name: 'cas',
                    jpath: ['$content', 'identifier'],
                    rendererOptions: {
                        forceType: 'object',
                        twig: listTemplate('cas', '.value')
                    },
                    maxWidth: 100
                },
                {
                    id: 'molfile',
                    name: 'molfile',
                    jpath: ['$content', 'general', 'molfile'],
                    rendererOptions: {
                        forceType: 'mol2d'
                    },
                    maxWidth: 250
                },
                {
                    id: 'source',
                    name: 'source',
                    field: 'source',
                    maxWidth: 70
                }
            ],
            idField: 'id',
            slick: {
                rowHeight: 150
            }
        }).catch(function(e) {
            console.error(e);
            ui.showNotification('search failed', 'error');
        });

    }
};

function listTemplate(val, prop) {
    return `

            {% for n in ${val} %}

{{ n${prop} }}

            {% endfor %}

    `;
}<commit_msg>Fix parenthesis<commit_after>'use strict';

import chemspider from './chemspider';
import chemexper from './chemexper';
import _ from 'lodash';

import ui from 'src/util/ui';

const defaultOptions = {
    chemexper: true,
    chemspider: true
};

module.exports = {
    chemspider: chemspider,
    chemexper: chemexper,
    choose(term, options) {
        options = Object.assign({}, defaultOptions, options);
        const sources = [];
        if(options.chemspider) sources.push({promise: chemspider.search(term)});
        if(options.chemexper) sources.push({promise: chemexper.search(term)});
        if(options.roc) {
            const roc = options.roc;
            const rocPromise =  roc.view('entryByKindAndId', {
                startkey: ['sample', [term]],
                endkey: ['sample', [term + '\ufff0', {}]]
            }).then(data => {
                data.forEach(d => {

                    const name = d.$content.general.name || [];
                    d.id = d._id;
                    d.source = 'sample';
                    d.names = _.uniq([
                        d.$id.join(' '),
                        d.$content.general.description,
                        ...name.filter(n => n.language === undefined || n.language.match(/^(en)|(EN)$/)).map(n => n.value)
                    ]);
                });
                return data;
            });
            sources.push({promise: rocPromise});
        }
        return ui.choose(sources, {
            autoSelect: options.autoSelect,
            asynchronous: true,
            noConfirmation: true,
            returnRow: true,
            dialog: {
                width: 1000,
                height: 800
            },
            columns: [
                {
                    id: 'names',
                    name: 'names',
                    jpath: [],
                    rendererOptions: {
                        forceType: 'object',
                        twig: `

                        {% for n in names %}

{{ n }}

                        {% endfor %}

                    `
                    }
                },
                {
                    id: 'cas',
                    name: 'cas',
                    jpath: ['$content', 'identifier'],
                    rendererOptions: {
                        forceType: 'object',
                        twig: listTemplate('cas', '.value')
                    },
                    maxWidth: 100
                },
                {
                    id: 'molfile',
                    name: 'molfile',
                    jpath: ['$content', 'general', 'molfile'],
                    rendererOptions: {
                        forceType: 'mol2d'
                    },
                    maxWidth: 250
                },
                {
                    id: 'source',
                    name: 'source',
                    field: 'source',
                    maxWidth: 70
                }
            ],
            idField: 'id',
            slick: {
                rowHeight: 150
            }
        }).catch(function(e) {
            console.error(e);
            ui.showNotification('search failed', 'error');
        });

    }
};

function listTemplate(val, prop) {
    return `

            {% for n in ${val} %}

{{ n${prop} }}

            {% endfor %}

    `;
}<|endoftext|>


<commit_before>import '@material/mwc-dialog';
import '@material/mwc-button';
import '@material/mwc-textfield';

import '../shared/demo-header';

addEventListener('load', function() {
 document.body.classList.remove('unresolved');
});

const buttons = document.body.querySelectorAll('mwc-button[data-num]');

for (let i = 0; i < buttons.length; i++) {
  const button = buttons[i];
  const buttonNum = button.dataset.num;

  const listenerFactory = (numButton) => {
    return function() {
      const dialog = document.body.querySelector('#dialog' + numButton);
      dialog.open = true;
    };
  };

  const listener = listenerFactory(buttonNum);

  button.addEventListener('click', listener);
}

window.toggleActions.onclick = function() {
  const dialog = document.body.querySelector('#dialog4');
  const hideActionSpan = document.body.querySelector('#hideActionVal');

  const hideAction = !dialog.hideActions;
  dialog.hideActions = hideAction;
  hideActionSpan.innerText = hideAction;
};

const dialog = document.querySelector('#dialog8');
const textField = document.querySelector('#dialog8-text-field');
const primaryButton = document.querySelector('#dialog8-primary-action-button');

primaryButton.addEventListener('click', (e) => {
  // validate, possible asynchronous such as a server response
  const isValid = textField.checkValidity();
  if (isValid) {
    dialog.close();
    return;
  }

  textField.reportValidity();
});
<commit_msg>Remove uneccessary event reference from anonymous callback<commit_after>import '@material/mwc-dialog';
import '@material/mwc-button';
import '@material/mwc-textfield';

import '../shared/demo-header';

addEventListener('load', function() {
 document.body.classList.remove('unresolved');
});

const buttons = document.body.querySelectorAll('mwc-button[data-num]');

for (let i = 0; i < buttons.length; i++) {
  const button = buttons[i];
  const buttonNum = button.dataset.num;

  const listenerFactory = (numButton) => {
    return function() {
      const dialog = document.body.querySelector('#dialog' + numButton);
      dialog.open = true;
    };
  };

  const listener = listenerFactory(buttonNum);

  button.addEventListener('click', listener);
}

window.toggleActions.onclick = function() {
  const dialog = document.body.querySelector('#dialog4');
  const hideActionSpan = document.body.querySelector('#hideActionVal');

  const hideAction = !dialog.hideActions;
  dialog.hideActions = hideAction;
  hideActionSpan.innerText = hideAction;
};

const dialog = document.querySelector('#dialog8');
const textField = document.querySelector('#dialog8-text-field');
const primaryButton = document.querySelector('#dialog8-primary-action-button');

primaryButton.addEventListener('click', () => {
  // validate, possible asynchronous such as a server response
  const isValid = textField.checkValidity();
  if (isValid) {
    dialog.close();
    return;
  }

  textField.reportValidity();
});
<|endoftext|>