<commit_before>//
// Copyright (C) 2013 - present Instructure, Inc.
//
// This file is part of Canvas.
//
// Canvas is free software: you can redistribute it and/or modify it under
// the terms of the GNU Affero General Public License as published by the Free
// Software Foundation, version 3 of the License.
//
// Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
// A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
// details.
//
// You should have received a copy of the GNU Affero General Public License along
// with this program. If not, see <http://www.gnu.org/licenses/>.
//

import $ from 'jquery'
import {changeMonth} from '../../jquery/calendar_move' // calendarMonths
import RichContentEditor from '@canvas/rce/RichContentEditor'
import KeyboardShortcuts from '@canvas/tinymce-keyboard-shortcuts'
import '@canvas/datetime' // dateString, datepicker
import '@canvas/forms/jquery/jquery.instructure_forms' // formSubmit, formErrors
import '@canvas/jquery/jquery.instructure_misc_plugins' // ifExists, showIf
import '@canvas/loading-image'
import 'jquery-scroll-to-visible/jquery.scrollTo'
import 'jqueryui/datepicker'
import easy_student_view from '@canvas/easy-student-view'
import mathml from 'mathml'

RichContentEditor.preloadRemoteModule()

// Highlight mini calendar days matching syllabus events
//    Queries the syllabus event list and highlights the
//    corresponding mini calendar dates.
function highlightDaysWithEvents() {
  const $mini_month = $('.mini_month')
  const $syllabus = $('#syllabus')
  if (!$mini_month || !$syllabus) return

  let events = $mini_month.find('.day.has_event')
  events.removeClass('has_event')
  let wrapper = events.find('.day_wrapper')
  wrapper.removeAttr('role')
  wrapper.removeAttr('tabindex')

  $syllabus.find('tr.date:visible').each(function() {
    const date = $(this)
      .find('.day_date')
      .attr('data-date')
    events = $mini_month.find(`#mini_day_${date}`)
    events.addClass('has_event')
    wrapper = events.find('.day_wrapper')
    wrapper.attr('role', 'link')
    wrapper.attr('tabindex', '0')
  })
}

// Sets highlighting on a given date
//    Removes all highlighting then highlights the given
//    date, if provided.
function highlightDate(date) {
  const $mini_month = $('.mini_month')
  const $syllabus = $('#syllabus')

  $('tr.date.related, .day.related').removeClass('related')
  if (date) {
    if ($mini_month) $mini_month.find(`#mini_day_${date}`).addClass('related')
    if ($syllabus) $syllabus.find(`tr.date.events_${date}`).addClass('related')
  }
}

function highlightRelated(related_id, self) {
  const $syllabus = $('#syllabus')

  $syllabus.find('tr.related_event').removeClass('related_event')
  if (related_id && $syllabus) {
    $syllabus
      .find(`tr.related-${related_id}`)
      .not(self)
      .addClass('related_event')
  }
}

// Binds to #syllabus dom events
//    Called to bind behaviors to #syllabus after it's rendered.
function bindToSyllabus() {
  const $syllabus = $('#syllabus')
  $syllabus.on('mouseenter mouseleave', 'tr.date', function(ev) {
    let date
    if (ev.type === 'mouseenter')
      date = $(this)
        .find('.day_date')
        .attr('data-date')
    highlightDate(date)
  })

  $syllabus.on('mouseenter mouseleave', 'tr.date.detail_list', function(ev) {
    let related_id = null
    if (ev.type === 'mouseenter') {
      const classNames = ($(this).attr('class') || '').split(/\s+/)
      classNames.some(c => {
        if (c.substr(0, 8) === 'related-') {
          return (related_id = c.substr(8))
        }
      })
    }

    highlightRelated(related_id, this)
  })

  highlightDaysWithEvents()

  const todayString = $.datepicker.formatDate('yy_mm_dd', new Date())
  highlightDate(todayString)
}

function selectRow($row) {
  if ($row.length > 0) {
    $('tr.selected').removeClass('selected')
    $row.addClass('selected')
    $('html, body').scrollTo($row)
    $row
      .find('a')
      .first()
      .focus()
  }
}

function selectDate(date) {
  $('.mini_month .day.selected').removeClass('selected')
  $('.mini_month')
    .find(`#mini_day_${date}`)
    .addClass('selected')
}

// Binds to mini calendar dom events
function bindToMiniCalendar() {
  const $mini_month = $('.mini_month')

  const prev_next_links = $mini_month.find('.next_month_link, .prev_month_link')
  prev_next_links.on('click', function(ev) {
    ev.preventDefault()
    changeMonth($mini_month, $(this).hasClass('next_month_link') ? 1 : -1)
    highlightDaysWithEvents()
  })

  const miniCalendarDayClick = function(ev) {
    ev.preventDefault()
    const date = $(ev.target)
      .closest('.mini_calendar_day')[0]
      .id.slice(9)
    const [year, month, day] = Array.from(date.split('_'))
    changeMonth($mini_month, `${month}/${day}/${year}`)
    highlightDaysWithEvents()
    selectDate(date)
    $(`.events_${date}`).ifExists($events => setTimeout(() => selectRow($events), 0)) // focus race condition hack. why do you do this to me, IE?
  }

  $mini_month.on('keypress', '.day_wrapper', ev => {
    if (ev.which === 13 || ev.which === 32) miniCalendarDayClick(ev)
  })

  $mini_month.on('click', '.day_wrapper', miniCalendarDayClick)

  $mini_month.on('focus blur mouseover mouseout', '.day_wrapper', ev => {
    let date
    if (ev.type !== 'mouseout' && ev.type !== 'blur') {
      date = $(ev.target)
        .closest('.mini_calendar_day')[0]
        .id.slice(9)
    }
    highlightDate(date)
  })

  $('.jump_to_today_link').on('click', ev => {
    ev.preventDefault()
    const todayString = $.datepicker.formatDate('yy_mm_dd', new Date())
    let $lastBefore
    $('tr.date').each(function() {
      const dateString = $(this)
        .find('.day_date')
        .attr('data-date')

      if (dateString) {
        if (dateString > todayString) return false
        $lastBefore = dateString
      }
    })

    changeMonth($mini_month, $.datepicker.formatDate('mm/dd/yy', new Date()))
    highlightDaysWithEvents()

    if (!$lastBefore) $lastBefore = $('tr.date:first')

    selectDate(todayString)
    selectRow($(`tr.date.events_${$lastBefore}`))
  })
}

// Binds to edit syllabus dom events
const bindToEditSyllabus = function(course_summary_enabled) {
  const $course_syllabus = $('#course_syllabus')
  $course_syllabus.data('syllabus_body', ENV.SYLLABUS_BODY)
  const $edit_syllabus_link = $('.edit_syllabus_link')

  // if there's no edit link, don't need to (and shouldn't) do the rest of
  // this. the edit link is included on the page if and only if the user has
  // :manage_content permission on the course (see assignments'
  // syllabus_right_side view)
  if (!$edit_syllabus_link.length) return

  // Add the backbone view for keyboardshortup help here
  if (!ENV.use_rce_enhancements) {
    $('.toggle_views_link')
      .first()
      .before(new KeyboardShortcuts().render().$el)
  }

  function resetToggleLinks() {
    $('.toggle_html_editor_link').show()
    $('.toggle_rich_editor_link').hide()
  }

  const $edit_course_syllabus_form = $('#edit_course_syllabus_form')
  let $course_syllabus_body = $('#course_syllabus_body')
  const $course_syllabus_details = $('#course_syllabus_details')

  RichContentEditor.initSidebar({
    show() {
      $('#sidebar_content, #course_show_secondary').hide()
    },
    hide() {
      $('#sidebar_content, #course_show_secondary').show()
    }
  })

  $edit_course_syllabus_form.on('edit', () => {
    $edit_course_syllabus_form.show()
    $edit_syllabus_link.hide()
    $course_syllabus.hide()
    $course_syllabus_details.hide()
    easy_student_view.hide()
    $course_syllabus_body = RichContentEditor.freshNode($course_syllabus_body)
    $course_syllabus_body.val($course_syllabus.data('syllabus_body'))
    RichContentEditor.loadNewEditor($course_syllabus_body, {
      focus: true,
      manageParent: true
    })

    $('.jump_to_today_link').focus()
  }) // a11y: Set focus so it doesn't get lost.

  function recreateCourseSyllabusBody() {
    $('#tinymce-parent-of-course_syllabus_body').append($course_syllabus_body)
  }

  $edit_course_syllabus_form.on('hide_edit', () => {
    $edit_course_syllabus_form.hide()
    $edit_syllabus_link.show()
    $course_syllabus.show()
    easy_student_view.show()
    const text = $.trim($course_syllabus.html())
    $course_syllabus_details.showIf(!text)
    RichContentEditor.destroyRCE($course_syllabus_body)
    recreateCourseSyllabusBody()
    resetToggleLinks()
    $edit_syllabus_link.focus()
  })

  $edit_syllabus_link.on('click', ev => {
    ev.preventDefault()
    $edit_course_syllabus_form.triggerHandler('edit')
  })

  $edit_course_syllabus_form.on('click', '.toggle_views_link', ev => {
    ev.preventDefault()
    RichContentEditor.callOnRCE($course_syllabus_body, 'toggle')
    // hide the clicked link, and show the other toggle link.
    // todo: replace .andSelf with .addBack when JQuery is upgraded.
    $(ev.currentTarget)
      .siblings('.toggle_views_link')
      .andSelf()
      .toggle()
      .focus()
  })

  $edit_course_syllabus_form.on('click', '.cancel_button', ev => {
    ev.preventDefault()
    RichContentEditor.closeRCE($course_syllabus_body)
    $edit_course_syllabus_form.triggerHandler('hide_edit')
  })

  return $edit_course_syllabus_form.formSubmit({
    object_name: 'course',

    processData(data) {
      RichContentEditor.closeRCE($course_syllabus_body) // I'd like to wait until success, but by then the RCE is gone
      const syllabus_body = RichContentEditor.callOnRCE($course_syllabus_body, 'get_code')
      data['course[syllabus_body]'] = syllabus_body
      return data
    },

    beforeSubmit(data) {
      $edit_course_syllabus_form.triggerHandler('hide_edit')
      $course_syllabus_details.hide()
      $course_syllabus.loadingImage()
    },

    success(data) {
      if (data.course.settings.syllabus_course_summary !== course_summary_enabled) {
        return window.location.reload()
      }
      /*
      xsslint safeString.property syllabus_body
      */
      $course_syllabus.loadingImage('remove').html(data.course.syllabus_body)
      $course_syllabus.data('syllabus_body', data.course.syllabus_body)
      $course_syllabus_details.hide()
      if (!ENV.FEATURES.new_math_equation_handling) {
        if (mathml.isMathMLOnPage()) {
          if (mathml.isMathJaxLoaded()) {
            mathml.reloadElement('content')
          } else {
            mathml.loadMathJax(undefined)
          }
        }
      }
    },

    error(data) {
      return $edit_course_syllabus_form.triggerHandler('edit').formErrors(data)
    }
  })
}

export default {
  bindToEditSyllabus,
  bindToMiniCalendar,
  bindToSyllabus
}
<commit_msg>Escape html in syllabus 'jump to today'<commit_after>//
// Copyright (C) 2013 - present Instructure, Inc.
//
// This file is part of Canvas.
//
// Canvas is free software: you can redistribute it and/or modify it under
// the terms of the GNU Affero General Public License as published by the Free
// Software Foundation, version 3 of the License.
//
// Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
// A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
// details.
//
// You should have received a copy of the GNU Affero General Public License along
// with this program. If not, see <http://www.gnu.org/licenses/>.
//

import $ from 'jquery'
import {changeMonth} from '../../jquery/calendar_move' // calendarMonths
import RichContentEditor from '@canvas/rce/RichContentEditor'
import KeyboardShortcuts from '@canvas/tinymce-keyboard-shortcuts'
import '@canvas/datetime' // dateString, datepicker
import '@canvas/forms/jquery/jquery.instructure_forms' // formSubmit, formErrors
import '@canvas/jquery/jquery.instructure_misc_plugins' // ifExists, showIf
import '@canvas/loading-image'
import 'jquery-scroll-to-visible/jquery.scrollTo'
import 'jqueryui/datepicker'
import easy_student_view from '@canvas/easy-student-view'
import mathml from 'mathml'
import htmlEscape from 'html-escape'

RichContentEditor.preloadRemoteModule()

// Highlight mini calendar days matching syllabus events
//    Queries the syllabus event list and highlights the
//    corresponding mini calendar dates.
function highlightDaysWithEvents() {
  const $mini_month = $('.mini_month')
  const $syllabus = $('#syllabus')
  if (!$mini_month || !$syllabus) return

  let events = $mini_month.find('.day.has_event')
  events.removeClass('has_event')
  let wrapper = events.find('.day_wrapper')
  wrapper.removeAttr('role')
  wrapper.removeAttr('tabindex')

  $syllabus.find('tr.date:visible').each(function() {
    const date = $(this)
      .find('.day_date')
      .attr('data-date')
    events = $mini_month.find(`#mini_day_${date}`)
    events.addClass('has_event')
    wrapper = events.find('.day_wrapper')
    wrapper.attr('role', 'link')
    wrapper.attr('tabindex', '0')
  })
}

// Sets highlighting on a given date
//    Removes all highlighting then highlights the given
//    date, if provided.
function highlightDate(date) {
  const $mini_month = $('.mini_month')
  const $syllabus = $('#syllabus')

  $('tr.date.related, .day.related').removeClass('related')
  if (date) {
    if ($mini_month) $mini_month.find(`#mini_day_${date}`).addClass('related')
    if ($syllabus) $syllabus.find(`tr.date.events_${date}`).addClass('related')
  }
}

function highlightRelated(related_id, self) {
  const $syllabus = $('#syllabus')

  $syllabus.find('tr.related_event').removeClass('related_event')
  if (related_id && $syllabus) {
    $syllabus
      .find(`tr.related-${related_id}`)
      .not(self)
      .addClass('related_event')
  }
}

// Binds to #syllabus dom events
//    Called to bind behaviors to #syllabus after it's rendered.
function bindToSyllabus() {
  const $syllabus = $('#syllabus')
  $syllabus.on('mouseenter mouseleave', 'tr.date', function(ev) {
    let date
    if (ev.type === 'mouseenter')
      date = $(this)
        .find('.day_date')
        .attr('data-date')
    highlightDate(date)
  })

  $syllabus.on('mouseenter mouseleave', 'tr.date.detail_list', function(ev) {
    let related_id = null
    if (ev.type === 'mouseenter') {
      const classNames = ($(this).attr('class') || '').split(/\s+/)
      classNames.some(c => {
        if (c.substr(0, 8) === 'related-') {
          return (related_id = c.substr(8))
        }
      })
    }

    highlightRelated(related_id, this)
  })

  highlightDaysWithEvents()

  const todayString = $.datepicker.formatDate('yy_mm_dd', new Date())
  highlightDate(todayString)
}

function selectRow($row) {
  if ($row.length > 0) {
    $('tr.selected').removeClass('selected')
    $row.addClass('selected')
    $('html, body').scrollTo($row)
    $row
      .find('a')
      .first()
      .focus()
  }
}

function selectDate(date) {
  $('.mini_month .day.selected').removeClass('selected')
  $('.mini_month')
    .find(`#mini_day_${date}`)
    .addClass('selected')
}

// Binds to mini calendar dom events
function bindToMiniCalendar() {
  const $mini_month = $('.mini_month')

  const prev_next_links = $mini_month.find('.next_month_link, .prev_month_link')
  prev_next_links.on('click', function(ev) {
    ev.preventDefault()
    changeMonth($mini_month, $(this).hasClass('next_month_link') ? 1 : -1)
    highlightDaysWithEvents()
  })

  const miniCalendarDayClick = function(ev) {
    ev.preventDefault()
    const date = $(ev.target)
      .closest('.mini_calendar_day')[0]
      .id.slice(9)
    const [year, month, day] = Array.from(date.split('_'))
    changeMonth($mini_month, `${month}/${day}/${year}`)
    highlightDaysWithEvents()
    selectDate(date)
    $(`.events_${date}`).ifExists($events => setTimeout(() => selectRow($events), 0)) // focus race condition hack. why do you do this to me, IE?
  }

  $mini_month.on('keypress', '.day_wrapper', ev => {
    if (ev.which === 13 || ev.which === 32) miniCalendarDayClick(ev)
  })

  $mini_month.on('click', '.day_wrapper', miniCalendarDayClick)

  $mini_month.on('focus blur mouseover mouseout', '.day_wrapper', ev => {
    let date
    if (ev.type !== 'mouseout' && ev.type !== 'blur') {
      date = $(ev.target)
        .closest('.mini_calendar_day')[0]
        .id.slice(9)
    }
    highlightDate(date)
  })

  $('.jump_to_today_link').on('click', ev => {
    ev.preventDefault()
    const todayString = $.datepicker.formatDate('yy_mm_dd', new Date())
    let $lastBefore
    $('tr.date').each(function() {
      const dateString = $(this)
        .find('.day_date')
        .attr('data-date')

      if (dateString) {
        if (dateString > todayString) return false
        $lastBefore = dateString
      }
    })

    changeMonth($mini_month, $.datepicker.formatDate('mm/dd/yy', new Date()))
    highlightDaysWithEvents()

    if (!$lastBefore) $lastBefore = $('tr.date:first')

    selectDate(todayString)
    selectRow($(htmlEscape(`tr.date.events_${$lastBefore}`)))
  })
}

// Binds to edit syllabus dom events
const bindToEditSyllabus = function(course_summary_enabled) {
  const $course_syllabus = $('#course_syllabus')
  $course_syllabus.data('syllabus_body', ENV.SYLLABUS_BODY)
  const $edit_syllabus_link = $('.edit_syllabus_link')

  // if there's no edit link, don't need to (and shouldn't) do the rest of
  // this. the edit link is included on the page if and only if the user has
  // :manage_content permission on the course (see assignments'
  // syllabus_right_side view)
  if (!$edit_syllabus_link.length) return

  // Add the backbone view for keyboardshortup help here
  if (!ENV.use_rce_enhancements) {
    $('.toggle_views_link')
      .first()
      .before(new KeyboardShortcuts().render().$el)
  }

  function resetToggleLinks() {
    $('.toggle_html_editor_link').show()
    $('.toggle_rich_editor_link').hide()
  }

  const $edit_course_syllabus_form = $('#edit_course_syllabus_form')
  let $course_syllabus_body = $('#course_syllabus_body')
  const $course_syllabus_details = $('#course_syllabus_details')

  RichContentEditor.initSidebar({
    show() {
      $('#sidebar_content, #course_show_secondary').hide()
    },
    hide() {
      $('#sidebar_content, #course_show_secondary').show()
    }
  })

  $edit_course_syllabus_form.on('edit', () => {
    $edit_course_syllabus_form.show()
    $edit_syllabus_link.hide()
    $course_syllabus.hide()
    $course_syllabus_details.hide()
    easy_student_view.hide()
    $course_syllabus_body = RichContentEditor.freshNode($course_syllabus_body)
    $course_syllabus_body.val($course_syllabus.data('syllabus_body'))
    RichContentEditor.loadNewEditor($course_syllabus_body, {
      focus: true,
      manageParent: true
    })

    $('.jump_to_today_link').focus()
  }) // a11y: Set focus so it doesn't get lost.

  function recreateCourseSyllabusBody() {
    $('#tinymce-parent-of-course_syllabus_body').append($course_syllabus_body)
  }

  $edit_course_syllabus_form.on('hide_edit', () => {
    $edit_course_syllabus_form.hide()
    $edit_syllabus_link.show()
    $course_syllabus.show()
    easy_student_view.show()
    const text = $.trim($course_syllabus.html())
    $course_syllabus_details.showIf(!text)
    RichContentEditor.destroyRCE($course_syllabus_body)
    recreateCourseSyllabusBody()
    resetToggleLinks()
    $edit_syllabus_link.focus()
  })

  $edit_syllabus_link.on('click', ev => {
    ev.preventDefault()
    $edit_course_syllabus_form.triggerHandler('edit')
  })

  $edit_course_syllabus_form.on('click', '.toggle_views_link', ev => {
    ev.preventDefault()
    RichContentEditor.callOnRCE($course_syllabus_body, 'toggle')
    // hide the clicked link, and show the other toggle link.
    // todo: replace .andSelf with .addBack when JQuery is upgraded.
    $(ev.currentTarget)
      .siblings('.toggle_views_link')
      .andSelf()
      .toggle()
      .focus()
  })

  $edit_course_syllabus_form.on('click', '.cancel_button', ev => {
    ev.preventDefault()
    RichContentEditor.closeRCE($course_syllabus_body)
    $edit_course_syllabus_form.triggerHandler('hide_edit')
  })

  return $edit_course_syllabus_form.formSubmit({
    object_name: 'course',

    processData(data) {
      RichContentEditor.closeRCE($course_syllabus_body) // I'd like to wait until success, but by then the RCE is gone
      const syllabus_body = RichContentEditor.callOnRCE($course_syllabus_body, 'get_code')
      data['course[syllabus_body]'] = syllabus_body
      return data
    },

    beforeSubmit(data) {
      $edit_course_syllabus_form.triggerHandler('hide_edit')
      $course_syllabus_details.hide()
      $course_syllabus.loadingImage()
    },

    success(data) {
      if (data.course.settings.syllabus_course_summary !== course_summary_enabled) {
        return window.location.reload()
      }
      /*
      xsslint safeString.property syllabus_body
      */
      $course_syllabus.loadingImage('remove').html(data.course.syllabus_body)
      $course_syllabus.data('syllabus_body', data.course.syllabus_body)
      $course_syllabus_details.hide()
      if (!ENV.FEATURES.new_math_equation_handling) {
        if (mathml.isMathMLOnPage()) {
          if (mathml.isMathJaxLoaded()) {
            mathml.reloadElement('content')
          } else {
            mathml.loadMathJax(undefined)
          }
        }
      }
    },

    error(data) {
      return $edit_course_syllabus_form.triggerHandler('edit').formErrors(data)
    }
  })
}

export default {
  bindToEditSyllabus,
  bindToMiniCalendar,
  bindToSyllabus
}
<|endoftext|>


<commit_before>"use strict";

function filterParams(pattern) {
	return function(details) {

		let url = new URL(details.url);
		let changed = false;

		for (let param of url.searchParams) {
			if (param[0].match(pattern)) {
				changed = true;
				url.searchParams.delete(param[0]);
			}
		}

		if (changed) {
			return {redirectUrl: url.href};
		} else {
			return {};
		}
	}
}

var filters = [
	{
		urls: ["<all_urls>"],
		fn: filterParams("^utm_(?:content|campaign|source|medium|term|id)")
	},
	{
		urls: ["*://*.aliexpress.com/*"],
		fn: filterParams("^(?:ws_ab_test|btsid)")
	}
];

for (var idx = 0; idx < filters.length; idx++) {
	chrome.webRequest.onBeforeRequest.addListener(
		filters[idx].fn,
		{urls: filters[idx].urls},
		["blocking"]
	);
}
<commit_msg>switch to single quotes<commit_after>'use strict';

function filterParams(pattern) {
	return function(details) {

		let url = new URL(details.url);
		let changed = false;

		for (let param of url.searchParams) {
			if (param[0].match(pattern)) {
				changed = true;
				url.searchParams.delete(param[0]);
			}
		}

		if (changed) {
			return {redirectUrl: url.href};
		} else {
			return {};
		}
	}
}

var filters = [
	{
		urls: ['<all_urls>'],
		fn: filterParams('^utm_(?:content|campaign|source|medium|term|id)')
	},
	{
		urls: ['*://*.aliexpress.com/*'],
		fn: filterParams('^(?:ws_ab_test|btsid)')
	}
];

for (var idx = 0; idx < filters.length; idx++) {
	chrome.webRequest.onBeforeRequest.addListener(
		filters[idx].fn,
		{urls: filters[idx].urls},
		['blocking']
	);
}
<|endoftext|>


<commit_before>var through = require('through2');
var tr = through(function(buffer, encoding, next) {
  this.push(buffer.toString('utf8').toUpperCase());
  next();
});

process.stdin.pipe(tr).pipe(process.stdout);
<commit_msg>completes step 5<commit_after>var split = require('split');
var through = require('through2');
var index = 1;

process.stdin
    .pipe(split())
    .pipe(through(function(line, encoding, next) {
      if (index % 2 === 0) {
        this.push(line.toString('utf8').toUpperCase() + '\n');
      } else {
        this.push(line.toString('utf8').toLowerCase() + '\n');
      }

      index += 1;
      next();
    })).pipe(process.stdout);
<|endoftext|>


<commit_before>var Helper = require('./helper');

function Promise(planned) {
  this.state = new StatefulResult();
}
Promise.PENDING = 0;
Promise.FULFILLED = 1;
Promise.REJECTED = 2;
Promise.prototype = {
  'constructor': Promise,
  // ---------------------------------------------------------------------------
  // 1. ENTRY - PRODUCE RESULT. NOT STATE
  // ---------------------------------------------------------------------------

  // ---------------------------------------------------------------------------
  // 1. ENTRY - EXTEND CONSUMER.
  // ---------------------------------------------------------------------------

  // ---------------------------------------------------------------------------
  // 2. PRODUCE STATE BY RESULT (VALUE/REASON)
  // ---------------------------------------------------------------------------

  // ---------------------------------------------------------------------------
  // 3. HANDLE STATE + RESULT
  // ---------------------------------------------------------------------------

  // ---------------------------------------------------------------------------
  // 4. PASS STATE + RESULT TO NEXT POSSIBLE PROMISES
  // ---------------------------------------------------------------------------
};
// -----------------------------------------------------------------------------
function StatefulResult(state, result) {
  this.state = state || Promise.PENDING;
  this.result = result; // XXX never change it even it's falsy
}
StatefulResult.isValid = function(state) {
  return Promise.PENDING <= state.type() && state.type() <= Promise.REJECTED;
};
StatefulResult.prototype = {
  'isReady': function() {
    return this.state !== Promise.PENDING;
  },
  'type': function() {
    return this.state;
  },
  'value': function() {
    return this.result;
  }
};
// -----------------------------------------------------------------------------

module.exports = {
  resolved: function(value) {
    return new Promise(function(resolve) {
      resolve(value);
    });
  },
  rejected: function(reason) {
    return new Promise(function(resolve, reject) {
      reject(reason);
    });
  },
  deferred: function() {
    var exportedResolve;
    var exportedReject;

    return {
      promise: new Promise(function(internalResolve, internalReject) {
        exportedResolve = internalResolve;
        exportedReject = internalReject;
      }),
      resolve: exportedResolve,
      reject: exportedReject
    };
  }
};
<commit_msg>implement `then` method<commit_after>var Helper = require('./helper');

function Promise(planned) {
  this.state = new StatefulResult();
  this.logics = new LogicPack();
  this.queue = [];
}
Promise.PENDING = 0;
Promise.FULFILLED = 1;
Promise.REJECTED = 2;
Promise.prototype = {
  'constructor': Promise,
  // ---------------------------------------------------------------------------
  // 1. ENTRY - PRODUCE RESULT. NOT STATE
  // ---------------------------------------------------------------------------

  // ---------------------------------------------------------------------------
  // 1. ENTRY - EXTEND CONSUMER.
  // ---------------------------------------------------------------------------
  'then': function(fullfilledLogic, rejectedLogic) {
    // could be primative, function or object. but normally, it's a Promise
    var consumer = new Promise();
    consumer.logics = new LogicPack(fullfilledLogic, rejectedLogic);

    this.enqueue(consumer);
    return consumer;
  },
  'enqueue': function(consumer) {
    this.queue.push(consumer);
  },
  // ---------------------------------------------------------------------------
  // 2. PRODUCE STATE BY RESULT (VALUE/REASON)
  // ---------------------------------------------------------------------------

  // ---------------------------------------------------------------------------
  // 3. HANDLE STATE + RESULT
  // ---------------------------------------------------------------------------

  // ---------------------------------------------------------------------------
  // 4. PASS STATE + RESULT TO NEXT POSSIBLE PROMISES
  // ---------------------------------------------------------------------------
};
// -----------------------------------------------------------------------------
function StatefulResult(state, result) {
  this.state = state || Promise.PENDING;
  this.result = result; // XXX never change it even it's falsy
}
StatefulResult.isValid = function(state) {
  return Promise.PENDING <= state.type() && state.type() <= Promise.REJECTED;
};
StatefulResult.prototype = {
  'isReady': function() {
    return this.state !== Promise.PENDING;
  },
  'type': function() {
    return this.state;
  },
  'value': function() {
    return this.result;
  }
};
// -----------------------------------------------------------------------------
function LogicPack(fullfilledLogic, rejectedLogic) {
  // logic could go wrong w/o fixed destination: resolve/reject
  fullfilledLogic = Helper.isFunction(fullfilledLogic) ?
                fullfilledLogic : function(value) {return value;};
  rejectedLogic = Helper.isFunction(rejectedLogic) ?
                rejectedLogic : function(reason) {throw reason;};
  this.logics = [];
  Helper.insertAt(this.logics, Promise.PENDING, null);
  Helper.insertAt(this.logics, Promise.FULFILLED, fullfilledLogic);
  Helper.insertAt(this.logics, Promise.REJECTED, rejectedLogic);
}
LogicPack.prototype = {
  'getBy': function(state) {
    return this.logics[state.type()];
  }
};

module.exports = {
  resolved: function(value) {
    return new Promise(function(resolve) {
      resolve(value);
    });
  },
  rejected: function(reason) {
    return new Promise(function(resolve, reject) {
      reject(reason);
    });
  },
  deferred: function() {
    var exportedResolve;
    var exportedReject;

    return {
      promise: new Promise(function(internalResolve, internalReject) {
        exportedResolve = internalResolve;
        exportedReject = internalReject;
      }),
      resolve: exportedResolve,
      reject: exportedReject
    };
  }
};
<|endoftext|>


<commit_before>/**
 * Crafty.math - Static functions.
 */

Crafty.math = {
    /**
     * Returns the absolute value.
     * @param x Some value.
     * @return param Absolute value.
     */
    abs: function(x) {
        return x < 0 ? -x : x;
    },

    /**
     * Returns the amount of how much a checkValue is more like minValue (=0)
     * or more like maxValue (=1)
     * @param checkValue Value that should checked with minimum and maximum.
     * @param minValue Minimum value to check.
     * @param maxValue Maximum value to check.
     * @return param Amount of checkValue compared to minValue and maxValue.
     */
    amountOf: function(checkValue, minValue, maxValue) {
        if (minValue < maxValue)
            return (checkValue - minValue) / (maxValue - minValue);
        else
            return (checkValue - maxValue) / (minValue - maxValue);
    },

    /**
     * Restricts a value to be within a specified range.
     * @param value A value.
     * @param max Maximum that value can be.
     * @param min Minimum that value can be.
     * @return param The value between minimum and maximum.
     */
    clamp: function(value, min, max) {
        if (value > max)
            return max;
        else if (value < min)
            return min;
        else
            return value;
    },

    /**
     * Converts angle from degree to radian.
     * @param angleInDeg The angle in degree.
     * @return param The angle in radian.
     */
    degToRad: function(angleInDeg) {
        return angleInDeg * Math.PI / 180;
    },

    /**
     * Distance between two points.
     * @param x1 First x coordinate.
     * @param y1 First y coordinate.
     * @param x2 Second x coordinate.
     * @param y2 Second y coordinate.
     * @return param The distance between the two points.
     */
    distance: function(x1, y1, x2, y2) {
        var squaredDistance = Crafty.math.squaredDistance(x1, y1, x2, y2);
        return Math.sqrt(parseFloat(squaredDistance));
    },

    /**
     * Linear interpolation. Passing amount with a value of 0 will cause value1 to be returned,
     * a value of 1 will cause value2 to be returned.
     * @param value1 One value.
     * @param value2 Another value.
     * @param amount Amount of value2 to value1.
     * @return param Linear interpolated value.
     */
    lerp: function(value1, value2, amount) {
        return value1 + (value2 - value1) * amount;
    },

    /**
     * Returnes "randomly" -1.
     * @param percent If you pass 1 a -1 will be returned. If you pass 0 a 1 will be returned.
     * @return param 1 or -1.
     */
    negate: function(percent) {
        if (Math.random() < percent)
            return -1;
        else
            return 1;
    },

    /**
     * Converts angle from radian to degree.
     * @param angleInRad The angle in radian.
     * @return param The angle in degree.
     */
    radToDeg: function(angleInRad) {
        return angleInRad * 180 / Math.PI;
    },

    /**
     * Returns a random element of a specific array.
     * @param array A specific array.
     * @return param A random element of a specific array.
     */
    randomElementOfArray: function(array) {
        return array[array.length * Math.random()];
    },

    /**
     * Returns a random int in within a specific range.
     * @param start Smallest int value that can be returned.
     * @param end Biggest int value that can be returned.
     * @return param A random int.
     */
    randomInt: function(start, end) {
        return start + Math.floor((1 + end - start) * Math.random());
    },

    /**
     * Returns a random number in within a specific range.
     * @param start Smallest number value that can be returned.
     * @param end Biggest number value that can be returned.
     * @return param A random number.
     */
    randomNumber: function(start, end) {
        return start + (end - start) * Math.random();
    },

    /**
     * Squared distance between two points.
     * @param x1 First x coordinate.
     * @param y1 First y coordinate.
     * @param x2 Second x coordinate.
     * @param y2 Second y coordinate.
     * @return param The squared distance between the two points.
     */
    squaredDistance: function(x1, y1, x2, y2) {
        return (x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2);
    },

    /**
     * Check if a value is within a specific range.
     * @param value The specific value.
     * @param min Minimum value.
     * @param max Maximum value.
     * @return param Returns true if value is within a specific range.
     */
    withinRange: function(value, min, max) {
        return (value >= min && value <= max);
    }
};

/**
 * Crafty.math - Classes.
 */

/**
 * Vector2D
 *
 * A vector class for basic vector calculations.
 * @param x The x value of the vector (default: 0).
 * @param y The y value of the vector (default: 0).
 */
Crafty.math.Vector2D = (function() {
    // place vor private attributes/methodes

    // return constructor
    return function Vector2D(x, y) {
        if (arguments.length === 1)
            y = 0;
        else if (arguments.length === 0)
            x = y = 0;

        this.x = x;
        this.y = y;
    };
})();

/**
 * Public functions.
 */

/**
 * Normalize vector. (Set length to 1.)
 * @return This vector.
 */
Crafty.math.Vector2D.prototype.normalize = function() {
    var l = this.getLength();
    if (l === 0)
        this.x = 1;
    else {
        this.x = this.x / l;
        this.y = this.y / l;
    }
    return this;
};

/**
 * Get vector length.
 * @return Length of Vector.
 */
Crafty.math.Vector2D.prototype.getLength = function() {
    return Math.sqrt(this.x * this.x + this.y * this.y);
};

/**
 * Set vector length.
 * @param length New length of Vector.
 */
Crafty.math.Vector2D.prototype.setLength = function(length) {
    this.normalize().multiply(length);
};

/**
 * Calculate the dot product of this vector and another.
 * @param vector Another vector.
 * @return The dot product.
 */
Crafty.math.Vector2D.prototype.dotProduct = function(vector) {
    return this.x * vector.x + this.y * vector.y;
};

/**
 * Calculate normal of line between this vector and another and
 * returns righthand normal vector (-dy, dx).
 * (Lefthand would be (dy, -dx). dy first because normal is rotated in 90° to vector.)
 * @param vector Another vector. If no vector is passed, a zero-vector will be used.
 * @return Normal vector.
 */
Crafty.math.Vector2D.prototype.getNormal = function(vector) {
    if (arguments.length === 0) vector = new Crafty.math.Vector2D();
    return new Crafty.math.Vector2D(-(this.y - vector.y), this.x - vector.x);	// (-dy, dx) == right or (dy, -dx) == left
};

/**
 * Get angle between this vector and another in rad.
 * Math.acos( a * b / ( |a| * |b| ) );
 * @param vector Another vector.
 * @return The angle between this vector and another in rad.
 */
Crafty.math.Vector2D.prototype.getAngle = function(vector) {
    return Math.acos(this.dotProduct(vector) / (this.getLength() * vector.getLength()));
    // or: Math.atan2(y, x) ?
};

/**
 * Checks if x and y are 0.
 * @return A boolean which specifies if this vector is a zero-vector.
 */
Crafty.math.Vector2D.prototype.isZero = function() {
    return this.x === 0 && this.y === 0;
};

/**
 * Negates this vector (-x, -y).
 * @return Returns this vector.
 */
Crafty.math.Vector2D.prototype.negate = function() {
    this.x *= -1;
    this.y *= -1;
    return this;
};

/**
 * Calculates distance between this vector and another.
 * @param vector Another vector.
 * @return Returns distance.
 */
Crafty.math.Vector2D.prototype.distance = function(vector) {
    return Math.sqrt(this.squaredDistance(vector));
};

/**
 * Multiply this vector with a scalar.
 * @param scalar A scalar which will be multiplied to this vector.
 * @return This vector.
 */
Crafty.math.Vector2D.prototype.multiply = function(scalar) {
    this.x *= scalar;
    this.y *= scalar;
    return this;
};

/**
 * Calculates squared distance between this vector and another.
 * @param vector Another vector.
 * @return Returns squared distance.
 */
Crafty.math.Vector2D.prototype.squaredDistance = function(vector) {
    return (this.x - vector.x) * (this.x - vector.x) + (this.y - vector.y) * (this.y - vector.y);
};

/**
 * Subtracts another vector from this vector.
 * @param vector Another vector.
 * @return This vector.
 */
Crafty.math.Vector2D.prototype.subtract = function(vector) {
    this.x -= vector.x;
    this.y -= vector.y;
    return this;
};

/**
 * Adds another vector to this vector.
 * @param vector Another vector.
 * @return This vector.
 */
Crafty.math.Vector2D.prototype.add = function(vector) {
    this.x += vector.x;
    this.y += vector.y;
    return this;
};

/**
 * Creates a copy of this vector.
 * @return Copy of this vector.
 */
Crafty.math.Vector2D.prototype.getCopy = function() {
    return new Crafty.math.Vector2D(this.x, this.y);
};

/**
 * Creates a new vector wich is the negative vector of this one.
 * @return New vector, which is the negative of this one.
 */
Crafty.math.Vector2D.prototype.getNegative = function() {
    return new Crafty.math.Vector2D(-this.x, -this.y);
};

/**
 * Get new vector from this vector to another.
 * @param vector Another vector.
 * @return New vector which points form this vector to antoher..
 */
Crafty.math.Vector2D.prototype.to = function(vector) {
    return new Crafty.math.Vector2D(vector.x - this.x, vector.y - this.y);
};

/**
 * Set x and y values of this vector
 * @param vector Holds the new x and y values.
 */
Crafty.math.Vector2D.prototype.setValues = function(vector) {
    this.x = vector.x;
    this.y = vector.y;
};

/**
 * Static public functions.
 */

/**
 * Create a triple product.
 * @param a Vecvtor1.
 * @param b Vecvtor2.
 * @param c Vecvtor3.
 * @return Returns the triple product of three vectors as a new vector.
 */
Crafty.math.Vector2D.tripleProduct = function(a, b, c) {
    var v = new Crafty.math.Vector2D();
    var ac = a.dotProduct(c);
    var bc = b.dotProduct(c);
    v.x = b.x * ac - a.x * bc;
    v.y = b.y * ac - a.y * bc;
    return v;
};<commit_msg>Fixed bug in Crafty.math.Vector2D.prototype.getAngle()<commit_after>/**
 * Crafty.math - Static functions.
 */

Crafty.math = {
    /**
     * Returns the absolute value.
     * @param x Some value.
     * @return param Absolute value.
     */
    abs: function(x) {
        return x < 0 ? -x : x;
    },

    /**
     * Returns the amount of how much a checkValue is more like minValue (=0)
     * or more like maxValue (=1)
     * @param checkValue Value that should checked with minimum and maximum.
     * @param minValue Minimum value to check.
     * @param maxValue Maximum value to check.
     * @return param Amount of checkValue compared to minValue and maxValue.
     */
    amountOf: function(checkValue, minValue, maxValue) {
        if (minValue < maxValue)
            return (checkValue - minValue) / (maxValue - minValue);
        else
            return (checkValue - maxValue) / (minValue - maxValue);
    },

    /**
     * Restricts a value to be within a specified range.
     * @param value A value.
     * @param max Maximum that value can be.
     * @param min Minimum that value can be.
     * @return param The value between minimum and maximum.
     */
    clamp: function(value, min, max) {
        if (value > max)
            return max;
        else if (value < min)
            return min;
        else
            return value;
    },

    /**
     * Converts angle from degree to radian.
     * @param angleInDeg The angle in degree.
     * @return param The angle in radian.
     */
    degToRad: function(angleInDeg) {
        return angleInDeg * Math.PI / 180;
    },

    /**
     * Distance between two points.
     * @param x1 First x coordinate.
     * @param y1 First y coordinate.
     * @param x2 Second x coordinate.
     * @param y2 Second y coordinate.
     * @return param The distance between the two points.
     */
    distance: function(x1, y1, x2, y2) {
        var squaredDistance = Crafty.math.squaredDistance(x1, y1, x2, y2);
        return Math.sqrt(parseFloat(squaredDistance));
    },

    /**
     * Linear interpolation. Passing amount with a value of 0 will cause value1 to be returned,
     * a value of 1 will cause value2 to be returned.
     * @param value1 One value.
     * @param value2 Another value.
     * @param amount Amount of value2 to value1.
     * @return param Linear interpolated value.
     */
    lerp: function(value1, value2, amount) {
        return value1 + (value2 - value1) * amount;
    },

    /**
     * Returnes "randomly" -1.
     * @param percent If you pass 1 a -1 will be returned. If you pass 0 a 1 will be returned.
     * @return param 1 or -1.
     */
    negate: function(percent) {
        if (Math.random() < percent)
            return -1;
        else
            return 1;
    },

    /**
     * Converts angle from radian to degree.
     * @param angleInRad The angle in radian.
     * @return param The angle in degree.
     */
    radToDeg: function(angleInRad) {
        return angleInRad * 180 / Math.PI;
    },

    /**
     * Returns a random element of a specific array.
     * @param array A specific array.
     * @return param A random element of a specific array.
     */
    randomElementOfArray: function(array) {
        return array[array.length * Math.random()];
    },

    /**
     * Returns a random int in within a specific range.
     * @param start Smallest int value that can be returned.
     * @param end Biggest int value that can be returned.
     * @return param A random int.
     */
    randomInt: function(start, end) {
        return start + Math.floor((1 + end - start) * Math.random());
    },

    /**
     * Returns a random number in within a specific range.
     * @param start Smallest number value that can be returned.
     * @param end Biggest number value that can be returned.
     * @return param A random number.
     */
    randomNumber: function(start, end) {
        return start + (end - start) * Math.random();
    },

    /**
     * Squared distance between two points.
     * @param x1 First x coordinate.
     * @param y1 First y coordinate.
     * @param x2 Second x coordinate.
     * @param y2 Second y coordinate.
     * @return param The squared distance between the two points.
     */
    squaredDistance: function(x1, y1, x2, y2) {
        return (x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2);
    },

    /**
     * Check if a value is within a specific range.
     * @param value The specific value.
     * @param min Minimum value.
     * @param max Maximum value.
     * @return param Returns true if value is within a specific range.
     */
    withinRange: function(value, min, max) {
        return (value >= min && value <= max);
    }
};

/**
 * Crafty.math - Classes.
 */

/**
 * Vector2D
 *
 * A vector class for basic vector calculations.
 * @param x The x value of the vector (default: 0).
 * @param y The y value of the vector (default: 0).
 */
Crafty.math.Vector2D = (function() {
    // place vor private attributes/methodes

    // return constructor
    return function Vector2D(x, y) {
        if (arguments.length === 1)
            y = 0;
        else if (arguments.length === 0)
            x = y = 0;

        this.x = x;
        this.y = y;
    };
})();

/**
 * Public functions.
 */

/**
 * Normalize vector. (Set length to 1.)
 * @return This vector.
 */
Crafty.math.Vector2D.prototype.normalize = function() {
    var l = this.getLength();
    if (l === 0)
        this.x = 1;
    else {
        this.x = this.x / l;
        this.y = this.y / l;
    }
    return this;
};

/**
 * Get vector length.
 * @return Length of Vector.
 */
Crafty.math.Vector2D.prototype.getLength = function() {
    return Math.sqrt(this.x * this.x + this.y * this.y);
};

/**
 * Set vector length.
 * @param length New length of Vector.
 */
Crafty.math.Vector2D.prototype.setLength = function(length) {
    this.normalize().multiply(length);
};

/**
 * Calculate the dot product of this vector and another.
 * @param vector Another vector.
 * @return The dot product.
 */
Crafty.math.Vector2D.prototype.dotProduct = function(vector) {
    return this.x * vector.x + this.y * vector.y;
};

/**
 * Calculate normal of line between this vector and another and
 * returns righthand normal vector (-dy, dx).
 * (Lefthand would be (dy, -dx). dy first because normal is rotated in 90° to vector.)
 * @param vector Another vector. If no vector is passed, a zero-vector will be used.
 * @return Normal vector.
 */
Crafty.math.Vector2D.prototype.getNormal = function(vector) {
    if (arguments.length === 0) vector = new Crafty.math.Vector2D();
    return new Crafty.math.Vector2D(-(this.y - vector.y), this.x - vector.x);	// (-dy, dx) == right or (dy, -dx) == left
};

/**
 * Get angle between this vector and another in rad.
 * Math.acos( a * b / ( |a| * |b| ) );
 * @param vector Another vector.
 * @return The angle between this vector and another in rad.
 */
Crafty.math.Vector2D.prototype.getAngle = function(vector) {
    var tempV = this.to(vector);
    return Math.atan2(tempV.y , tempV.x);
};

/**
 * Checks if x and y are 0.
 * @return A boolean which specifies if this vector is a zero-vector.
 */
Crafty.math.Vector2D.prototype.isZero = function() {
    return this.x === 0 && this.y === 0;
};

/**
 * Negates this vector (-x, -y).
 * @return Returns this vector.
 */
Crafty.math.Vector2D.prototype.negate = function() {
    this.x *= -1;
    this.y *= -1;
    return this;
};

/**
 * Calculates distance between this vector and another.
 * @param vector Another vector.
 * @return Returns distance.
 */
Crafty.math.Vector2D.prototype.distance = function(vector) {
    return Math.sqrt(this.squaredDistance(vector));
};

/**
 * Multiply this vector with a scalar.
 * @param scalar A scalar which will be multiplied to this vector.
 * @return This vector.
 */
Crafty.math.Vector2D.prototype.multiply = function(scalar) {
    this.x *= scalar;
    this.y *= scalar;
    return this;
};

/**
 * Calculates squared distance between this vector and another.
 * @param vector Another vector.
 * @return Returns squared distance.
 */
Crafty.math.Vector2D.prototype.squaredDistance = function(vector) {
    return (this.x - vector.x) * (this.x - vector.x) + (this.y - vector.y) * (this.y - vector.y);
};

/**
 * Subtracts another vector from this vector.
 * @param vector Another vector.
 * @return This vector.
 */
Crafty.math.Vector2D.prototype.subtract = function(vector) {
    this.x -= vector.x;
    this.y -= vector.y;
    return this;
};

/**
 * Adds another vector to this vector.
 * @param vector Another vector.
 * @return This vector.
 */
Crafty.math.Vector2D.prototype.add = function(vector) {
    this.x += vector.x;
    this.y += vector.y;
    return this;
};

/**
 * Creates a copy of this vector.
 * @return Copy of this vector.
 */
Crafty.math.Vector2D.prototype.getCopy = function() {
    return new Crafty.math.Vector2D(this.x, this.y);
};

/**
 * Creates a new vector wich is the negative vector of this one.
 * @return New vector, which is the negative of this one.
 */
Crafty.math.Vector2D.prototype.getNegative = function() {
    return new Crafty.math.Vector2D(-this.x, -this.y);
};

/**
 * Get new vector from this vector to another.
 * @param vector Another vector.
 * @return New vector which points form this vector to antoher..
 */
Crafty.math.Vector2D.prototype.to = function(vector) {
    return new Crafty.math.Vector2D(vector.x - this.x, vector.y - this.y);
};

/**
 * Set x and y values of this vector
 * @param vector Holds the new x and y values.
 */
Crafty.math.Vector2D.prototype.setValues = function(vector) {
    this.x = vector.x;
    this.y = vector.y;
};

/**
 * Static public functions.
 */

/**
 * Create a triple product.
 * @param a Vecvtor1.
 * @param b Vecvtor2.
 * @param c Vecvtor3.
 * @return Returns the triple product of three vectors as a new vector.
 */
Crafty.math.Vector2D.tripleProduct = function(a, b, c) {
    var v = new Crafty.math.Vector2D();
    var ac = a.dotProduct(c);
    var bc = b.dotProduct(c);
    v.x = b.x * ac - a.x * bc;
    v.y = b.y * ac - a.y * bc;
    return v;
};<|endoftext|>


<commit_before>"use strict";

var halloPlugins = {
    'halloformat': {},
    'halloheadings': {formatBlocks: ["p", "h2", "h3", "h4", "h5"]},
    'hallolists': {},
    'hallohr': {},
    'halloreundo': {},
    'hallowagtaillink': {}
};

function registerHalloPlugin(name, opts) {
    halloPlugins[name] = (opts || {});
}

function makeRichTextEditable(id) {
    var input = $('#' + id);
    var richText = $('

').html(input.val());
    richText.insertBefore(input);
    input.hide();

    var removeStylingPending = false;
    function removeStyling() {
        /* Strip the 'style' attribute from spans that have no other attributes.
        (we don't remove the span entirely as that messes with the cursor position,
        and spans will be removed anyway by our whitelisting)
        */
        $('span[style]', richText).filter(function() {
            return this.attributes.length === 1;
        }).removeAttr('style');
        removeStylingPending = false;
    }

    richText.hallo({
        toolbar: 'halloToolbarFixed',
        toolbarCssClass: (input.closest('.object').hasClass('full')) ? 'full' : '',
        plugins: halloPlugins
    }).bind('hallomodified', function(event, data) {
        input.val(data.content);
        if (!removeStylingPending) {
            setTimeout(removeStyling, 100);
            removeStylingPending = true;
        }
    }).bind('paste', function(event, data) {
        setTimeout(removeStyling, 1);
    });
}

function insertRichTextDeleteControl(elem) {
    var a = $('Delete');
    $(elem).addClass('rich-text-deletable').prepend(a);
    a.click(function() {
        $(elem).fadeOut(function() {
            $(elem).remove();
        });
    });
}

function initDateChooser(id) {
    if (window.dateTimePickerTranslations) {
        $('#' + id).datetimepicker({
            timepicker: false,
            scrollInput:false,
            format: 'Y-m-d',
            i18n: {
                lang: window.dateTimePickerTranslations
            },
            lang: 'lang'
        });
    } else {
        $('#' + id).datetimepicker({
            timepicker: false,
            scrollInput:false,
            format: 'Y-m-d',
        });
    }
}

function initTimeChooser(id) {
    if (window.dateTimePickerTranslations) {
        $('#' + id).datetimepicker({
            datepicker: false,
            scrollInput:false,
            format: 'H:i',
            i18n: {
                lang: window.dateTimePickerTranslations
            },
            lang: 'lang'
        });
    } else {
        $('#' + id).datetimepicker({
            datepicker: false,
            format: 'H:i',
        });
    }
}

function initDateTimeChooser(id) {
    if (window.dateTimePickerTranslations) {
        $('#' + id).datetimepicker({
            format: 'Y-m-d H:i',
            scrollInput:false,
            i18n: {
                lang: window.dateTimePickerTranslations
            },
            language: 'lang'
        });
    } else {
    $('#' + id).datetimepicker({
            format: 'Y-m-d H:i',
        });
    }
}

function initTagField(id, autocompleteUrl) {
    $('#' + id).tagit({
        autocomplete: {source: autocompleteUrl}
    });
}

function InlinePanel(opts) {
    var self = {};

    self.setHasContent = function(){
        if($('li:visible', self.formsUl).length){
            self.formsUl.parent().removeClass('empty');
        }else{
            self.formsUl.parent().addClass('empty');
        }
    };

    self.initChildControls = function (prefix) {
        var childId = 'inline_child_' + prefix;
        var deleteInputId = 'id_' + prefix + '-DELETE';

        //mark container as having children to identify fields in use from those not
        self.setHasContent();

        $('#' + deleteInputId + '-button').click(function() {
            /* set 'deleted' form field to true */
            $('#' + deleteInputId).val('1');
            $('#' + childId).slideUp(function() {
                self.updateMoveButtonDisabledStates();
                self.setHasContent();
            });
        });
        if (opts.canOrder) {
            $('#' + prefix + '-move-up').click(function() {
                var currentChild = $('#' + childId);
                var currentChildOrderElem = currentChild.find('input[name$="-ORDER"]');
                var currentChildOrder = currentChildOrderElem.val();

                /* find the previous visible 'inline_child' li before this one */
                var prevChild = currentChild.prev(':visible');
                if (!prevChild.length) return;
                var prevChildOrderElem = prevChild.find('input[name$="-ORDER"]');
                var prevChildOrder = prevChildOrderElem.val();

                // async swap animation must run before the insertBefore line below, but doesn't need to finish first
                self.animateSwap(currentChild, prevChild);

                currentChild.insertBefore(prevChild);
                currentChildOrderElem.val(prevChildOrder);
                prevChildOrderElem.val(currentChildOrder);

                self.updateMoveButtonDisabledStates();
            });

            $('#' + prefix + '-move-down').click(function() {
                var currentChild = $('#' + childId);
                var currentChildOrderElem = currentChild.find('input[name$="-ORDER"]');
                var currentChildOrder = currentChildOrderElem.val();

                /* find the next visible 'inline_child' li after this one */
                var nextChild = currentChild.next(':visible');
                if (!nextChild.length) return;
                var nextChildOrderElem = nextChild.find('input[name$="-ORDER"]');
                var nextChildOrder = nextChildOrderElem.val();

                // async swap animation must run before the insertAfter line below, but doesn't need to finish first
                self.animateSwap(currentChild, nextChild);

                currentChild.insertAfter(nextChild);
                currentChildOrderElem.val(nextChildOrder);
                nextChildOrderElem.val(currentChildOrder);

                self.updateMoveButtonDisabledStates();
            });
        }

        /* Hide container on page load if it is marked as deleted. Remove the error
         message so that it doesn't count towards the number of errors on the tab at the
         top of the page. */
        if ( $('#' + deleteInputId).val() === "1" ) {
            $('#' + childId).hide(0, function() {
                self.updateMoveButtonDisabledStates();
                self.setHasContent();
            });
            $('#' + childId).find(".error-message").remove();
        }
    };

    self.formsUl = $('#' + opts.formsetPrefix + '-FORMS');

    self.updateMoveButtonDisabledStates = function() {
        if (opts.canOrder) {
            var forms = self.formsUl.children('li:visible');
            forms.each(function(i) {
                $('ul.controls .inline-child-move-up', this).toggleClass('disabled', i === 0).toggleClass('enabled', i !== 0);
                $('ul.controls .inline-child-move-down', this).toggleClass('disabled', i === forms.length - 1).toggleClass('enabled', i != forms.length - 1);
            });
        }
    };

    self.animateSwap = function(item1, item2){
        var parent = self.formsUl;
        var children = parent.children('li:visible');

        // Apply moving class to container (ul.multiple) so it can assist absolute positioning of it's children
        // Also set it's relatively calculated height to be an absolute one, to prevent the container collapsing while its children go absolute
        parent.addClass('moving').css('height', parent.height());

        children.each(function(){
            // console.log($(this));
            $(this).css('top', $(this).position().top);
        }).addClass('moving');

        // animate swapping around
        item1.animate({
            top:item2.position().top
        }, 200, function(){
            parent.removeClass('moving').removeAttr('style');
            children.removeClass('moving').removeAttr('style');
        });
        item2.animate({
            top:item1.position().top
        }, 200, function(){
            parent.removeClass('moving').removeAttr('style');
            children.removeClass('moving').removeAttr('style');
        });
    };

    buildExpandingFormset(opts.formsetPrefix, {
        onAdd: function(formCount) {
            function fixPrefix(str) {
                return str.replace(/__prefix__/g, formCount);
            }
            self.initChildControls(fixPrefix(opts.emptyChildFormPrefix));
            if (opts.canOrder) {
                $(fixPrefix('#id_' + opts.emptyChildFormPrefix + '-ORDER')).val(formCount);
            }
            self.updateMoveButtonDisabledStates();

            opts.onAdd(fixPrefix);
        }
    });

    return self;
}

function cleanForSlug(val, useURLify){
    if(URLify != undefined && useURLify !== false) { // Check to be sure that URLify function exists, and that we want to use it.
        return URLify(val, val.length);
    } else { // If not just do the "replace"
        return val.replace(/\s/g,"-").replace(/[^A-Za-z0-9\-]/g,"").toLowerCase();
    }
}

function initSlugAutoPopulate(){
    $('#id_title').on('focus', function(){
        $('#id_slug').data('previous-val', $('#id_slug').val());
        $(this).data('previous-val', $(this).val());
    });
    $('#id_title').on('keyup keydown keypress blur', function(){
        if($('body').hasClass('create') || (!$('#id_slug').data('previous-val').length || cleanForSlug($('#id_title').data('previous-val')) === $('#id_slug').data('previous-val'))){
            // only update slug if the page is being created from scratch, if slug is completely blank, or if title and slug prior to typing were identical
            $('#id_slug').val(cleanForSlug($('#id_title').val()));
        }
    });
}

function initSlugCleaning(){
    $('#id_slug').blur(function(){
        // if a user has just set the slug themselves, don't remove stop words etc, just illegal characters
        $(this).val(cleanForSlug($(this).val(), false));
    });
}

function initErrorDetection(){
    var errorSections = {};

    // first count up all the errors
    $('.error-message').each(function(){
        var parentSection = $(this).closest('section');

        if(!errorSections[parentSection.attr('id')]){
            errorSections[parentSection.attr('id')] = 0;
        }

        errorSections[parentSection.attr('id')] = errorSections[parentSection.attr('id')]+1;
    });

    // now identify them on each tab
    for(var index in errorSections) {
        $('.tab-nav a[href=#'+ index +']').addClass('errors').attr('data-count', errorSections[index]);
    }
}

function initCollapsibleBlocks(){
    $(".object.multi-field.collapsible").each(function(){
        var $li = $(this);
        var $fieldset = $li.find("fieldset");
        if($li.hasClass("collapsed")){
            $fieldset.hide();
        }
        $li.find("h2").click(function(){
            if(!$li.hasClass("collapsed")){
                $li.addClass("collapsed");
                $fieldset.hide("slow");
            }else{
                $li.removeClass("collapsed");
                $fieldset.show("show");
            }
        });
    });
}

$(function() {
    initSlugAutoPopulate();
    initSlugCleaning();
    initErrorDetection();
    initCollapsibleBlocks();

    $('.richtext [contenteditable="false"]').each(function() {
        insertRichTextDeleteControl(this);
    });

    /* Set up behaviour of preview button */
    $('.action-preview').click(function(e) {
        e.preventDefault();
        $this = $(this);

        var previewWindow = window.open($this.data('placeholder'), $this.data('windowname'));

        if(/MSIE/.test(navigator.userAgent)){
            submitPreview.call($this, false);
        } else {
            previewWindow.onload = function(){
                submitPreview.call($this, true);
            }
        }

        function submitPreview(enhanced){
            $.ajax({
                type: "POST",
                url: $this.data('action'),
                data: $('#page-edit-form').serialize(),
                success: function(data, textStatus, request) {
                    if (request.getResponseHeader('X-Wagtail-Preview') == 'ok') {
                        var pdoc = previewWindow.document;

                        if(enhanced){
                            var frame = pdoc.getElementById('preview-frame');

                            frame = frame.contentWindow || frame.contentDocument.document || frame.contentDocument;
                            frame.document.open();
                            frame.document.write(data);
                            frame.document.close();

                            var hideTimeout = setTimeout(function(){
                                pdoc.getElementById('loading-spinner-wrapper').className += 'remove';
                                clearTimeout(hideTimeout);
                            }) // just enough to give effect without adding discernible slowness
                        } else {
                            pdoc.open();
                            pdoc.write(data);
                            pdoc.close()
                        }
                    } else {
                        previewWindow.close();
                        document.open();
                        document.write(data);
                        document.close();
                    }
                },
                error: function(xhr, textStatus, errorThrown) {
                    /* If an error occurs, display it in the preview window so that
                    we aren't just showing the spinner forever. We preserve the original
                    error output rather than giving a 'friendly' error message so that
                    developers can debug template errors. (On a production site, we'd
                    typically be serving a friendly custom 500 page anyhow.) */

                    previewWindow.document.open();
                    previewWindow.document.write(xhr.responseText);
                    previewWindow.document.close();
                }
            });

        }

    });
});
<commit_msg>Fixed: 'use strict' caused undeclared variable to prevent preview<commit_after>"use strict";

var halloPlugins = {
    'halloformat': {},
    'halloheadings': {formatBlocks: ["p", "h2", "h3", "h4", "h5"]},
    'hallolists': {},
    'hallohr': {},
    'halloreundo': {},
    'hallowagtaillink': {}
};

function registerHalloPlugin(name, opts) {
    halloPlugins[name] = (opts || {});
}

function makeRichTextEditable(id) {
    var input = $('#' + id);
    var richText = $('

').html(input.val());
    richText.insertBefore(input);
    input.hide();

    var removeStylingPending = false;
    function removeStyling() {
        /* Strip the 'style' attribute from spans that have no other attributes.
        (we don't remove the span entirely as that messes with the cursor position,
        and spans will be removed anyway by our whitelisting)
        */
        $('span[style]', richText).filter(function() {
            return this.attributes.length === 1;
        }).removeAttr('style');
        removeStylingPending = false;
    }

    richText.hallo({
        toolbar: 'halloToolbarFixed',
        toolbarCssClass: (input.closest('.object').hasClass('full')) ? 'full' : '',
        plugins: halloPlugins
    }).bind('hallomodified', function(event, data) {
        input.val(data.content);
        if (!removeStylingPending) {
            setTimeout(removeStyling, 100);
            removeStylingPending = true;
        }
    }).bind('paste', function(event, data) {
        setTimeout(removeStyling, 1);
    });
}

function insertRichTextDeleteControl(elem) {
    var a = $('Delete');
    $(elem).addClass('rich-text-deletable').prepend(a);
    a.click(function() {
        $(elem).fadeOut(function() {
            $(elem).remove();
        });
    });
}

function initDateChooser(id) {
    if (window.dateTimePickerTranslations) {
        $('#' + id).datetimepicker({
            timepicker: false,
            scrollInput:false,
            format: 'Y-m-d',
            i18n: {
                lang: window.dateTimePickerTranslations
            },
            lang: 'lang'
        });
    } else {
        $('#' + id).datetimepicker({
            timepicker: false,
            scrollInput:false,
            format: 'Y-m-d',
        });
    }
}

function initTimeChooser(id) {
    if (window.dateTimePickerTranslations) {
        $('#' + id).datetimepicker({
            datepicker: false,
            scrollInput:false,
            format: 'H:i',
            i18n: {
                lang: window.dateTimePickerTranslations
            },
            lang: 'lang'
        });
    } else {
        $('#' + id).datetimepicker({
            datepicker: false,
            format: 'H:i',
        });
    }
}

function initDateTimeChooser(id) {
    if (window.dateTimePickerTranslations) {
        $('#' + id).datetimepicker({
            format: 'Y-m-d H:i',
            scrollInput:false,
            i18n: {
                lang: window.dateTimePickerTranslations
            },
            language: 'lang'
        });
    } else {
    $('#' + id).datetimepicker({
            format: 'Y-m-d H:i',
        });
    }
}

function initTagField(id, autocompleteUrl) {
    $('#' + id).tagit({
        autocomplete: {source: autocompleteUrl}
    });
}

function InlinePanel(opts) {
    var self = {};

    self.setHasContent = function(){
        if($('li:visible', self.formsUl).length){
            self.formsUl.parent().removeClass('empty');
        }else{
            self.formsUl.parent().addClass('empty');
        }
    };

    self.initChildControls = function (prefix) {
        var childId = 'inline_child_' + prefix;
        var deleteInputId = 'id_' + prefix + '-DELETE';

        //mark container as having children to identify fields in use from those not
        self.setHasContent();

        $('#' + deleteInputId + '-button').click(function() {
            /* set 'deleted' form field to true */
            $('#' + deleteInputId).val('1');
            $('#' + childId).slideUp(function() {
                self.updateMoveButtonDisabledStates();
                self.setHasContent();
            });
        });
        if (opts.canOrder) {
            $('#' + prefix + '-move-up').click(function() {
                var currentChild = $('#' + childId);
                var currentChildOrderElem = currentChild.find('input[name$="-ORDER"]');
                var currentChildOrder = currentChildOrderElem.val();

                /* find the previous visible 'inline_child' li before this one */
                var prevChild = currentChild.prev(':visible');
                if (!prevChild.length) return;
                var prevChildOrderElem = prevChild.find('input[name$="-ORDER"]');
                var prevChildOrder = prevChildOrderElem.val();

                // async swap animation must run before the insertBefore line below, but doesn't need to finish first
                self.animateSwap(currentChild, prevChild);

                currentChild.insertBefore(prevChild);
                currentChildOrderElem.val(prevChildOrder);
                prevChildOrderElem.val(currentChildOrder);

                self.updateMoveButtonDisabledStates();
            });

            $('#' + prefix + '-move-down').click(function() {
                var currentChild = $('#' + childId);
                var currentChildOrderElem = currentChild.find('input[name$="-ORDER"]');
                var currentChildOrder = currentChildOrderElem.val();

                /* find the next visible 'inline_child' li after this one */
                var nextChild = currentChild.next(':visible');
                if (!nextChild.length) return;
                var nextChildOrderElem = nextChild.find('input[name$="-ORDER"]');
                var nextChildOrder = nextChildOrderElem.val();

                // async swap animation must run before the insertAfter line below, but doesn't need to finish first
                self.animateSwap(currentChild, nextChild);

                currentChild.insertAfter(nextChild);
                currentChildOrderElem.val(nextChildOrder);
                nextChildOrderElem.val(currentChildOrder);

                self.updateMoveButtonDisabledStates();
            });
        }

        /* Hide container on page load if it is marked as deleted. Remove the error
         message so that it doesn't count towards the number of errors on the tab at the
         top of the page. */
        if ( $('#' + deleteInputId).val() === "1" ) {
            $('#' + childId).hide(0, function() {
                self.updateMoveButtonDisabledStates();
                self.setHasContent();
            });
            $('#' + childId).find(".error-message").remove();
        }
    };

    self.formsUl = $('#' + opts.formsetPrefix + '-FORMS');

    self.updateMoveButtonDisabledStates = function() {
        if (opts.canOrder) {
            var forms = self.formsUl.children('li:visible');
            forms.each(function(i) {
                $('ul.controls .inline-child-move-up', this).toggleClass('disabled', i === 0).toggleClass('enabled', i !== 0);
                $('ul.controls .inline-child-move-down', this).toggleClass('disabled', i === forms.length - 1).toggleClass('enabled', i != forms.length - 1);
            });
        }
    };

    self.animateSwap = function(item1, item2){
        var parent = self.formsUl;
        var children = parent.children('li:visible');

        // Apply moving class to container (ul.multiple) so it can assist absolute positioning of it's children
        // Also set it's relatively calculated height to be an absolute one, to prevent the container collapsing while its children go absolute
        parent.addClass('moving').css('height', parent.height());

        children.each(function(){
            // console.log($(this));
            $(this).css('top', $(this).position().top);
        }).addClass('moving');

        // animate swapping around
        item1.animate({
            top:item2.position().top
        }, 200, function(){
            parent.removeClass('moving').removeAttr('style');
            children.removeClass('moving').removeAttr('style');
        });
        item2.animate({
            top:item1.position().top
        }, 200, function(){
            parent.removeClass('moving').removeAttr('style');
            children.removeClass('moving').removeAttr('style');
        });
    };

    buildExpandingFormset(opts.formsetPrefix, {
        onAdd: function(formCount) {
            function fixPrefix(str) {
                return str.replace(/__prefix__/g, formCount);
            }
            self.initChildControls(fixPrefix(opts.emptyChildFormPrefix));
            if (opts.canOrder) {
                $(fixPrefix('#id_' + opts.emptyChildFormPrefix + '-ORDER')).val(formCount);
            }
            self.updateMoveButtonDisabledStates();

            opts.onAdd(fixPrefix);
        }
    });

    return self;
}

function cleanForSlug(val, useURLify){
    if(URLify != undefined && useURLify !== false) { // Check to be sure that URLify function exists, and that we want to use it.
        return URLify(val, val.length);
    } else { // If not just do the "replace"
        return val.replace(/\s/g,"-").replace(/[^A-Za-z0-9\-]/g,"").toLowerCase();
    }
}

function initSlugAutoPopulate(){
    $('#id_title').on('focus', function(){
        $('#id_slug').data('previous-val', $('#id_slug').val());
        $(this).data('previous-val', $(this).val());
    });
    $('#id_title').on('keyup keydown keypress blur', function(){
        if($('body').hasClass('create') || (!$('#id_slug').data('previous-val').length || cleanForSlug($('#id_title').data('previous-val')) === $('#id_slug').data('previous-val'))){
            // only update slug if the page is being created from scratch, if slug is completely blank, or if title and slug prior to typing were identical
            $('#id_slug').val(cleanForSlug($('#id_title').val()));
        }
    });
}

function initSlugCleaning(){
    $('#id_slug').blur(function(){
        // if a user has just set the slug themselves, don't remove stop words etc, just illegal characters
        $(this).val(cleanForSlug($(this).val(), false));
    });
}

function initErrorDetection(){
    var errorSections = {};

    // first count up all the errors
    $('.error-message').each(function(){
        var parentSection = $(this).closest('section');

        if(!errorSections[parentSection.attr('id')]){
            errorSections[parentSection.attr('id')] = 0;
        }

        errorSections[parentSection.attr('id')] = errorSections[parentSection.attr('id')]+1;
    });

    // now identify them on each tab
    for(var index in errorSections) {
        $('.tab-nav a[href=#'+ index +']').addClass('errors').attr('data-count', errorSections[index]);
    }
}

function initCollapsibleBlocks(){
    $(".object.multi-field.collapsible").each(function(){
        var $li = $(this);
        var $fieldset = $li.find("fieldset");
        if($li.hasClass("collapsed")){
            $fieldset.hide();
        }
        $li.find("h2").click(function(){
            if(!$li.hasClass("collapsed")){
                $li.addClass("collapsed");
                $fieldset.hide("slow");
            }else{
                $li.removeClass("collapsed");
                $fieldset.show("show");
            }
        });
    });
}

$(function() {
    initSlugAutoPopulate();
    initSlugCleaning();
    initErrorDetection();
    initCollapsibleBlocks();

    $('.richtext [contenteditable="false"]').each(function() {
        insertRichTextDeleteControl(this);
    });

    /* Set up behaviour of preview button */
    $('.action-preview').click(function(e) {
        e.preventDefault();
        var $this = $(this);

        var previewWindow = window.open($this.data('placeholder'), $this.data('windowname'));

        if(/MSIE/.test(navigator.userAgent)){
            submitPreview.call($this, false);
        } else {
            previewWindow.onload = function(){
                submitPreview.call($this, true);
            }
        }

        function submitPreview(enhanced){
            $.ajax({
                type: "POST",
                url: $this.data('action'),
                data: $('#page-edit-form').serialize(),
                success: function(data, textStatus, request) {
                    if (request.getResponseHeader('X-Wagtail-Preview') == 'ok') {
                        var pdoc = previewWindow.document;

                        if(enhanced){
                            var frame = pdoc.getElementById('preview-frame');

                            frame = frame.contentWindow || frame.contentDocument.document || frame.contentDocument;
                            frame.document.open();
                            frame.document.write(data);
                            frame.document.close();

                            var hideTimeout = setTimeout(function(){
                                pdoc.getElementById('loading-spinner-wrapper').className += 'remove';
                                clearTimeout(hideTimeout);
                            }) // just enough to give effect without adding discernible slowness
                        } else {
                            pdoc.open();
                            pdoc.write(data);
                            pdoc.close()
                        }
                    } else {
                        previewWindow.close();
                        document.open();
                        document.write(data);
                        document.close();
                    }
                },
                error: function(xhr, textStatus, errorThrown) {
                    /* If an error occurs, display it in the preview window so that
                    we aren't just showing the spinner forever. We preserve the original
                    error output rather than giving a 'friendly' error message so that
                    developers can debug template errors. (On a production site, we'd
                    typically be serving a friendly custom 500 page anyhow.) */

                    previewWindow.document.open();
                    previewWindow.document.write(xhr.responseText);
                    previewWindow.document.close();
                }
            });

        }

    });
});
<|endoftext|>


<commit_before>// two menu implementations -- one native and one in html
D.installMenu = function Menu(mx) {
  if (D.el) {
    const pk = D.prf.keys();
    const k = {}; // k:a map of all keyboard shortcuts
    for (let i = 0; i < D.cmds.length; i++) {
      const [c,, d] = D.cmds[i];
      [k[c]] = (pk[c] || d);
    } // c:code, d:defaults
    const render = (x) => {
      let mi;
      if (x[''] === '-') return new D.el.MenuItem({ type: 'separator' });
      const h = {
        label: x[''],
        click: x.action,
        accelerator: x.cmd && k[x.cmd] ? k[x.cmd].replace(/-(.)/g, '+$1') : null,
      };
      if (x.group) {
        h.type = 'radio';
        h.checked = !!x.checked;
      } else if (x.checkBoxPref) {
        h.type = 'checkbox';
        h.checked = !!x.checkBoxPref();
        if (x.action) h.click = () => { x.action(mi.checked); };
        x.checkBoxPref((v) => { mi.checked = !!v; });
      }
      const roles = {
        undo: 1,
        redo: 1,
        cut: 1,
        copy: 1,
        paste: 1,
        selectall: 1,
        togglefullscreen: 1,
        window: 1,
        help: 1,
      };
      const r = x[''].replace(/[& ]/g, '').toLowerCase();
      if (r in roles) h.role = r;
      if (x.items) {
        h.submenu = new D.el.Menu();
        x.items.forEach((y) => { h.submenu.append(render(y)); });
      }
      mi = new D.el.MenuItem(h);
      return mi;
    };
    const m = new D.el.Menu();
    mx.forEach((y) => { m.append(render(y)); });
    if (D.mac) D.el.Menu.setApplicationMenu(m);
    else D.elw.setMenu(m);
  } else {
    const arg = mx;
    // DOM structure:
    // ┌.menu───────────────────────────────────────────┐
    // │┌div.m-sub────────────────────────────────┐     │
    // ││            ┌div.m-box──────────────────┐│     │
    // ││┌a.m-opener┐│┌a─────┐┌a─────┐┌div.m-sub┐││     │
    // │││┌span┐    │││┌span┐││┌span┐││         │││     │
    // ││││File│    ││││Open││││Save│││   ...   │││ ... │
    // │││└────┘    │││└────┘││└────┘││         │││     │
    // ││└──────────┘│└──────┘└──────┘└─────────┘││     │
    // ││            └───────────────────────────┘│     │
    // │└─────────────────────────────────────────┘     │
    // └────────────────────────────────────────────────┘
    // Top-level ".m-opener"-s also have class ".m-top"
    let $o; // original focused element
    let $m;
    const mFocus = (anchor) => {
      $m.find('.m-open').removeClass('m-open');
      if (!anchor) { $o && $o.focus(); $o = null; return; }
      $o || ($o = $(':focus'));
      const $a = $(anchor);
      $a.parentsUntil('.menu').addClass('m-open');
      $a.is('.m-top') ? $a.closest('.m-sub').find('a').eq(1).focus() : $a.focus();
    };
    const render = (x) => {
      if (!x) return null;
      if (x[''] === '-') return $('
');
      let acc;
      const name = x[''].replace(/&(.)/g, (_, k) => {
        const r = acc || k === '&' ? k : `<u>${acc = k}</u>`;
        return r;
      }); // acc:access key
      const $a = $(`<a href=#><span>${name}</span></a>`);
      x.cmd && $a.append(`<span class=m-shc data-cmd=${x.cmd}>`);
      if (x.group) {
        $a.addClass(`m-group-${x.group}`)
          .toggleClass('m-checked', !!x.checked)
          .on('mousedown mouseup click', (e) => {
            const $e = $(e.currentTarget);
            $e.closest('.menu').find(`.m-group-${x.group}`).removeClass('m-checked');
            $e.addClass('m-checked');
            mFocus(null);
            x.action && x.action();
            return !1;
          });
      } else if (x.checkBoxPref) {
        x.checkBoxPref((v) => { $a.toggleClass('m-checked', !!v); });
        $a.toggleClass('m-checked', !!x.checkBoxPref())
          .on('mousedown mouseup click', (e) => {
            mFocus(null);
            x.action && x.action($(e.currentTarget).hasClass('m-checked'));
            return !1;
          });
      } else {
        x.action && $a.on('mousedown mouseup click', () => { mFocus(null); x.action(); return !1; });
      }
      if (!x.items) return $a;
      const $b = $('
');
      return $('
')
        .append($a.addClass('m-opener'), $b.append(...x.items.map(render)));
    };
    const leftRight = (d, $e) => { // d: +1 or -1, $e: target element
      if (d > 0 && $e.is('.m-opener')) {
        mFocus($e.next('.m-box').find('a').first());
      } else if (d < 0 && !$e.is('.m-opener') && $e.parents('.m-sub').length > 1) {
        mFocus($e.closest('.m-sub').find('.m-opener').first());
      } else {
        const $t = $m.children();
        const n = $t.length;
        const i = $e.parentsUntil('.menu').last().index();
        mFocus($t.eq((i + d + n) % n).find('a').eq(1));
      }
      return !1;
    };
    const upDown = (d, $e) => { // d: +1 or -1, $e: target element
      if ($e.is('.m-top')) {
        mFocus($e.parent().find(':not(hr)').eq(1));
      } else {
        const $s = $e.closest('.m-box').children(':not(hr)');
        const i = $s.index($e);
        const n = $s.length;
        const $f = $s.eq((i + d + n) % n);
        mFocus($f.is('a') ? $f : $f.find('a').first());
      }
      return !1;
    };
    $m = $('
').prependTo('body').empty()
      .addClass('menu')
      .append(arg.map(render));
    $m.find('>.m-sub>.m-opener').addClass('m-top');
    $m.on('mouseover', 'a', (e) => {
      const t = e.currentTarget;
      $(t).closest('.menu').children().is('.m-open') && mFocus(t);
    });
    $m.on('mousedown click', 'a', (e) => {
      const t = e.currentTarget;
      mFocus($(t).parentsUntil('.menu').last().is('.m-open') && e.type === 'mousedown' ? null : t);
      return !1;
    });
    $m.keydown((e) => {
      switch (e.key) {
        case 'ArrowLeft': leftRight(-1, $(e.target)); break;
        case 'ArrowRight': leftRight(1, $(e.target)); break;
        case 'ArrowUp': upDown(-1, $(e.target)); break;
        case 'ArrowDown': upDown(1, $(e.target)); break;
        case 'Escape': case 'F10': mFocus(null); return !1;
        default:
      }
      return !0;
    });
    $(document).mousedown((e) => { $(e.target).closest('.menu').length || mFocus(null); });
    const updShcs = (h) => {
      const k = {};
      for (let i = 0; i < D.cmds.length; i++) {
        const [c,, d] = D.cmds[i];
        [k[c]] = (h[c] || d);
      } // c:code, d:defaults
      $('.m-shc').each((e) => {
        const $e = $(e.currentTarget);
        $e.text(k[$e.data('cmd')] || '');
      });
    };
    updShcs(D.prf.keys()); D.prf.keys(updShcs);
  }
};
<commit_msg>replace menu on update in zero footprint mode (#692)<commit_after>// two menu implementations -- one native and one in html
D.installMenu = function Menu(mx) {
  if (D.el) {
    const pk = D.prf.keys();
    const k = {}; // k:a map of all keyboard shortcuts
    for (let i = 0; i < D.cmds.length; i++) {
      const [c,, d] = D.cmds[i];
      [k[c]] = (pk[c] || d);
    } // c:code, d:defaults
    const render = (x) => {
      let mi;
      if (x[''] === '-') return new D.el.MenuItem({ type: 'separator' });
      const h = {
        label: x[''],
        click: x.action,
        accelerator: x.cmd && k[x.cmd] ? k[x.cmd].replace(/-(.)/g, '+$1') : null,
      };
      if (x.group) {
        h.type = 'radio';
        h.checked = !!x.checked;
      } else if (x.checkBoxPref) {
        h.type = 'checkbox';
        h.checked = !!x.checkBoxPref();
        if (x.action) h.click = () => { x.action(mi.checked); };
        x.checkBoxPref((v) => { mi.checked = !!v; });
      }
      const roles = {
        undo: 1,
        redo: 1,
        cut: 1,
        copy: 1,
        paste: 1,
        selectall: 1,
        togglefullscreen: 1,
        window: 1,
        help: 1,
      };
      const r = x[''].replace(/[& ]/g, '').toLowerCase();
      if (r in roles) h.role = r;
      if (x.items) {
        h.submenu = new D.el.Menu();
        x.items.forEach((y) => { h.submenu.append(render(y)); });
      }
      mi = new D.el.MenuItem(h);
      return mi;
    };
    const m = new D.el.Menu();
    mx.forEach((y) => { m.append(render(y)); });
    if (D.mac) D.el.Menu.setApplicationMenu(m);
    else D.elw.setMenu(m);
  } else {
    const arg = mx;
    // DOM structure:
    // ┌.menu───────────────────────────────────────────┐
    // │┌div.m-sub────────────────────────────────┐     │
    // ││            ┌div.m-box──────────────────┐│     │
    // ││┌a.m-opener┐│┌a─────┐┌a─────┐┌div.m-sub┐││     │
    // │││┌span┐    │││┌span┐││┌span┐││         │││     │
    // ││││File│    ││││Open││││Save│││   ...   │││ ... │
    // │││└────┘    │││└────┘││└────┘││         │││     │
    // ││└──────────┘│└──────┘└──────┘└─────────┘││     │
    // ││            └───────────────────────────┘│     │
    // │└─────────────────────────────────────────┘     │
    // └────────────────────────────────────────────────┘
    // Top-level ".m-opener"-s also have class ".m-top"
    let $o; // original focused element
    let $m;
    const mFocus = (anchor) => {
      $m.find('.m-open').removeClass('m-open');
      if (!anchor) { $o && $o.focus(); $o = null; return; }
      $o || ($o = $(':focus'));
      const $a = $(anchor);
      $a.parentsUntil('.menu').addClass('m-open');
      $a.is('.m-top') ? $a.closest('.m-sub').find('a').eq(1).focus() : $a.focus();
    };
    const render = (x) => {
      if (!x) return null;
      if (x[''] === '-') return $('
');
      let acc;
      const name = x[''].replace(/&(.)/g, (_, k) => {
        const r = acc || k === '&' ? k : `<u>${acc = k}</u>`;
        return r;
      }); // acc:access key
      const $a = $(`<a href=#><span>${name}</span></a>`);
      x.cmd && $a.append(`<span class=m-shc data-cmd=${x.cmd}>`);
      if (x.group) {
        $a.addClass(`m-group-${x.group}`)
          .toggleClass('m-checked', !!x.checked)
          .on('mousedown mouseup click', (e) => {
            const $e = $(e.currentTarget);
            $e.closest('.menu').find(`.m-group-${x.group}`).removeClass('m-checked');
            $e.addClass('m-checked');
            mFocus(null);
            x.action && x.action();
            return !1;
          });
      } else if (x.checkBoxPref) {
        x.checkBoxPref((v) => { $a.toggleClass('m-checked', !!v); });
        $a.toggleClass('m-checked', !!x.checkBoxPref())
          .on('mousedown mouseup click', (e) => {
            mFocus(null);
            x.action && x.action($(e.currentTarget).hasClass('m-checked'));
            return !1;
          });
      } else {
        x.action && $a.on('mousedown mouseup click', () => { mFocus(null); x.action(); return !1; });
      }
      if (!x.items) return $a;
      const $b = $('
');
      return $('
')
        .append($a.addClass('m-opener'), $b.append(...x.items.map(render)));
    };
    const leftRight = (d, $e) => { // d: +1 or -1, $e: target element
      if (d > 0 && $e.is('.m-opener')) {
        mFocus($e.next('.m-box').find('a').first());
      } else if (d < 0 && !$e.is('.m-opener') && $e.parents('.m-sub').length > 1) {
        mFocus($e.closest('.m-sub').find('.m-opener').first());
      } else {
        const $t = $m.children();
        const n = $t.length;
        const i = $e.parentsUntil('.menu').last().index();
        mFocus($t.eq((i + d + n) % n).find('a').eq(1));
      }
      return !1;
    };
    const upDown = (d, $e) => { // d: +1 or -1, $e: target element
      if ($e.is('.m-top')) {
        mFocus($e.parent().find(':not(hr)').eq(1));
      } else {
        const $s = $e.closest('.m-box').children(':not(hr)');
        const i = $s.index($e);
        const n = $s.length;
        const $f = $s.eq((i + d + n) % n);
        mFocus($f.is('a') ? $f : $f.find('a').first());
      }
      return !1;
    };
    $m = $('div[class=menu]');
    $m.length || ($m = $('
').prependTo('body'));
    $m.empty()
      .addClass('menu')
      .append(arg.map(render));
    $m.find('>.m-sub>.m-opener').addClass('m-top');
    $m.on('mouseover', 'a', (e) => {
      const t = e.currentTarget;
      $(t).closest('.menu').children().is('.m-open') && mFocus(t);
    });
    $m.on('mousedown click', 'a', (e) => {
      const t = e.currentTarget;
      mFocus($(t).parentsUntil('.menu').last().is('.m-open') && e.type === 'mousedown' ? null : t);
      return !1;
    });
    $m.keydown((e) => {
      switch (e.key) {
        case 'ArrowLeft': leftRight(-1, $(e.target)); break;
        case 'ArrowRight': leftRight(1, $(e.target)); break;
        case 'ArrowUp': upDown(-1, $(e.target)); break;
        case 'ArrowDown': upDown(1, $(e.target)); break;
        case 'Escape': case 'F10': mFocus(null); return !1;
        default:
      }
      return !0;
    });
    $(document).mousedown((e) => { $(e.target).closest('.menu').length || mFocus(null); });
    const updShcs = (h) => {
      const k = {};
      for (let i = 0; i < D.cmds.length; i++) {
        const [c,, d] = D.cmds[i];
        [k[c]] = (h[c] || d);
      } // c:code, d:defaults
      $('.m-shc').each((e) => {
        const $e = $(e.currentTarget);
        $e.text(k[$e.data('cmd')] || '');
      });
    };
    updShcs(D.prf.keys()); D.prf.keys(updShcs);
  }
};
<|endoftext|>


<commit_before>//# sourceURL=capturePlotButton.js

'use strict';

window.smartRApp.directive('capturePlotButton', [function() {

    return {
        restrict: 'E',
        scope: {
            disabled: '=?',
            filename: '@',
            target: '@'
        },
        template: '<button id="sr-capture-button" ng-disabled="disabled">Capture SVG</button>',
        link: function(scope, elements) {
            if (!scope.filename) {
                // default filename
                scope.filename = 'image.svg';
            }

            var template_btn = elements.children()[0];
            template_btn.addEventListener('click', function() {
                var svg = $(scope.target + ' svg');
                if (!svg.length) {
                    return;
                }

                var smartRSheets = [];
                for (var i = 0; i < document.styleSheets.length; i++) {
                    var sheet = document.styleSheets[i];
                    if (sheet.href && sheet.href.indexOf('smart-r') !== -1) {
                        smartRSheets.push(sheet);
                    }
                }

                var rules = [];
                smartRSheets.forEach(function(d) {
                    for (var key in d.cssRules) {
                        if (d.cssRules.hasOwnProperty(key) && d.cssRules[key] instanceof CSSStyleRule) {
                            rules.push(d.cssRules[key].cssText);
                        }
                    }
                });

                var defs = '<defs></defs>';
                svg.attr({version: '1.1' , xmlns:"http://www.w3.org/2000/svg"});
                svg.append(defs);
                $(scope.target + ' svg').wrap('

');
                var b64 = btoa(unescape(encodeURIComponent($('#sr-capture-container').html())));
                var tab = window.open();
                tab.document.write("Download SVG");
                $(scope.target + ' svg').unwrap();
                $(scope.target + ' svg defs').remove();
            });
        }
    };
}]);
<commit_msg>b64 to blob<commit_after>//# sourceURL=capturePlotButton.js

'use strict';

window.smartRApp.directive('capturePlotButton', [function() {

    return {
        restrict: 'E',
        scope: {
            disabled: '=?',
            filename: '@',
            target: '@'
        },
        template: '<button id="sr-capture-button" ng-disabled="disabled">Capture SVG</button>',
        link: function(scope, elements) {
            if (!scope.filename) {
                // default filename
                scope.filename = 'image.svg';
            }

            var template_btn = elements.children()[0];
            template_btn.addEventListener('click', function() {
                var svg = $(scope.target + ' svg');
                if (!svg.length) {
                    return;
                }

                var smartRSheets = [];
                for (var i = 0; i < document.styleSheets.length; i++) {
                    var sheet = document.styleSheets[i];
                    if (sheet.href && sheet.href.indexOf('smart-r') !== -1) {
                        smartRSheets.push(sheet);
                    }
                }

                var rules = [];
                smartRSheets.forEach(function(d) {
                    for (var key in d.cssRules) {
                        if (d.cssRules.hasOwnProperty(key) && d.cssRules[key] instanceof CSSStyleRule) {
                            rules.push(d.cssRules[key].cssText);
                        }
                    }
                });

                function b64toBlob(b64Data, contentType, sliceSize) {
                    contentType = contentType || '';
                    sliceSize = sliceSize || 512;

                    var byteCharacters = atob(b64Data);
                    var byteArrays = [];

                    for (var offset = 0; offset < byteCharacters.length; offset += sliceSize) {
                        var slice = byteCharacters.slice(offset, offset + sliceSize);

                        var byteNumbers = new Array(slice.length);
                        for (var i = 0; i < slice.length; i++) {
                            byteNumbers[i] = slice.charCodeAt(i);
                        }

                        var byteArray = new Uint8Array(byteNumbers);

                        byteArrays.push(byteArray);
                    }

                    var blob = new Blob(byteArrays, {type: contentType});
                    return blob;
                }

                var defs = '<defs></defs>';
                svg.attr({version: '1.1' , xmlns:"http://www.w3.org/2000/svg"});
                svg.append(defs);
                $(scope.target + ' svg').wrap('

');
                var b64 = btoa(unescape(encodeURIComponent($('#sr-capture-container').html())));
                var blob = b64toBlob(b64, 'image/svg+xml');
                var blobUrl = URL.createObjectURL(blob);
                var a = document.createElement('a');
                a.style = 'display: none';
                a.href = blobUrl;
                a.download = scope.filename;
                a.click();
                URL.revokeObjectURL(blobUrl);
                $(scope.target + ' svg').unwrap();
                $(scope.target + ' svg defs').remove();
            });
        }
    };
}]);
<|endoftext|>