<commit_before>/*
 * Author: Abdullah A Almsaeed
 * Date: 4 Jan 2014
 * Description:
 *      This is a demo file used only for the main dashboard (index.html)
 **/

$(function () {

  'use strict';

  // Make the dashboard widgets sortable Using jquery UI
  $('.connectedSortable').sortable({
    placeholder         : 'sort-highlight',
    connectWith         : '.connectedSortable',
    handle              : '.box-header, .nav-tabs',
    forcePlaceholderSize: true,
    zIndex              : 999999
  });
  $('.connectedSortable .box-header, .connectedSortable .nav-tabs-custom').css('cursor', 'move');

  // jQuery UI sortable for the todo list
  $('.todo-list').sortable({
    placeholder         : 'sort-highlight',
    handle              : '.handle',
    forcePlaceholderSize: true,
    zIndex              : 999999
  });

  // bootstrap WYSIHTML5 - text editor
  $('.textarea').wysihtml5();

  $('.daterange').daterangepicker({
    ranges   : {
      'Today'       : [moment(), moment()],
      'Yesterday'   : [moment().subtract(1, 'days'), moment().subtract(1, 'days')],
      'Last 7 Days' : [moment().subtract(6, 'days'), moment()],
      'Last 30 Days': [moment().subtract(29, 'days'), moment()],
      'This Month'  : [moment().startOf('month'), moment().endOf('month')],
      'Last Month'  : [moment().subtract(1, 'month').startOf('month'), moment().subtract(1, 'month').endOf('month')]
    },
    startDate: moment().subtract(29, 'days'),
    endDate  : moment()
  }, function (start, end) {
    window.alert('You chose: ' + start.format('MMMM D, YYYY') + ' - ' + end.format('MMMM D, YYYY'));
  });

  /* jQueryKnob */
  $('.knob').knob();

  // jvectormap data
  var visitorsData = {
    US: 398, // USA
    SA: 400, // Saudi Arabia
    CA: 1000, // Canada
    DE: 500, // Germany
    FR: 760, // France
    CN: 300, // China
    AU: 700, // Australia
    BR: 600, // Brazil
    IN: 800, // India
    GB: 320, // Great Britain
    RU: 3000 // Russia
  };
  // World map by jvectormap
  $('#world-map').vectorMap({
    map              : 'world_mill_en',
    backgroundColor  : 'transparent',
    regionStyle      : {
      initial: {
        fill            : '#e4e4e4',
        'fill-opacity'  : 1,
        stroke          : 'none',
        'stroke-width'  : 0,
        'stroke-opacity': 1
      }
    },
    series           : {
      regions: [
        {
          values           : visitorsData,
          scale            : ['#92c1dc', '#ebf4f9'],
          normalizeFunction: 'polynomial'
        }
      ]
    },
    onRegionLabelShow: function (e, el, code) {
      if (typeof visitorsData[code] != 'undefined')
        el.html(el.html() + ': ' + visitorsData[code] + ' new visitors');
    }
  });

  // Sparkline charts
  var myvalues = [1000, 1200, 920, 927, 931, 1027, 819, 930, 1021];
  $('#sparkline-1').sparkline(myvalues, {
    type     : 'line',
    lineColor: '#92c1dc',
    fillColor: '#ebf4f9',
    height   : '50',
    width    : '80'
  });
  myvalues = [515, 519, 520, 522, 652, 810, 370, 627, 319, 630, 921];
  $('#sparkline-2').sparkline(myvalues, {
    type     : 'line',
    lineColor: '#92c1dc',
    fillColor: '#ebf4f9',
    height   : '50',
    width    : '80'
  });
  myvalues = [15, 19, 20, 22, 33, 27, 31, 27, 19, 30, 21];
  $('#sparkline-3').sparkline(myvalues, {
    type     : 'line',
    lineColor: '#92c1dc',
    fillColor: '#ebf4f9',
    height   : '50',
    width    : '80'
  });

  // The Calender
  $('#calendar').datepicker();

  // SLIMSCROLL FOR CHAT WIDGET
  $('#chat-box').slimScroll({
    height: '250px'
  });

  /* Morris.js Charts */
  // Sales chart
  var area = new Morris.Area({
    element   : 'revenue-chart',
    resize    : true,
    data      : [
      { y: '2011 Q1', item1: 2666, item2: 2666 },
      { y: '2011 Q2', item1: 2778, item2: 2294 },
      { y: '2011 Q3', item1: 4912, item2: 1969 },
      { y: '2011 Q4', item1: 3767, item2: 3597 },
      { y: '2012 Q1', item1: 6810, item2: 1914 },
      { y: '2012 Q2', item1: 5670, item2: 4293 },
      { y: '2012 Q3', item1: 4820, item2: 3795 },
      { y: '2012 Q4', item1: 15073, item2: 5967 },
      { y: '2013 Q1', item1: 10687, item2: 4460 },
      { y: '2013 Q2', item1: 8432, item2: 5713 }
    ],
    xkey      : 'y',
    ykeys     : ['item1', 'item2'],
    labels    : ['Item 1', 'Item 2'],
    lineColors: ['#a0d0e0', '#3c8dbc'],
    hideHover : 'auto'
  });
  var line = new Morris.Line({
    element          : 'line-chart',
    resize           : true,
    data             : [
      { y: '2011 Q1', item1: 2666 },
      { y: '2011 Q2', item1: 2778 },
      { y: '2011 Q3', item1: 4912 },
      { y: '2011 Q4', item1: 3767 },
      { y: '2012 Q1', item1: 6810 },
      { y: '2012 Q2', item1: 5670 },
      { y: '2012 Q3', item1: 4820 },
      { y: '2012 Q4', item1: 15073 },
      { y: '2013 Q1', item1: 10687 },
      { y: '2013 Q2', item1: 8432 }
    ],
    xkey             : 'y',
    ykeys            : ['item1'],
    labels           : ['Item 1'],
    lineColors       : ['#efefef'],
    lineWidth        : 2,
    hideHover        : 'auto',
    gridTextColor    : '#fff',
    gridStrokeWidth  : 0.4,
    pointSize        : 4,
    pointStrokeColors: ['#efefef'],
    gridLineColor    : '#efefef',
    gridTextFamily   : 'Open Sans',
    gridTextSize     : 10
  });

  // Donut Chart
  var donut = new Morris.Donut({
    element  : 'sales-chart',
    resize   : true,
    colors   : ['#3c8dbc', '#f56954', '#00a65a'],
    data     : [
      { label: 'Download Sales', value: 12 },
      { label: 'In-Store Sales', value: 30 },
      { label: 'Mail-Order Sales', value: 20 }
    ],
    hideHover: 'auto'
  });

  // Fix for charts under tabs
  $('.box ul.nav a').on('shown.bs.tab', function () {
    area.redraw();
    donut.redraw();
    line.redraw();
  });

  /* The todo list plugin */
  $('.todo-list').todoList({
    onCheck  : function () {
      window.console.log($(this), 'The element has been checked');
    },
    onUnCheck: function () {
      window.console.log($(this), 'The element has been unchecked');
    }
  });

});
<commit_msg>Removed useless js code<commit_after>/*
 * Author: Abdullah A Almsaeed
 * Date: 4 Jan 2014
 * Description:
 *      This is a demo file used only for the main dashboard (index.html)
 **/

$(function () {

  'use strict';

  // Make the dashboard widgets sortable Using jquery UI
  $('.connectedSortable').sortable({
    placeholder         : 'sort-highlight',
    connectWith         : '.connectedSortable',
    handle              : '.box-header, .nav-tabs',
    forcePlaceholderSize: true,
    zIndex              : 999999
  });
  $('.connectedSortable .box-header, .connectedSortable .nav-tabs-custom').css('cursor', 'move');

  // jQuery UI sortable for the todo list
  $('.todo-list').sortable({
    placeholder         : 'sort-highlight',
    handle              : '.handle',
    forcePlaceholderSize: true,
    zIndex              : 999999
  });

  // bootstrap WYSIHTML5 - text editor
  $('.textarea').wysihtml5();

  $('.daterange').daterangepicker({
    ranges   : {
      'Today'       : [moment(), moment()],
      'Yesterday'   : [moment().subtract(1, 'days'), moment().subtract(1, 'days')],
      'Last 7 Days' : [moment().subtract(6, 'days'), moment()],
      'Last 30 Days': [moment().subtract(29, 'days'), moment()],
      'This Month'  : [moment().startOf('month'), moment().endOf('month')],
      'Last Month'  : [moment().subtract(1, 'month').startOf('month'), moment().subtract(1, 'month').endOf('month')]
    },
    startDate: moment().subtract(29, 'days'),
    endDate  : moment()
  }, function (start, end) {
    window.alert('You chose: ' + start.format('MMMM D, YYYY') + ' - ' + end.format('MMMM D, YYYY'));
  });

  /* jQueryKnob */
  $('.knob').knob();

  // jvectormap data
  var visitorsData = {
    US: 398, // USA
    SA: 400, // Saudi Arabia
    CA: 1000, // Canada
    DE: 500, // Germany
    FR: 760, // France
    CN: 300, // China
    AU: 700, // Australia
    BR: 600, // Brazil
    IN: 800, // India
    GB: 320, // Great Britain
    RU: 3000 // Russia
  };
  // World map by jvectormap
  $('#world-map').vectorMap({
    map              : 'world_mill_en',
    backgroundColor  : 'transparent',
    regionStyle      : {
      initial: {
        fill            : '#e4e4e4',
        'fill-opacity'  : 1,
        stroke          : 'none',
        'stroke-width'  : 0,
        'stroke-opacity': 1
      }
    },
    series           : {
      regions: [
        {
          values           : visitorsData,
          scale            : ['#92c1dc', '#ebf4f9'],
          normalizeFunction: 'polynomial'
        }
      ]
    },
    onRegionLabelShow: function (e, el, code) {
      if (typeof visitorsData[code] != 'undefined')
        el.html(el.html() + ': ' + visitorsData[code] + ' new visitors');
    }
  });

  // Sparkline charts
  var myvalues = [1000, 1200, 920, 927, 931, 1027, 819, 930, 1021];
  $('#sparkline-1').sparkline(myvalues, {
    type     : 'line',
    lineColor: '#92c1dc',
    fillColor: '#ebf4f9',
    height   : '50',
    width    : '80'
  });
  myvalues = [515, 519, 520, 522, 652, 810, 370, 627, 319, 630, 921];
  $('#sparkline-2').sparkline(myvalues, {
    type     : 'line',
    lineColor: '#92c1dc',
    fillColor: '#ebf4f9',
    height   : '50',
    width    : '80'
  });
  myvalues = [15, 19, 20, 22, 33, 27, 31, 27, 19, 30, 21];
  $('#sparkline-3').sparkline(myvalues, {
    type     : 'line',
    lineColor: '#92c1dc',
    fillColor: '#ebf4f9',
    height   : '50',
    width    : '80'
  });

  // The Calender
  $('#calendar').datepicker();

  // SLIMSCROLL FOR CHAT WIDGET
  $('#chat-box').slimScroll({
    height: '250px'
  });

  // Fix for charts under tabs
  $('.box ul.nav a').on('shown.bs.tab', function () {
    area.redraw();
    donut.redraw();
    line.redraw();
  });

  /* The todo list plugin */
  $('.todo-list').todoList({
    onCheck  : function () {
      window.console.log($(this), 'The element has been checked');
    },
    onUnCheck: function () {
      window.console.log($(this), 'The element has been unchecked');
    }
  });

});
<|endoftext|>


<commit_before>// Rich filebrowser configuration, initialization and main controller

var rich = rich || {};

rich.Browser = function(){

	this._options = {
		currentStyle: '',
		insertionModeMany: false,
		currentPage: 1,
		loading: false,
		reachedBottom: false,
		viewModeGrid: true
	};

};

rich.Browser.prototype = {

	initialize: function() {
		// intialize styles
		this.initStyles($.QueryString["allowed_styles"], $.QueryString["default_style"]);

		// initialize image insertion mode
		this._options.insertionModeMany = ($.QueryString["insert_many"]=="true")?true:false;
		this.toggleInsertionMode(false);
    	this.toggleViewMode(false);
	},

	initStyles: function(opt, def) {
		opt=opt.split(',');
		$.each(opt, function(index, value) {
			if(value != 'rich_thumb') $('#styles').append("
"+value+"
");
		});

		browser.selectStyle(def);

    //check if we are inserting an object
    var dom_id_param = $.QueryString["dom_id"];
    var split_field_name = dom_id_param ? dom_id_param.split('_') : null;

		if(opt.length < 2 || (split_field_name && split_field_name[split_field_name.length - 1] == "id")) {
			$('#styles').hide();
			browser.selectStyle(opt[0]);
		}
	},

	setLoading: function(loading) {
		this._options.loading = loading;

		if(loading == true) {
			// $('#loading').css({visibility: 'visible'});
			$('#loading').fadeIn();
		} else {
			$('#loading').fadeOut();
		}
	},

	selectStyle: function(name) {
		this._options.currentStyle = name;
		$('#styles li').removeClass('selected');
		$('#style-'+name).addClass('selected');
    },

	toggleInsertionMode: function(switchMode) {
		if(switchMode==true) this._options.insertionModeMany = !this._options.insertionModeMany;

		if(this._options.insertionModeMany == true) {
	    $('#insert-one').hide();
	    $('#insert-many').show();
	  } else {
	    $('#insert-one').show();
	    $('#insert-many').hide();
	  }
	},

    toggleViewMode: function(switchMode) {
      if(switchMode==true) {
        this._options.viewModeGrid = !this._options.viewModeGrid;
      } else {
        this._options.viewModeGrid = ($.QueryString["viewMode"]=="grid") ? false : true;
      }

      if(this._options.viewModeGrid == true) {
        $('#view-grid').hide();
        $('#view-list').show();
        $('#items').addClass('list');
      } else {
        $('#view-grid').show();
        $('#view-list').hide();
        $('#items').removeClass('list');
      }
    },

	selectItem: function(item) {
		var url = $(item).data('uris')[this._options.currentStyle];
		var id = $(item).data('rich-asset-id');
		var type = $(item).data('rich-asset-type');
		var name = $(item).data('rich-asset-name');

		if($.QueryString["CKEditor"]=='picker') {
			window.opener.assetPicker.setAsset($.QueryString["dom_id"], url, id, type);
		} else {
			window.opener.CKEDITOR.tools.callFunction($.QueryString["CKEditorFuncNum"], url, id, name);
		}

		// wait a short while before closing the window or regaining focus
		var self = this;
		window.setTimeout(function(){
			    if(self._options.insertionModeMany == false) {
			  window.close();
		  } else {
		    window.focus();
		  }
		},100);
	},

	loadNextPage: function() {
		if (this._options.loading || this._options.reachedBottom) {
      return;
    }

    if(this.nearBottomOfWindow()) {
			this.setLoading(true);
      this._options.currentPage++;

			var self = this;
      $.ajax({
        url: window.location.href + '&page=' + this._options.currentPage,
        type: 'get',
        dataType: 'script',
        success: function(e) {
					if(e=="") self._options.reachedBottom = true;
					self.setLoading(false);
        }
      });
    }
	},

	nearBottomOfWindow: function() {
		return $(window).scrollTop() > $(document).height() - $(window).height() - 100;
	}

};

var browser;

$(function(){

	browser = new rich.Browser();
	browser.initialize();

	new rich.Uploader();

	// hook up insert mode switching
	$('#insert-one, #insert-many').click(function(e){
		browser.toggleInsertionMode(true);
    e.preventDefault();
    return false;
  });

  // hook up insert view switching
  $('#view-grid, #view-list').click(function(e){
    browser.toggleViewMode(true);
    e.preventDefault();
    return false;
  });

	// hook up style selection
	$('#styles li').click(function(e){
		browser.selectStyle($(this).data('rich-style'));
	});

	// hook up item insertion
	$('#items li img').live('click', function(e){
		browser.selectItem(e.target);
	});

	// fluid pagination
	$(window).scroll(function(){
		browser.loadNextPage();
	});

});<commit_msg>Fixed obselete jQuery reference to live()<commit_after>// Rich filebrowser configuration, initialization and main controller

var rich = rich || {};

rich.Browser = function(){

	this._options = {
		currentStyle: '',
		insertionModeMany: false,
		currentPage: 1,
		loading: false,
		reachedBottom: false,
		viewModeGrid: true
	};

};

rich.Browser.prototype = {

	initialize: function() {
		// intialize styles
		this.initStyles($.QueryString["allowed_styles"], $.QueryString["default_style"]);

		// initialize image insertion mode
		this._options.insertionModeMany = ($.QueryString["insert_many"]=="true")?true:false;
		this.toggleInsertionMode(false);
    	this.toggleViewMode(false);
	},

	initStyles: function(opt, def) {
		opt=opt.split(',');
		$.each(opt, function(index, value) {
			if(value != 'rich_thumb') $('#styles').append("
"+value+"
");
		});

		browser.selectStyle(def);

    //check if we are inserting an object
    var dom_id_param = $.QueryString["dom_id"];
    var split_field_name = dom_id_param ? dom_id_param.split('_') : null;

		if(opt.length < 2 || (split_field_name && split_field_name[split_field_name.length - 1] == "id")) {
			$('#styles').hide();
			browser.selectStyle(opt[0]);
		}
	},

	setLoading: function(loading) {
		this._options.loading = loading;

		if(loading == true) {
			// $('#loading').css({visibility: 'visible'});
			$('#loading').fadeIn();
		} else {
			$('#loading').fadeOut();
		}
	},

	selectStyle: function(name) {
		this._options.currentStyle = name;
		$('#styles li').removeClass('selected');
		$('#style-'+name).addClass('selected');
    },

	toggleInsertionMode: function(switchMode) {
		if(switchMode==true) this._options.insertionModeMany = !this._options.insertionModeMany;

		if(this._options.insertionModeMany == true) {
	    $('#insert-one').hide();
	    $('#insert-many').show();
	  } else {
	    $('#insert-one').show();
	    $('#insert-many').hide();
	  }
	},

    toggleViewMode: function(switchMode) {
      if(switchMode==true) {
        this._options.viewModeGrid = !this._options.viewModeGrid;
      } else {
        this._options.viewModeGrid = ($.QueryString["viewMode"]=="grid") ? false : true;
      }

      if(this._options.viewModeGrid == true) {
        $('#view-grid').hide();
        $('#view-list').show();
        $('#items').addClass('list');
      } else {
        $('#view-grid').show();
        $('#view-list').hide();
        $('#items').removeClass('list');
      }
    },

	selectItem: function(item) {
		var url = $(item).data('uris')[this._options.currentStyle];
		var id = $(item).data('rich-asset-id');
		var type = $(item).data('rich-asset-type');
		var name = $(item).data('rich-asset-name');

		if($.QueryString["CKEditor"]=='picker') {
			window.opener.assetPicker.setAsset($.QueryString["dom_id"], url, id, type);
		} else {
			window.opener.CKEDITOR.tools.callFunction($.QueryString["CKEditorFuncNum"], url, id, name);
		}

		// wait a short while before closing the window or regaining focus
		var self = this;
		window.setTimeout(function(){
			    if(self._options.insertionModeMany == false) {
			  window.close();
		  } else {
		    window.focus();
		  }
		},100);
	},

	loadNextPage: function() {
		if (this._options.loading || this._options.reachedBottom) {
      return;
    }

    if(this.nearBottomOfWindow()) {
			this.setLoading(true);
      this._options.currentPage++;

			var self = this;
      $.ajax({
        url: window.location.href + '&page=' + this._options.currentPage,
        type: 'get',
        dataType: 'script',
        success: function(e) {
					if(e=="") self._options.reachedBottom = true;
					self.setLoading(false);
        }
      });
    }
	},

	nearBottomOfWindow: function() {
		return $(window).scrollTop() > $(document).height() - $(window).height() - 100;
	}

};

var browser;

$(function(){

	browser = new rich.Browser();
	browser.initialize();

	new rich.Uploader();

	// hook up insert mode switching
	$('#insert-one, #insert-many').click(function(e){
		browser.toggleInsertionMode(true);
    e.preventDefault();
    return false;
  });

  // hook up insert view switching
  $('#view-grid, #view-list').click(function(e){
    browser.toggleViewMode(true);
    e.preventDefault();
    return false;
  });

	// hook up style selection
	$('#styles li').click(function(e){
		browser.selectStyle($(this).data('rich-style'));
	});

	// hook up item insertion
	$('body').on('click', '#items li img', function(e){
		browser.selectItem(e.target);
	});

	// fluid pagination
	$(window).scroll(function(){
		browser.loadNextPage();
	});

});<|endoftext|>


<commit_before>import * as util from '../util.js';
import SizedMap from '../SizedMap.js';
import shorteners from './shorteners.js';

// Keep a local cache of videos, since some collections link to host sites,
// but will often not contain enough metainfo of the videos, such
// as the thumbnail, views, even the user.
//
// Note that this is different from ajax cache.
const cachedVideos = new SizedMap(200, 'cachedVideos');

// Keep an in memory cache in case there are errors retrieving videos
// in the future. If that happens, it will use the last successful result
// from that source.
const cachedResults = { videos: {}, collections: {} };

import youtube from './videos/youtube.js';
import twitch from './videos/twitch.js';
import haloruns from './collections/haloruns.js';
import speedrundotcom from './collections/speedrundotcom.js';

const sources = {
  // Sources which directly host videos.
  videos: {
    youtube,
    twitch,
  },

  // Sources that link to other websites' videos.
  collections: {
    haloruns,
    speedrundotcom,
  },

  getVideos: async (options) => {
    const fetchEnabledSources = async (type, isHost) => {
      return await Promise.all(Object.keys(sources[type])
        .filter(source => options[source])
        .map(async (source) => {
          let fn = sources[type][source];
          if (isHost) {
            fn = fn.getAllVideos;
          }
          let videos;
          try {
            videos = await fn();
            for (let video of videos) {
              // Add links to video description.
              if (video.desc) {
                video.desc = util.embedLinks(video.desc);
              }
            }
            if (!isHost) {
              videos = await util.parallelFilter(videos, sources.addMetaToVideo);
            }
            cachedResults[type][source] = videos;

          } catch (err) {
            console.warn(`Failed to get videos for ${source}: ${err.message}`);
            console.error(err);

            // Fallback to cached videos from this source if getting videos fails.
            videos = cachedResults[type][source] || [];
          }

          return { source, videos };
        }));
    };

    const [sourceResults, colResults] = await Promise.all([
      fetchEnabledSources('videos', true),
      fetchEnabledSources('collections', false)
    ]);

    // First, collect videos directly from where they're hosted,
    // in case any of them are included in collection sites.
    // And if they are, that's one less API request we have to make to
    // get that video's metadata.
    const videos = [].concat(...sourceResults.map((result) => {
      result.videos.forEach((video, i) => {
        video.source = result.source;
        video.index = i;
      });
      return result.videos;
    }));
    const videosMap = new Map();
    videos.forEach((video) => {
      videosMap.set(video.url, video);
      cachedVideos.push(video.url, video, true);
    });

    const colVideos = [].concat(...colResults.map((result) => {
      result.videos.forEach((video, i) => {
        const col = video.col || {};
        delete video.col;
        col.source = result.source;
        video.collections = [col];
        video.index = i;
      });
      return result.videos;
    }));

    // If anything from a collection site is already in the list of
    // videos directly gathered from a hosting site, then merge them.
    // But give preference to the site hosting the video, in case
    // the user is following those channels.
    // That way, direct channel subscriptions get priority.
    colVideos.forEach((colVideo) => {
      const video = videosMap.get(colVideo.url);
      if (video) {
        // Keep a reference of the collection title for filtering,
        // since the video will have its own title.
        colVideo.collections[0].title = colVideo.title;
        if (video.collections) {
          // It's possible that the same video could be posted in
          // many collectioin sites.
          video.collections.push(colVideo.collections[0]);
        } else {
          video.collections = colVideo.collections.slice();
        }
        video.desc = video.desc || colVideo.desc;
        video.game = video.game || colVideo.game;
      } else {
        colVideo.source = sources.sourceFromURL(colVideo.url);
        videosMap.set(colVideo.url, colVideo);
      }
    });

    return Array.from(videosMap.values());
  },

  getMetaForVideo: async (url) => {
    if (shorteners.isShortened(url)) {
      const realurl = await shorteners.getRealURL(url);
      if (realurl) {
        return sources.getMetaForVideo(realurl);
      }

    } else if (cachedVideos.has(url)) {
      return cachedVideos.get(url);

    } else {
      const source = sources.sourceFromURL(url);
      if (source) {
        return sources.videos[source].getVideo(url);
      } else {
        console.warn('Could not find source for URL: ' + url);
      }
    }
  },

  addMetaToVideo: async (video) => {
    let meta;
    try {
      meta = await sources.getMetaForVideo(video.url);
      if (!meta) { return false; }
    } catch (err) {
      console.warn('Failed to get meta for video: ' + video.url);
      console.error(err);
      return false;
    }
    video.url = meta.url;
    video.thumbnail = meta.thumbnail;
    video.length = meta.length;

    // Views and title can update later, so don't include these when getting
    // metainfo from cache.
    ['game', 'title', 'views', 'user'].forEach((field) => {
      if (meta[field] && !video[field]) {
        video[field] = meta[field];
      }
    });

    return true;
  },

  sourceFromURL: (url) => {
    for (const source in sources.videos) {
      if (sources.videos[source]._patterns.some(p => p.test(url))) {
        return source;
      }
    }
    return null;
  },

  isVideoPage: url => !!sources.sourceFromURL(url),
};

sources.patterns = [];
for (let [, videos] of Object.entries(sources.videos)) {
  videos._patterns = videos.patterns.map(util.minimatch);

  // Gather all video page patterns for context menus.
  sources.patterns = sources.patterns.concat(videos.patterns);
}

export default sources;
<commit_msg>fix(sources): use source video cache when collections have same video<commit_after>import * as util from '../util.js';
import SizedMap from '../SizedMap.js';
import shorteners from './shorteners.js';

// Keep a local cache of videos, since some collections link to host sites,
// but will often not contain enough metainfo of the videos, such
// as the thumbnail, views, even the user.
//
// Note that this is different from ajax cache.
const cachedVideos = new SizedMap(200, 'cachedVideos');

// Keep an in memory cache in case there are errors retrieving videos
// in the future. If that happens, it will use the last successful result
// from that source.
const cachedResults = { videos: {}, collections: {} };

import youtube from './videos/youtube.js';
import twitch from './videos/twitch.js';
import haloruns from './collections/haloruns.js';
import speedrundotcom from './collections/speedrundotcom.js';

const sources = {
  // Sources which directly host videos.
  videos: {
    youtube,
    twitch,
  },

  // Sources that link to other websites' videos.
  collections: {
    haloruns,
    speedrundotcom,
  },

  getVideos: async (options) => {
    const fetchEnabledSources = async (type, isHost) => {
      return Promise.all(Object.keys(sources[type])
        .filter(source => options[source])
        .map(async (source) => {
          let fn = sources[type][source];
          if (isHost) {
            fn = fn.getAllVideos;
          }
          let videos, success = false;
          try {
            videos = await fn();
            for (let video of videos) {
              // Add links to video description.
              if (video.desc) {
                video.desc = util.embedLinks(video.desc);
              }
            }
            cachedResults[type][source] = videos;
            success = true;

          } catch (err) {
            console.warn(`Failed to get videos for ${source}: ${err.message}`);
            console.error(err);

            // Fallback to cached videos from this source if fetching videos fails.
            videos = cachedResults[type][source] || [];
          }

          return { source, videos, success };
        }));
    };

    const [sourceResults, colResults] = await Promise.all([
      fetchEnabledSources('videos', true),
      fetchEnabledSources('collections', false)
    ]);

    // First, collect videos directly from where they're hosted,
    // in case any of them are included in collection sites.
    // And if they are, that's one less API request we have to make to
    // get that video's metadata.
    const videos = [].concat(...sourceResults.map((result) => {
      result.videos.forEach((video, i) => {
        video.source = result.source;
        video.index = i;
      });
      return result.videos;
    }));

    const videosMap = new Map();
    videos.forEach((video) => {
      videosMap.set(video.url, video);
      cachedVideos.push(video.url, video, true);
    });

    await Promise.all(colResults.map(async ({videos, source, success}) => {
      if (success) {
        videos = await util.parallelFilter(videos, sources.addMetaToVideo);
      }
      videos.forEach((colVideo, i) => {
        const col = colVideo.col || {};
        delete colVideo.col;
        col.source = source;
        colVideo.collections = [col];
        colVideo.index = i;

        // If anything from a collection site is already in the list of
        // videos directly gathered from a hosting site, then merge them.
        // But give preference to the site hosting the video, in case
        // the user is following those channels.
        // That way, direct channel subscriptions get priority.
        const video = videosMap.get(colVideo.url);
        if (video) {
          // Keep a reference of the collection title for filtering,
          // since the video will have its own title.
          colVideo.collections[0].title = colVideo.title;
          if (video.collections) {
            // It's possible that the same video could be posted in
            // many collectioin sites.
            video.collections.push(colVideo.collections[0]);
          } else {
            video.collections = colVideo.collections.slice();
          }
          video.desc = video.desc || colVideo.desc;
          video.game = video.game || colVideo.game;
        } else {
          colVideo.source = sources.sourceFromURL(colVideo.url);
          videosMap.set(colVideo.url, colVideo);
        }
      });
    }));

    return Array.from(videosMap.values());
  },

  getMetaForVideo: async (url) => {
    if (shorteners.isShortened(url)) {
      const realurl = await shorteners.getRealURL(url);
      if (realurl) {
        return sources.getMetaForVideo(realurl);
      }

    } else if (cachedVideos.has(url)) {
      return cachedVideos.get(url);

    } else {
      const source = sources.sourceFromURL(url);
      if (source) {
        return sources.videos[source].getVideo(url);
      } else {
        console.warn('Could not find source for URL: ' + url);
      }
    }
  },

  addMetaToVideo: async (video) => {
    let meta;
    try {
      meta = await sources.getMetaForVideo(video.url);
      if (!meta) { return false; }
    } catch (err) {
      console.warn('Failed to get meta for video: ' + video.url);
      console.error(err);
      return false;
    }
    video.url = meta.url;
    video.thumbnail = meta.thumbnail;
    video.length = meta.length;

    // Views and title can update later, so don't include these when getting
    // metainfo from cache.
    ['game', 'title', 'views', 'user'].forEach((field) => {
      if (meta[field] && !video[field]) {
        video[field] = meta[field];
      }
    });

    return true;
  },

  sourceFromURL: (url) => {
    for (const source in sources.videos) {
      if (sources.videos[source]._patterns.some(p => p.test(url))) {
        return source;
      }
    }
    return null;
  },

  isVideoPage: url => !!sources.sourceFromURL(url),
};

sources.patterns = [];
for (let [, videos] of Object.entries(sources.videos)) {
  videos._patterns = videos.patterns.map(util.minimatch);

  // Gather all video page patterns for context menus.
  sources.patterns = sources.patterns.concat(videos.patterns);
}

export default sources;
<|endoftext|>


<commit_before>import React from 'react'
import Edgenote from './Edgenote.js'
import {Link} from 'react-router'
import gatherEdgenotes from '../gatherEdgenotes.js';
import {I18n} from './I18n.js'

function isElementInViewport (el) {

  //special bonus for those using jQuery
  if (typeof jQuery === "function" && el instanceof jQuery) {
    el = el[0];
  }

  var rect = el.getBoundingClientRect();

  return (
    rect.top >= 0 &&
      rect.left >= 0 &&
        rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) && /*or $(window).height() */
        rect.right <= (window.innerWidth || document.documentElement.clientWidth) /*or $(window).width() */
  );
}

class Narrative extends React.Component {
  componentDidUpdate() {
    let top = document.getElementById('top');
    if (top && window.innerWidth < 749) {top.scrollIntoView()}
  }

  nextLink() {
    let nextChapterID = this.props.selectedPage + 2
    if (nextChapterID - 1 < this.props.pages.length) {
      return <Link className="nextLink" to={`/${nextChapterID}`}><I18n meaning="next" /> {this.props.pages[nextChapterID - 1].title}</Link>
    } else {
      return
<I18n meaning="end" />

    }
  }

  render() {
    let i = this.props.selectedPage
    if (this.props.pages.length === 0) {
      return

    }
    let page = this.props.pages[i]
    return (

        <Page
          page={page}
          handleEdit={this.props.handleEdit}
        />
        {this.nextLink()}

    )
  }
}

export default Narrative

class Page extends React.Component {
  render() {
    let {page} = this.props
    let cards = page.cards.map( (card, i) => {
      return <Card id={i} key={`c${i}`}
                   handleEdit={this.props.handleEdit}
                   selectedPage={page.position}
                   solid={card.solid}
                   card={card} />
    } )

    return (

{page.title}

        {cards}

    )
  }
}

String.prototype.addAttributeToLinks = function (attribute) {
  return this.replace(/(data-edgenote=\"([a-zA-Z0-9-]+)\")/g, '$1 ' + attribute)
}

String.prototype.addAttributeToLinksPointingToEdgenoteID = function (id, attribute) {
  return this.replace(new RegExp('(data-edgenote=\"'+id+'\")', 'g'), '$1 ' + attribute)
}

String.prototype.addHREF = function(firstPartOfPath) {
  return this.addAttributeToLinks(`href=\"#${firstPartOfPath}/edgenotes/$2\"`)
}

class Card extends React.Component {
  constructor() {
    super()
    this.state = {
      selectedEdgenote: null,
      edgenoteSlugs: []
    }
  }

  addHoverCallbacksToParagraphText(paragraph) {
    let mouseover = 'onmouseover=\'window.handleHover'+this.props.id+'(\"$2\")\''
    let mouseout = 'onmouseout=\'window.handleHover'+this.props.id+'(null)\''
    return { __html: paragraph.addAttributeToLinks(mouseover).addAttributeToLinks(mouseout)
      .addHREF(`/${this.props.selectedPage + 1}`) }
  }

  setEdgenotes(contents) {
    let edgenoteSlugs = gatherEdgenotes(contents)
    this.setState({edgenoteSlugs: edgenoteSlugs})
  }

  componentWillMount() {
    window["handleHover"+this.props.id] = (slug) => {
      this.setState({selectedEdgenote: slug})
    }
  }

  componentDidMount() {
    this.setEdgenotes(this.props.card.content)
  }

  componentWillReceiveProps(nextProps) {
    if (this.props.selectedPage !== nextProps.selectedPage) {
      this.setState({ edgenoteSlugs: [] })
      this.setEdgenotes(nextProps.card.content)
    }
  }

  renderEdgenotes() {
    let aside
    if (this.state.edgenoteSlugs.length != 0) {
      aside =
                {
                  this.state.edgenoteSlugs.map( (slug) => {
                    return (
                      <Edgenote
                        pathPrefix={`/${this.props.selectedPage}`}
                        selectedEdgenote={this.state.selectedEdgenote}
                        slug={slug}
                        key={`edgenote_${slug}`}
                        handleHoverID={this.props.id}
                      />
                      )
                    } )
                }

    }
    return aside
  }

  prepareSave() {}

  render() {
    //let paragraph = this.props.contents.addAttributeToLinksPointingToEdgenoteID(this.state.selected_id, 'class="focus"')
    let paragraph = this.props.card.content
    return (

        {this.renderEdgenotes()}

    )
  }
}
<commit_msg>Edgenote links from in Narrative don't advance section.<commit_after>import React from 'react'
import Edgenote from './Edgenote.js'
import {Link} from 'react-router'
import gatherEdgenotes from '../gatherEdgenotes.js';
import {I18n} from './I18n.js'

function isElementInViewport (el) {

  //special bonus for those using jQuery
  if (typeof jQuery === "function" && el instanceof jQuery) {
    el = el[0];
  }

  var rect = el.getBoundingClientRect();

  return (
    rect.top >= 0 &&
      rect.left >= 0 &&
        rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) && /*or $(window).height() */
        rect.right <= (window.innerWidth || document.documentElement.clientWidth) /*or $(window).width() */
  );
}

class Narrative extends React.Component {
  componentDidUpdate() {
    let top = document.getElementById('top');
    if (top && window.innerWidth < 749) {top.scrollIntoView()}
  }

  nextLink() {
    let nextChapterID = this.props.selectedPage + 2
    if (nextChapterID - 1 < this.props.pages.length) {
      return <Link className="nextLink" to={`/${nextChapterID}`}><I18n meaning="next" /> {this.props.pages[nextChapterID - 1].title}</Link>
    } else {
      return
<I18n meaning="end" />

    }
  }

  render() {
    let i = this.props.selectedPage
    if (this.props.pages.length === 0) {
      return

    }
    let page = this.props.pages[i]
    return (

        <Page
          page={page}
          handleEdit={this.props.handleEdit}
        />
        {this.nextLink()}

    )
  }
}

export default Narrative

class Page extends React.Component {
  render() {
    let {page} = this.props
    let cards = page.cards.map( (card, i) => {
      return <Card id={i} key={`c${i}`}
                   handleEdit={this.props.handleEdit}
                   selectedPage={page.position}
                   solid={card.solid}
                   card={card} />
    } )

    return (

{page.title}

        {cards}

    )
  }
}

String.prototype.addAttributeToLinks = function (attribute) {
  return this.replace(/(data-edgenote=\"([a-zA-Z0-9-]+)\")/g, '$1 ' + attribute)
}

String.prototype.addAttributeToLinksPointingToEdgenoteID = function (id, attribute) {
  return this.replace(new RegExp('(data-edgenote=\"'+id+'\")', 'g'), '$1 ' + attribute)
}

String.prototype.addHREF = function(firstPartOfPath) {
  return this.addAttributeToLinks(`href=\"#${firstPartOfPath}/edgenotes/$2\"`)
}

class Card extends React.Component {
  constructor() {
    super()
    this.state = {
      selectedEdgenote: null,
      edgenoteSlugs: []
    }
  }

  addHoverCallbacksToParagraphText(paragraph) {
    let mouseover = 'onmouseover=\'window.handleHover'+this.props.id+'(\"$2\")\''
    let mouseout = 'onmouseout=\'window.handleHover'+this.props.id+'(null)\''
    return { __html: paragraph.addAttributeToLinks(mouseover).addAttributeToLinks(mouseout)
      .addHREF(`/${this.props.selectedPage}`) }
  }

  setEdgenotes(contents) {
    let edgenoteSlugs = gatherEdgenotes(contents)
    this.setState({edgenoteSlugs: edgenoteSlugs})
  }

  componentWillMount() {
    window["handleHover"+this.props.id] = (slug) => {
      this.setState({selectedEdgenote: slug})
    }
  }

  componentDidMount() {
    this.setEdgenotes(this.props.card.content)
  }

  componentWillReceiveProps(nextProps) {
    if (this.props.selectedPage !== nextProps.selectedPage) {
      this.setState({ edgenoteSlugs: [] })
      this.setEdgenotes(nextProps.card.content)
    }
  }

  renderEdgenotes() {
    let aside
    if (this.state.edgenoteSlugs.length != 0) {
      aside =
                {
                  this.state.edgenoteSlugs.map( (slug) => {
                    return (
                      <Edgenote
                        pathPrefix={`/${this.props.selectedPage}`}
                        selectedEdgenote={this.state.selectedEdgenote}
                        slug={slug}
                        key={`edgenote_${slug}`}
                        handleHoverID={this.props.id}
                      />
                      )
                    } )
                }

    }
    return aside
  }

  prepareSave() {}

  render() {
    //let paragraph = this.props.contents.addAttributeToLinksPointingToEdgenoteID(this.state.selected_id, 'class="focus"')
    let paragraph = this.props.card.content
    return (

        {this.renderEdgenotes()}

    )
  }
}
<|endoftext|>


<commit_before>'use strict';

importScripts('/workbox.js');
const workboxSW = new WorkboxSW({
  clientsClaim: true,
  skipWaiting: true
});

// Static precaching of images
workboxSW.precache([]);

// Register main route for all navigation links to pages
workboxSW.router.registerNavigationRoute('index.html', {
  whitelist: [/./],
  blacklist: [/img\/.*/, /\.(js|css)/]
});

// Append a query param to  requests for index.html.  This may replace the previous registernavigationroute
/*
workboxSW.router.registerRoute('???', ({url, event, params}) => {
  return fetch(addUrlParam(url, 'pwa=true'));
});
*/

// Cache external libraries and fonts
workboxSW.router.registerRoute('https://cdn.ampproject.org/(.*)', workboxSW.strategies.staleWhileRevalidate());
workboxSW.router.registerRoute('https://cdn.polyfill.io/(.*)', workboxSW.strategies.staleWhileRevalidate());
workboxSW.router.registerRoute('https://pasteup.guim.co.uk/fonts/(.*)', workboxSW.strategies.cacheFirst());

// Cache a number of YQL queries, but only for the offline scenario
workboxSW.router.registerRoute(
  'https://query.yahooapis.com/v1/public/(.*)',
  workboxSW.strategies.networkFirst()
);

// Cache a number of images
workboxSW.router.registerRoute(
  'https://i.guim.co.uk/img/(.*)',
  workboxSW.strategies.cacheFirst({
    cacheName: 'images',
    cacheExpiration: {
      maxEntries: 10,
      maxAgeSeconds: 7 * 24 * 60 * 60
    },
    cacheableResponse: {statuses: [ 0, 200 ]}
  })
);

// Make sure new versions of the Service Worker activate immediately
self.addEventListener('install', () => {
  self.skipWaiting();
});

function addUrlParam(url, param) {
  url += (param.indexOf('?') === -1) ? '?' : '&';
  return url + param;
}
<commit_msg>Removed potential new routing.  We didn't need it!<commit_after>'use strict';

importScripts('/workbox.js');
const workboxSW = new WorkboxSW({
  clientsClaim: true,
  skipWaiting: true
});

// Static precaching of images
workboxSW.precache([]);

// Register main route for all navigation links to pages
workboxSW.router.registerNavigationRoute('index.html', {
  whitelist: [/./],
  blacklist: [/img\/.*/, /\.(js|css)/]
});

// Cache external libraries and fonts
workboxSW.router.registerRoute('https://cdn.ampproject.org/(.*)', workboxSW.strategies.staleWhileRevalidate());
workboxSW.router.registerRoute('https://cdn.polyfill.io/(.*)', workboxSW.strategies.staleWhileRevalidate());
workboxSW.router.registerRoute('https://pasteup.guim.co.uk/fonts/(.*)', workboxSW.strategies.cacheFirst());

// Cache a number of YQL queries, but only for the offline scenario
workboxSW.router.registerRoute(
  'https://query.yahooapis.com/v1/public/(.*)',
  workboxSW.strategies.networkFirst()
);

// Cache a number of images
workboxSW.router.registerRoute(
  'https://i.guim.co.uk/img/(.*)',
  workboxSW.strategies.cacheFirst({
    cacheName: 'images',
    cacheExpiration: {
      maxEntries: 10,
      maxAgeSeconds: 7 * 24 * 60 * 60
    },
    cacheableResponse: {statuses: [ 0, 200 ]}
  })
);

// Make sure new versions of the Service Worker activate immediately
self.addEventListener('install', () => {
  self.skipWaiting();
});

function addUrlParam(url, param) {
  url += (param.indexOf('?') === -1) ? '?' : '&';
  return url + param;
}
<|endoftext|>


<commit_before>// $ Dashbot
// $ Authors: john
// $ Created on: Tue Apr 12 17:09:46 BRT 2016
// - Test: This is a sample command description that will be parsed
// and shown to users when they request help. Make sure to document
// every command your module provides.

var Base = require('../src/base_module');

var Dashbot = function(bot) {
    Base.call(this, bot);
    var Posts = this.registerModel('Posts', {
        title: String,
        description: String,
        image: String,
        due: Date
    });
    this.exposeApi('get', '/posts', (req, res) => {
        res.set('Content-Type', 'text/html');
        Posts.find({
            "due": {
                $gte: new Date()
            }
        }, (err, result) => {
            var list = '';
            result.forEach((item) => {
                list = list + `<li style="background-image: url('${item.image}');" title="${item.title}" subtitle="${item.description}"></li>`;
            });
            res.send(`<!DOCTYPE html>  <title>D3 | Dashboard</title> <link rel="stylesheet" href="http://d3estudio.com.br/dashboard/assets/css/style.css" type="text/css"/> <link rel="stylesheet" href="http://d3estudio.com.br/dashboard/assets/css/jquery.bxslider.css" type="text/css"/>
 ${list}
  `);
        });
    })
    this.respond(/(?:posta|poste) no dashbot o t(?:í|i)tulo (.*), com a descri(?:ç|c)(?:ã|a)o (.*), com a dura(?:ç|c)(?:ã|a)o (.*), e a imagem (.*)$/i, (response) => {
        var title = response.match[1],
            description = response.match[2],
            duration = response.match[3],
            image = response.match[4].replace('<', '').replace('>', '');
        if (duration === 'infinito' || duration === 'infinita') {
            duration = 9999;
        } else {
            duration = parseInt(duration);
        }
        if (isNaN(duration) || duration == 0) {
            response.reply('Não vai ser possível publicar com a `duração` que você informou :face_with_rolling_eyes:. Me manda novamente!');
        } else {
            var due = new Date();
            due.setDate(due.getDate() + duration);
            Posts.create({
                title: title,
                description: description,
                image: image,
                due: due
            }, (err, post) => {
                if (err) {
                    response.reply('Deu algo errado, tenta de novo? :fearful:');
                } else {
                    response.reply('Maravilha, está publicado! :metal:```Título: ' + title + '\nDescrição: ' + description + '\nImagem: ' + image + '\nPublicado Até: ' + due.toISOString().slice(0, 10) + '```');
                }
            });
        }
    });
};

Base.setup(Dashbot, 'Dashbot');
module.exports = Dashbot;
<commit_msg>correction for help<commit_after>// $ Dashbot
// $ Authors: john
// $ Created on: Tue Apr 12 17:09:46 BRT 2016
// - posta no dashbot o titulo [titulo], com a descricao [descricao], com a duracao [duracao], e a imagem [url_da_imagem]
// - Onde subir imagems: https://imgsafe.org/

var Base = require('../src/base_module');

var Dashbot = function(bot) {
    Base.call(this, bot);
    var Posts = this.registerModel('Posts', {
        title: String,
        description: String,
        image: String,
        due: Date
    });
    this.exposeApi('get', '/posts', (req, res) => {
        res.set('Content-Type', 'text/html');
        Posts.find({
            "due": {
                $gte: new Date()
            }
        }, (err, result) => {
            var list = '';
            result.forEach((item) => {
                list = list + `<li style="background-image: url('${item.image}');" title="${item.title}" subtitle="${item.description}"></li>`;
            });
            res.send(`<!DOCTYPE html>  <title>D3 | Dashboard</title> <link rel="stylesheet" href="http://d3estudio.com.br/dashboard/assets/css/style.css" type="text/css"/> <link rel="stylesheet" href="http://d3estudio.com.br/dashboard/assets/css/jquery.bxslider.css" type="text/css"/>
 ${list}
  `);
        });
    })
    this.respond(/(?:posta|poste) no dashbot o t(?:í|i)tulo (.*), com a descri(?:ç|c)(?:ã|a)o (.*), com a dura(?:ç|c)(?:ã|a)o (.*), e a imagem (.*)$/i, (response) => {
        var title = response.match[1],
            description = response.match[2],
            duration = response.match[3],
            image = response.match[4].replace('<', '').replace('>', '');
        if (duration === 'infinito' || duration === 'infinita') {
            duration = 9999;
        } else {
            duration = parseInt(duration);
        }
        if (isNaN(duration) || duration == 0) {
            response.reply('Não vai ser possível publicar com a `duração` que você informou :face_with_rolling_eyes:. Me manda novamente!');
        } else {
            var due = new Date();
            due.setDate(due.getDate() + duration);
            Posts.create({
                title: title,
                description: description,
                image: image,
                due: due
            }, (err, post) => {
                if (err) {
                    response.reply('Deu algo errado, tenta de novo? :fearful:');
                } else {
                    response.reply('Maravilha, está publicado! :metal:```Título: ' + title + '\nDescrição: ' + description + '\nImagem: ' + image + '\nPublicado Até: ' + due.toISOString().slice(0, 10) + '```');
                }
            });
        }
    });
};

Base.setup(Dashbot, 'Dashbot');
module.exports = Dashbot;
<|endoftext|>


<commit_before>const date = new Date(Date.UTC(2020, 11, 20, 3, 23, 16, 738));
// Results below assume UTC timezone - your results may vary

// Specify default date formatting for language (locale)
console.log(new Intl.DateTimeFormat('en-US').format(date));
// expected output: "12/20/2020"

// Specify default date formatting for language with a fallback language (in this case Indonesian)
console.log(new Intl.DateTimeFormat(['ban', 'id']).format(date));
// expected output: "20/12/2020"

// Specify date and time format using "style" options (i.e. full, long, medium, short)
console.log(new Intl.DateTimeFormat('en-GB', { dateStyle:'full', timeStyle: 'long' }).format(date));
// Expected output "Sunday, 20 December 2020 at 14:23:16 GMT+11"
<commit_msg>Fix minor layout issue<commit_after>const date = new Date(Date.UTC(2020, 11, 20, 3, 23, 16, 738));
// Results below assume UTC timezone - your results may vary

// Specify default date formatting for language (locale)
console.log(new Intl.DateTimeFormat('en-US').format(date));
// expected output: "12/20/2020"

// Specify default date formatting for language with a fallback language (in this case Indonesian)
console.log(new Intl.DateTimeFormat(['ban', 'id']).format(date));
// expected output: "20/12/2020"

// Specify date and time format using "style" options (i.e. full, long, medium, short)
console.log(new Intl.DateTimeFormat('en-GB', { dateStyle: 'full', timeStyle: 'long' }).format(date));
// Expected output "Sunday, 20 December 2020 at 14:23:16 GMT+11"
<|endoftext|>


<commit_before>/**
 * Returns a score for the password
 *
 * @author Hendrik Rust
 * @param pass Password for evaluation
 * @return score The score of the password
 */
function scorePassword(pass) {

  	var score = 0;
  	if (!pass) return score;
  	var letters = new Object();

	for (var i=0; i<pass.length; i++) {

		letters[pass[i]] = (letters[pass[i]] || 0) + 1;
      		score += 5.0 / letters[pass[i]];

	}

  	var variations = {

		digits: /\d/.test(pass),
      		lower: /[a-z]/.test(pass),
      		upper: /[A-Z]/.test(pass),
      		nonWords: /\W/.test(pass),

	}

  	variationCount = 0;
  	for (var check in variations) variationCount += (variations[check] == true) ? 1 : 0;
  	score += (variationCount - 1) * 10;

  	return parseInt(score);

}

/**
 * Checks a password's strength based on what score it achieved
 *
 * @author Hendrik Rust
 * @param pass Password for evaluation
 * @return html The strength of the password, formatted in HTML
 */
function checkPasswordStrength(pass) {

  	var score = scorePassword(pass);
  	if (score > 80) return "Strong password";
  	if (score > 60) return "Average password";
  	if (score >= 30) return "Weak password";
	return "";

}

/**
 * Checks if a password is strong, and then returns a corresponding boolean
 *
 * @author the_DJDJ
 * @param pass Password for evaluation
 * @return boolean A boolean representing whether or not the password is strong
 */
function pass_isPasswordStrong(pass) {

	var score = scorePassword(pass);

	if (score > 80) return true;
	return false;

}

/**
 * Capitalises the first letter in an array
 *
 * @author the_DJDJ
 * @param pass The array that needs capitalisation
 * @return array The array, with only the first letter capitalised
 */
function pass_capitaliseFirstLetter(pass){

	return pass[0].toUpperCase() + pass.slice(1).toLowerCase();

}

/**
 * Filters out all special and non-alphanumeric characters so that only letters and numbers remain
 *
 * @author the_DJDJ
 * @param data The array of letters to be filtered
 * @return out An array of the filtered characters
 */
function pass_keepLetters(data){

	var out = [];

    	for (var i = 0; i < data.length; i++) if(data[i] != "") out[i] = data[i].replace(/[^a-zA-Z0-9]+/g, "");
    	return out;

}

/**
 * Chooses a strong password based on an array. Also checks for whether or not there are enough words to form a strong password
 *
 * @author the_DJDJ
 * @param data The array of letters from which a password can be selected
 */
function pass_selectWords(data) {

    	var words = pass_keepLetters(data);
    	var usedWords = [];
    	var password = "";
    	var minimumLength = 4; //can be changed

    	var rand = [];
    	var password = "";
    	var count = 0;

    	while (!pass_isPasswordStrong(password)) {

        	rand = words[Math.floor(Math.random() * words.length)];

        	if(usedWords.indexOf(rand) == -1 && rand.length >= minimumLength){

        		password += pass_capitaliseFirstLetter(rand);
        		usedWords.push(rand);

        	}

        	if(count == words.length) pass_generate();
        	count++;

	}

	$("#suggested_password").html("Random password generated for you:
"+password+"");

}

/**
 * Starts the password generation process. Is only it's own method to allow for id='suggested_password' to have two non-password values
 *
 * @author the_DJDJ
 */
function pass_start(){

	$("#suggested_password").html('Random password generated for you:
  Loading...');
	pass_generate();

}

/**
 * Generates a random URL at which to find the resources that are required to generate a password
 *
 * @author the_DJDJ
 */
function pass_generate(){

	var sections = [//"arts",
			"business",
			"dining",
			//"fashion",
			"garden",
			//"health",
			//"magazine",
			//"national",
			//"nyregion",
			//"opinion",
			"politics",
			"realestate",
			"science",
			"sports",
			"technology",
			"travel",
			//"world"
			];

	var types =    ["mostemailed",
			"mostshared",
			"mostviewed"];

	var time =     ["1",
			"7",
			"30"];

	var prefix = 'https:';
    	if (window.parent.document.location.protocol != 'https:') prefix = 'http:';

	var key = "c20545b4dc42cab4d83a7ace81ec9ac2:0:69814665";
	var address = prefix + "//api.nytimes.com/svc/mostpopular/v2/" +
		types[Math.floor(Math.random() * types.length)] + "/" +		//randomly select one of the different types
		sections[Math.floor(Math.random() * sections.length)] + "/" +	//randomly select one of the different sections
		time[Math.floor(Math.random() * time.length)] + 		//randomly select the time period (day, week, month)
		".jsonp?api-key=" + key + "&callback=pass_getWords";			//wrap using JSONP and getWords() method

	$.ajax({

		url:		address,
		cache:		false,
		crossOrigin:	true,
		dataType:	'jsonp',
		success:	function(data){

			pass_getWords(data, address);

		}

	});

}

/**
 * Converts JSON data into an array of words that can be used to generate the password. Also checks for blank documents
 *
 * @author the_DJDJ
 * @param data The JSON data that needs to be converted
 * @param address The URL at which the data was found, in case that the data is blank
 */
function pass_getWords(data, address){

	var results = data.results;

	if(results.length == 0) pass_generate();
    	else pass_selectWords(results[Math.floor(Math.random() * data.results.length)].abstract.split(' '));

}
<commit_msg>Added a generate new password function<commit_after>/**
 * Returns a score for the password
 *
 * @author Hendrik Rust
 * @param pass Password for evaluation
 * @return score The score of the password
 */
function scorePassword(pass) {

  	var score = 0;
  	if (!pass) return score;
  	var letters = new Object();

	for (var i=0; i<pass.length; i++) {

		letters[pass[i]] = (letters[pass[i]] || 0) + 1;
      		score += 5.0 / letters[pass[i]];

	}

  	var variations = {

		digits: /\d/.test(pass),
      		lower: /[a-z]/.test(pass),
      		upper: /[A-Z]/.test(pass),
      		nonWords: /\W/.test(pass),

	}

  	variationCount = 0;
  	for (var check in variations) variationCount += (variations[check] == true) ? 1 : 0;
  	score += (variationCount - 1) * 10;

  	return parseInt(score);

}

/**
 * Checks a password's strength based on what score it achieved
 *
 * @author Hendrik Rust
 * @param pass Password for evaluation
 * @return html The strength of the password, formatted in HTML
 */
function checkPasswordStrength(pass) {

  	var score = scorePassword(pass);
  	if (score > 80) return "Strong password";
  	if (score > 60) return "Average password";
  	if (score >= 30) return "Weak password";
	return "";

}

/**
 * Checks if a password is strong, and then returns a corresponding boolean
 *
 * @author the_DJDJ
 * @param pass Password for evaluation
 * @return boolean A boolean representing whether or not the password is strong
 */
function pass_isPasswordStrong(pass) {

	var score = scorePassword(pass);

	if (score > 80) return true;
	return false;

}

/**
 * Capitalises the first letter in an array
 *
 * @author the_DJDJ
 * @param pass The array that needs capitalisation
 * @return array The array, with only the first letter capitalised
 */
function pass_capitaliseFirstLetter(pass){

	return pass[0].toUpperCase() + pass.slice(1).toLowerCase();

}

/**
 * Filters out all special and non-alphanumeric characters so that only letters and numbers remain
 *
 * @author the_DJDJ
 * @param data The array of letters to be filtered
 * @return out An array of the filtered characters
 */
function pass_keepLetters(data){

	var out = [];

    	for (var i = 0; i < data.length; i++) if(data[i] != "") out[i] = data[i].replace(/[^a-zA-Z0-9]+/g, "");
    	return out;

}

/**
 * Chooses a strong password based on an array. Also checks for whether or not there are enough words to form a strong password
 *
 * @author the_DJDJ
 * @param data The array of letters from which a password can be selected
 */
function pass_selectWords(data) {

    	var words = pass_keepLetters(data);
    	var usedWords = [];
    	var password = "";
    	var minimumLength = 4; //can be changed

    	var rand = [];
    	var password = "";
    	var count = 0;

    	while (!pass_isPasswordStrong(password)) {

        	rand = words[Math.floor(Math.random() * words.length)];

        	if(usedWords.indexOf(rand) == -1 && rand.length >= minimumLength){

        		password += pass_capitaliseFirstLetter(rand);
        		usedWords.push(rand);

        	}

        	if(count == words.length) pass_generate();
        	count++;

	}

	$("#suggested_password").html("Random password generated for you:
"+password+"
Test
");

}

/**
 * Starts the password generation process. Is only it's own method to allow for id='suggested_password' to have two non-password values
 *
 * @author the_DJDJ
 */
function pass_start(){

	$("#suggested_password").html('Random password generated for you:
  Loading...');

	pass_generate();

}

/**
 * Generates a random URL at which to find the resources that are required to generate a password
 *
 * @author the_DJDJ
 */
function pass_generate(){

	var sections = [//"arts",
			"automobiles",
			//"books",
			"business",
			"dining",
			"education",
			//"fashion",
			"garden",
			//"health",
			//"magazine",
			//"movies",
			//"national",
			//"nyregion",
			"obituaries",
			//"opinion",
			"politics",
			"realestate",
			"science",
			"sports",
			"technology",
			"theater",
			"travel",
			//"world"
			];

	var types =    ["mostemailed",
			"mostshared",
			"mostviewed"];

	var time =     ["1",
			"7",
			"30"];

	var prefix = 'https:';
    	if (window.parent.document.location.protocol != 'https:') prefix = 'http:';

	var key = "c20545b4dc42cab4d83a7ace81ec9ac2:0:69814665";
	var address = prefix + "//api.nytimes.com/svc/mostpopular/v2/" +
		types[Math.floor(Math.random() * types.length)] + "/" +		//randomly select one of the different types
		sections[Math.floor(Math.random() * sections.length)] + "/" +	//randomly select one of the different sections
		time[Math.floor(Math.random() * time.length)] + 		//randomly select the time period (day, week, month)
		".jsonp?api-key=" + key + "&callback=pass_getWords";		//wrap using JSONP and getWords() method

	$.ajax({

		url:		address,
		cache:		false,
		crossOrigin:	true,
		dataType:	'jsonp',
		success:	function(data){

			pass_getWords(data, address);

		}

	});

}

/**
 * Converts JSON data into an array of words that can be used to generate the password. Also checks for blank documents
 *
 * @author the_DJDJ
 * @param data The JSON data that needs to be converted
 * @param address The URL at which the data was found, in case that the data is blank
 */
function pass_getWords(data, address){

	var results = data.results;

	if(results.length == 0) pass_generate();
    	else pass_selectWords(results[Math.floor(Math.random() * data.results.length)].abstract.split(' '));

}
<|endoftext|>


<commit_before>/**
 * Generate Markdown from your Javadoc, PHPDoc or JSDoc comments
 *
 * Usage: Create a new instance of JavadocToMarkdown and then
 * call either fromJavadoc(), fromPHPDoc() or fromJSDoc()
 *
 * @constructor
 */
var JavadocToMarkdown = function () {

    "use strict";

    var self = this;

    /**
     * Generates Markdown documentation from code on a more abstract level
     *
     * @param {string} code the code that contains doc comments
     * @param {number} headingsLevel the headings level to use as the base (1-6)
     * @param {function} fnAddTagsMarkdown the function that processes doc tags and generates the Markdown documentation
     * @returns {string} the Markdown documentation
     */
    function fromDoc(code, headingsLevel, fnAddTagsMarkdown) {
        var i,
            out,
            sections;

        // get all documentation sections from code
        sections = getSections(code);
        // initialize a string buffer
        out = [];

        out.push("#".repeat(headingsLevel)+" Documentation");

        for (i = 0; i < sections.length; i++) {
            out.push(fromSection(sections[i], headingsLevel, fnAddTagsMarkdown));
        }

        // return the contents of the string buffer
        return out.join("");
    }

    /**
     * Generates Markdown documentation from a statically typed language's doc comments
     *
     * @param {string} code the code that contains doc comments
     * @param {number} headingsLevel the headings level to use as the base (1-6)
     * @returns {string} the Markdown documentation
     */
    this.fromStaticTypesDoc = function(code, headingsLevel) {
        return fromDoc(code, headingsLevel, function(tag, assocBuffer) {
            var tokens;
            switch (tag.key) {
                case "abstract": addToBuffer(assocBuffer, "Abstract", tag.value); break;
                case "access": addToBuffer(assocBuffer, "Access", tag.value); break;
                case "author": addToBuffer(assocBuffer, "Author", tag.value); break;
                case "constructor": addToBuffer(assocBuffer, "Constructor", null); break;
                case "copyright": addToBuffer(assocBuffer, "Copyright", tag.value); break;
                case "deprec":
                case "deprecated": addToBuffer(assocBuffer, "Deprecated", null); break;
                case "example": addToBuffer(assocBuffer, "Example", tag.value); break;
                case "exception":
                case "throws":
                    tokens = tag.value.tokenize(/\s+/g, 2);
                    addToBuffer(assocBuffer, "Exceptions", "`"+tokens[0]+"` — "+tokens[1]);
                    break;
                case "exports": addToBuffer(assocBuffer, "Exports", tag.value); break;
                case "license": addToBuffer(assocBuffer, "License", tag.value); break;
                case "link": addToBuffer(assocBuffer, "Link", tag.value); break;
                case "name": addToBuffer(assocBuffer, "Alias", tag.value); break;
                case "package": addToBuffer(assocBuffer, "Package", tag.value); break;
                case "param":
                    tokens = tag.value.tokenize(/\s+/g, 2);
                    addToBuffer(assocBuffer, "Parameters", "`"+tokens[0]+"` — "+tokens[1]);
                    break;
                case "private": addToBuffer(assocBuffer, "Private", null); break;
                case "return":
                case "returns": addToBuffer(assocBuffer, "Returns", tag.value); break;
                case "see": addToBuffer(assocBuffer, "See also", tag.value); break;
                case "since": addToBuffer(assocBuffer, "Since", tag.value); break;
                case "static": addToBuffer(assocBuffer, "Static", tag.value); break;
                case "subpackage": addToBuffer(assocBuffer, "Sub-package", tag.value); break;
                case "this": addToBuffer(assocBuffer, "This", "`"+tag.value+"`"); break;
                case "todo": addToBuffer(assocBuffer, "To-do", tag.value); break;
                case "version": addToBuffer(assocBuffer, "Version", tag.value); break;
                default: break;
            }
        });
    };

    /**
     * Generates Markdown documentation from a dynamically typed language's doc comments
     *
     * @param {string} code the code that contains doc comments
     * @param {number} headingsLevel the headings level to use as the base (1-6)
     * @param {function} fnFormatType the function that formats a type information (single argument)
     * @param {function} fnFormatTypeAndName the function that formats type and name information (two arguments)
     * @returns {string} the Markdown documentation
     */
    this.fromDynamicTypesDoc = function(code, headingsLevel, fnFormatType, fnFormatTypeAndName) {
        return fromDoc(code, headingsLevel, function(tag, assocBuffer) {
            var tokens;
            switch (tag.key) {
                case "abstract": addToBuffer(assocBuffer, "Abstract", tag.value); break;
                case "access": addToBuffer(assocBuffer, "Access", tag.value); break;
                case "author": addToBuffer(assocBuffer, "Author", tag.value); break;
                case "constructor": addToBuffer(assocBuffer, "Constructor", null); break;
                case "copyright": addToBuffer(assocBuffer, "Copyright", tag.value); break;
                case "deprec":
                case "deprecated": addToBuffer(assocBuffer, "Deprecated", null); break;
                case "example": addToBuffer(assocBuffer, "Example", tag.value); break;
                case "exception":
                case "throws":
                    tokens = tag.value.tokenize(/\s+/g, 2);
                    addToBuffer(assocBuffer, "Exceptions", fnFormatType(tokens[0])+" — "+tokens[1]);
                    break;
                case "exports": addToBuffer(assocBuffer, "Exports", tag.value); break;
                case "license": addToBuffer(assocBuffer, "License", tag.value); break;
                case "link": addToBuffer(assocBuffer, "Link", tag.value); break;
                case "name": addToBuffer(assocBuffer, "Alias", tag.value); break;
                case "package": addToBuffer(assocBuffer, "Package", tag.value); break;
                case "param":
                    tokens = tag.value.tokenize(/\s+/g, 3);
                    addToBuffer(assocBuffer, "Parameters", fnFormatTypeAndName(tokens[0], tokens[1])+" — "+tokens[2]);
                    break;
                case "private": addToBuffer(assocBuffer, "Private", null); break;
                case "return":
                case "returns":
                    tokens = tag.value.tokenize(/\s+/g, 2);
                    addToBuffer(assocBuffer, "Returns", fnFormatType(tokens[0])+" — "+tokens[1]);
                    break;
                case "see": addToBuffer(assocBuffer, "See also", tag.value); break;
                case "since": addToBuffer(assocBuffer, "Since", tag.value); break;
                case "static": addToBuffer(assocBuffer, "Static", tag.value); break;
                case "subpackage": addToBuffer(assocBuffer, "Sub-package", tag.value); break;
                case "this": addToBuffer(assocBuffer, "This", "`"+tag.value+"`"); break;
                case "todo": addToBuffer(assocBuffer, "To-do", tag.value); break;
                case "var":
                    tokens = tag.value.tokenize(/\s+/g, 2);
                    addToBuffer(assocBuffer, "Type", fnFormatType(tokens[0])+" — "+tokens[1]);
                    break;
                case "version": addToBuffer(assocBuffer, "Version", tag.value); break;
                default: break;
            }
        });
    };

    /**
     * Generates Markdown documentation from Javadoc comments
     *
     * @param {string} code the code that contains doc comments
     * @param {number} headingsLevel the headings level to use as the base (1-6)
     * @returns {string} the Markdown documentation
     */
    this.fromJavadoc = function(code, headingsLevel) {
        return self.fromStaticTypesDoc(code, headingsLevel);
    };

    /**
     * Generates Markdown documentation from PHPDoc comments
     *
     * @param {string} code the code that contains doc comments
     * @param {number} headingsLevel the headings level to use as the base (1-6)
     * @returns {string} the Markdown documentation
     */
    this.fromPHPDoc = function(code, headingsLevel) {
        return self.fromDynamicTypesDoc(
            code,
            headingsLevel,
            function (type) {
                return "`"+type+"`";
            },
            function (type, name) {
                // if we have a valid name (and type)
                if (/^\$([a-zA-Z0-9_$]+)$/.test(name)) {
                    return "`"+name+"` — `"+type+"`";
                }
                // if it seems we only have a name
                else {
                    // return the name that was, wrongly, in the position of the type
                    return "`"+type+"`";
                }
            }
        );
    };

    /**
     * Generates Markdown documentation from JSDoc comments
     *
     * @param {string} code the code that contains doc comments
     * @param {number} headingsLevel the headings level to use as the base (1-6)
     * @returns {string} the Markdown documentation
     */
    this.fromJSDoc = function(code, headingsLevel) {
        return self.fromDynamicTypesDoc(
            code,
            headingsLevel,
            function (type) {
                return "`"+type.substr(1, type.length-2)+"`";
            },
            function (type, name) {
                // if we have a valid type (and name)
                if (/^\{([^{}]+)\}$/.test(type)) {
                    return "`"+name+"` — `"+type.substr(1, type.length-2)+"`";
                }
                // if it seems we only have a name
                else {
                    // return the name that was, wrongly, in the position of the type
                    return "`"+type+"`";
                }
            }
        );
    };

    /**
     * Generates Markdown documentation from a given section
     *
     * The function processes units of documentation, a line of code with accompanying doc comment
     *
     * @param {object} section the section that consists of code line and doc comment
     * @param {number} headingsLevel the headings level to use as the base (1-6)
     * @param {function} fnAddTagsMarkdown the function that processes doc tags and generates the Markdown documentation
     * @returns {string} the Markdown documentation
     */
    function fromSection(section, headingsLevel, fnAddTagsMarkdown) {
        var assocBuffer,
            description,
            field,
            out,
            p,
            t,
            tags;

        // initialize a string buffer
        out = [];

        // first get the field that we want to describe
        field = getFieldDeclaration(section.line);
        // if there is no field to describe
        if (!field) {
            // do not return any documentation
            return "";
        }

        out.push("\n\n");
        out.push("#".repeat(headingsLevel+1)+" `"+field+"`");

        description = getDocDescription(section.doc);
        if (description.length) {
            out.push("\n\n");
            out.push(description);
        }

        tags = getDocTags(section.doc);
        if (tags.length) {
            out.push("\n");

            assocBuffer = {};
            for (t = 0; t < tags.length; t++) {
                fnAddTagsMarkdown(tags[t], assocBuffer);
            }

            for (p in assocBuffer) {
                if (assocBuffer.hasOwnProperty(p)) {
                    out.push(fromTagGroup(p, assocBuffer[p]));
                }
            }
        }

        // return the contents of the string buffer
        return out.join("");
    }

    function fromTagGroup(name, entries) {
        var i,
            out;

        // initialize a string buffer
        out = [];

        out.push("\n");
        if (entries.length === 1 && entries[0] === null) {
            out.push(" * **"+name+"**");
        }
        else {
            out.push(" * **"+name+":**");
            if (entries.length > 1) {
                for (i = 0; i < entries.length; i++) {
                    out.push("\n");
                    out.push("   * "+entries[i]);
                }
            }
            else if (entries.length === 1) {
                out.push(" "+entries[0]);
            }
        }

        // return the contents of the string buffer
        return out.join("");
    }

    function getSections(code) {
        var docLine,
            fieldDeclaration,
            m,
            out,
            regex;

        regex = /\/\*\*([^]*?)\*\/([^{;/]+)/gm;
        out = [];

        while ((m = regex.exec(code)) !== null) {
            if (m.index === regex.lastIndex) {
                regex.lastIndex++;
            }

            if (typeof m[1] === "string" && m[1] !== null) {
                if (typeof m[2] === "string" && m[2] !== null) {
                    fieldDeclaration = m[2].trim();
                    docLine = m[1];

                    // if this is a single line comment
                    if (docLine.indexOf("*") === -1) {
                        // prepend an asterisk to achieve the normal line structure
                        docLine = "*"+docLine;
                    }

                    // interpret empty lines as if they contained a p-tag
                    docLine = docLine.replace(/\*[ ]*$/gm, "*
");

                    out.push({ "line": fieldDeclaration, "doc": docLine });
                }
            }
        }

        return out;
    }

    function getFieldDeclaration(line) {
        var regex = /^([^\{;]+)(.*?)$/gm;
        var m;

        while ((m = regex.exec(line)) !== null) {
            if (m.index === regex.lastIndex) {
                regex.lastIndex++;
            }

            if (typeof m[1] === "string" && m[1] !== null) {
                return cleanSingleLine(m[1]);
            }
        }

        return "";
    }

    function getDocDescription(docLines) {
        var regex = /^(\t| )*?\*(\t| )+([^@].*)$/gm;
        var m;
        var out = [];

        while ((m = regex.exec(docLines)) !== null) {
            if (m.index === regex.lastIndex) {
                regex.lastIndex++;
            }

            if (typeof m[3] === "string" && m[3] !== null) {
                out.push(cleanLine(m[3]));
            }
        }

        return cleanLine(out.join(" ").replace(/<(\/)?p>/gi, "\n\n"));
    }

    function getDocTags(docLines) {
        var regex = /^(\t| )*?\*(\t| )*?@([a-zA-Z]+)(.*)$/gm;
        var m;
        var out = [];

        while ((m = regex.exec(docLines)) !== null) {
            if (m.index === regex.lastIndex) {
                regex.lastIndex++;
            }

            if (typeof m[3] === "string" && m[3] !== null) {
                if (typeof m[4] === "string" && m[4] !== null) {
                    out.push({ "key": cleanSingleLine(m[3]), "value": cleanSingleLine(m[4]) });
                }
            }
        }

        return out;
    }

    function cleanLine(line) {
        // trim leading and trailing spaces
        line = line.trim();

        // clear spaces after line breaks and tabs
        line = line.replace(/([\n\r\t])[ ]+/gm, "$1");

        // make consecutive spaces one
        line = line.replace(/[ ]{2,}/g, " ");

        return line;
    }

    function cleanSingleLine(line) {
        // perform normal line cleaning
        line = cleanLine(line);

        // replace line breaks and tabs with spaces
        line = line.replace(/(\n|\r|\t)/g, " ");

        return line;
    }

    function addToBuffer(buffer, key, value) {
        if (typeof buffer[key] === "undefined" || buffer[key] === null) {
            buffer[key] = [];
        }
        buffer[key].push(value);
    }

    String.prototype.tokenize = function(splitByRegex, limit) {
        var counter,
            i,
            m,
            start,
            tokens;

        tokens = [];
        counter = 1;
        start = 0;

        while ((m = splitByRegex.exec(this)) !== null) {
            if (m.index === splitByRegex.lastIndex) {
                splitByRegex.lastIndex++;
            }

            if (counter < limit) {
                tokens.push(this.substring(start, m.index));
                start = m.index + m[0].length;
            }

            counter++;
        }

        // add the remainder as a single part
        tokens.push(this.substring(start));

        // fill the array to match the limit if necessary
        for (i = tokens.length; i < limit; i++) {
            tokens.push("");
        }

        return tokens;
    };

    String.prototype.repeat = function(count) {
        return new Array(count + 1).join(this);
    };

};
<commit_msg>Ignore Java import statements<commit_after>/**
 * Generate Markdown from your Javadoc, PHPDoc or JSDoc comments
 *
 * Usage: Create a new instance of JavadocToMarkdown and then
 * call either fromJavadoc(), fromPHPDoc() or fromJSDoc()
 *
 * @constructor
 */
var JavadocToMarkdown = function () {

    "use strict";

    var self = this;

    /**
     * Generates Markdown documentation from code on a more abstract level
     *
     * @param {string} code the code that contains doc comments
     * @param {number} headingsLevel the headings level to use as the base (1-6)
     * @param {function} fnAddTagsMarkdown the function that processes doc tags and generates the Markdown documentation
     * @returns {string} the Markdown documentation
     */
    function fromDoc(code, headingsLevel, fnAddTagsMarkdown) {
        var i,
            out,
            sections;

        // get all documentation sections from code
        sections = getSections(code);
        // initialize a string buffer
        out = [];

        out.push("#".repeat(headingsLevel)+" Documentation");

        for (i = 0; i < sections.length; i++) {
            out.push(fromSection(sections[i], headingsLevel, fnAddTagsMarkdown));
        }

        // return the contents of the string buffer
        return out.join("");
    }

    /**
     * Generates Markdown documentation from a statically typed language's doc comments
     *
     * @param {string} code the code that contains doc comments
     * @param {number} headingsLevel the headings level to use as the base (1-6)
     * @returns {string} the Markdown documentation
     */
    this.fromStaticTypesDoc = function(code, headingsLevel) {
        return fromDoc(code, headingsLevel, function(tag, assocBuffer) {
            var tokens;
            switch (tag.key) {
                case "abstract": addToBuffer(assocBuffer, "Abstract", tag.value); break;
                case "access": addToBuffer(assocBuffer, "Access", tag.value); break;
                case "author": addToBuffer(assocBuffer, "Author", tag.value); break;
                case "constructor": addToBuffer(assocBuffer, "Constructor", null); break;
                case "copyright": addToBuffer(assocBuffer, "Copyright", tag.value); break;
                case "deprec":
                case "deprecated": addToBuffer(assocBuffer, "Deprecated", null); break;
                case "example": addToBuffer(assocBuffer, "Example", tag.value); break;
                case "exception":
                case "throws":
                    tokens = tag.value.tokenize(/\s+/g, 2);
                    addToBuffer(assocBuffer, "Exceptions", "`"+tokens[0]+"` — "+tokens[1]);
                    break;
                case "exports": addToBuffer(assocBuffer, "Exports", tag.value); break;
                case "license": addToBuffer(assocBuffer, "License", tag.value); break;
                case "link": addToBuffer(assocBuffer, "Link", tag.value); break;
                case "name": addToBuffer(assocBuffer, "Alias", tag.value); break;
                case "package": addToBuffer(assocBuffer, "Package", tag.value); break;
                case "param":
                    tokens = tag.value.tokenize(/\s+/g, 2);
                    addToBuffer(assocBuffer, "Parameters", "`"+tokens[0]+"` — "+tokens[1]);
                    break;
                case "private": addToBuffer(assocBuffer, "Private", null); break;
                case "return":
                case "returns": addToBuffer(assocBuffer, "Returns", tag.value); break;
                case "see": addToBuffer(assocBuffer, "See also", tag.value); break;
                case "since": addToBuffer(assocBuffer, "Since", tag.value); break;
                case "static": addToBuffer(assocBuffer, "Static", tag.value); break;
                case "subpackage": addToBuffer(assocBuffer, "Sub-package", tag.value); break;
                case "this": addToBuffer(assocBuffer, "This", "`"+tag.value+"`"); break;
                case "todo": addToBuffer(assocBuffer, "To-do", tag.value); break;
                case "version": addToBuffer(assocBuffer, "Version", tag.value); break;
                default: break;
            }
        });
    };

    /**
     * Generates Markdown documentation from a dynamically typed language's doc comments
     *
     * @param {string} code the code that contains doc comments
     * @param {number} headingsLevel the headings level to use as the base (1-6)
     * @param {function} fnFormatType the function that formats a type information (single argument)
     * @param {function} fnFormatTypeAndName the function that formats type and name information (two arguments)
     * @returns {string} the Markdown documentation
     */
    this.fromDynamicTypesDoc = function(code, headingsLevel, fnFormatType, fnFormatTypeAndName) {
        return fromDoc(code, headingsLevel, function(tag, assocBuffer) {
            var tokens;
            switch (tag.key) {
                case "abstract": addToBuffer(assocBuffer, "Abstract", tag.value); break;
                case "access": addToBuffer(assocBuffer, "Access", tag.value); break;
                case "author": addToBuffer(assocBuffer, "Author", tag.value); break;
                case "constructor": addToBuffer(assocBuffer, "Constructor", null); break;
                case "copyright": addToBuffer(assocBuffer, "Copyright", tag.value); break;
                case "deprec":
                case "deprecated": addToBuffer(assocBuffer, "Deprecated", null); break;
                case "example": addToBuffer(assocBuffer, "Example", tag.value); break;
                case "exception":
                case "throws":
                    tokens = tag.value.tokenize(/\s+/g, 2);
                    addToBuffer(assocBuffer, "Exceptions", fnFormatType(tokens[0])+" — "+tokens[1]);
                    break;
                case "exports": addToBuffer(assocBuffer, "Exports", tag.value); break;
                case "license": addToBuffer(assocBuffer, "License", tag.value); break;
                case "link": addToBuffer(assocBuffer, "Link", tag.value); break;
                case "name": addToBuffer(assocBuffer, "Alias", tag.value); break;
                case "package": addToBuffer(assocBuffer, "Package", tag.value); break;
                case "param":
                    tokens = tag.value.tokenize(/\s+/g, 3);
                    addToBuffer(assocBuffer, "Parameters", fnFormatTypeAndName(tokens[0], tokens[1])+" — "+tokens[2]);
                    break;
                case "private": addToBuffer(assocBuffer, "Private", null); break;
                case "return":
                case "returns":
                    tokens = tag.value.tokenize(/\s+/g, 2);
                    addToBuffer(assocBuffer, "Returns", fnFormatType(tokens[0])+" — "+tokens[1]);
                    break;
                case "see": addToBuffer(assocBuffer, "See also", tag.value); break;
                case "since": addToBuffer(assocBuffer, "Since", tag.value); break;
                case "static": addToBuffer(assocBuffer, "Static", tag.value); break;
                case "subpackage": addToBuffer(assocBuffer, "Sub-package", tag.value); break;
                case "this": addToBuffer(assocBuffer, "This", "`"+tag.value+"`"); break;
                case "todo": addToBuffer(assocBuffer, "To-do", tag.value); break;
                case "var":
                    tokens = tag.value.tokenize(/\s+/g, 2);
                    addToBuffer(assocBuffer, "Type", fnFormatType(tokens[0])+" — "+tokens[1]);
                    break;
                case "version": addToBuffer(assocBuffer, "Version", tag.value); break;
                default: break;
            }
        });
    };

    /**
     * Generates Markdown documentation from Javadoc comments
     *
     * @param {string} code the code that contains doc comments
     * @param {number} headingsLevel the headings level to use as the base (1-6)
     * @returns {string} the Markdown documentation
     */
    this.fromJavadoc = function(code, headingsLevel) {
        return self.fromStaticTypesDoc(code, headingsLevel);
    };

    /**
     * Generates Markdown documentation from PHPDoc comments
     *
     * @param {string} code the code that contains doc comments
     * @param {number} headingsLevel the headings level to use as the base (1-6)
     * @returns {string} the Markdown documentation
     */
    this.fromPHPDoc = function(code, headingsLevel) {
        return self.fromDynamicTypesDoc(
            code,
            headingsLevel,
            function (type) {
                return "`"+type+"`";
            },
            function (type, name) {
                // if we have a valid name (and type)
                if (/^\$([a-zA-Z0-9_$]+)$/.test(name)) {
                    return "`"+name+"` — `"+type+"`";
                }
                // if it seems we only have a name
                else {
                    // return the name that was, wrongly, in the position of the type
                    return "`"+type+"`";
                }
            }
        );
    };

    /**
     * Generates Markdown documentation from JSDoc comments
     *
     * @param {string} code the code that contains doc comments
     * @param {number} headingsLevel the headings level to use as the base (1-6)
     * @returns {string} the Markdown documentation
     */
    this.fromJSDoc = function(code, headingsLevel) {
        return self.fromDynamicTypesDoc(
            code,
            headingsLevel,
            function (type) {
                return "`"+type.substr(1, type.length-2)+"`";
            },
            function (type, name) {
                // if we have a valid type (and name)
                if (/^\{([^{}]+)\}$/.test(type)) {
                    return "`"+name+"` — `"+type.substr(1, type.length-2)+"`";
                }
                // if it seems we only have a name
                else {
                    // return the name that was, wrongly, in the position of the type
                    return "`"+type+"`";
                }
            }
        );
    };

    /**
     * Generates Markdown documentation from a given section
     *
     * The function processes units of documentation, a line of code with accompanying doc comment
     *
     * @param {object} section the section that consists of code line and doc comment
     * @param {number} headingsLevel the headings level to use as the base (1-6)
     * @param {function} fnAddTagsMarkdown the function that processes doc tags and generates the Markdown documentation
     * @returns {string} the Markdown documentation
     */
    function fromSection(section, headingsLevel, fnAddTagsMarkdown) {
        var assocBuffer,
            description,
            field,
            out,
            p,
            t,
            tags;

        // initialize a string buffer
        out = [];

        // first get the field that we want to describe
        field = getFieldDeclaration(section.line);
        // if there is no field to describe
        if (!field) {
            // do not return any documentation
            return "";
        }

        out.push("\n\n");
        out.push("#".repeat(headingsLevel+1)+" `"+field+"`");

        description = getDocDescription(section.doc);
        if (description.length) {
            out.push("\n\n");
            out.push(description);
        }

        tags = getDocTags(section.doc);
        if (tags.length) {
            out.push("\n");

            assocBuffer = {};
            for (t = 0; t < tags.length; t++) {
                fnAddTagsMarkdown(tags[t], assocBuffer);
            }

            for (p in assocBuffer) {
                if (assocBuffer.hasOwnProperty(p)) {
                    out.push(fromTagGroup(p, assocBuffer[p]));
                }
            }
        }

        // return the contents of the string buffer
        return out.join("");
    }

    function fromTagGroup(name, entries) {
        var i,
            out;

        // initialize a string buffer
        out = [];

        out.push("\n");
        if (entries.length === 1 && entries[0] === null) {
            out.push(" * **"+name+"**");
        }
        else {
            out.push(" * **"+name+":**");
            if (entries.length > 1) {
                for (i = 0; i < entries.length; i++) {
                    out.push("\n");
                    out.push("   * "+entries[i]);
                }
            }
            else if (entries.length === 1) {
                out.push(" "+entries[0]);
            }
        }

        // return the contents of the string buffer
        return out.join("");
    }

    function getSections(code) {
        var docLine,
            fieldDeclaration,
            m,
            out,
            regex;

        regex = /\/\*\*([^]*?)\*\/([^{;/]+)/gm;
        out = [];

        while ((m = regex.exec(code)) !== null) {
            if (m.index === regex.lastIndex) {
                regex.lastIndex++;
            }

            if (typeof m[1] === "string" && m[1] !== null) {
                if (typeof m[2] === "string" && m[2] !== null) {
                    fieldDeclaration = m[2].trim();
                    docLine = m[1];

                    // if the source code line is an import statement
                    if (/^import\s+/.test(fieldDeclaration)) {
                        // ignore this piece
                        continue;
                    }

                    // if this is a single line comment
                    if (docLine.indexOf("*") === -1) {
                        // prepend an asterisk to achieve the normal line structure
                        docLine = "*"+docLine;
                    }

                    // interpret empty lines as if they contained a p-tag
                    docLine = docLine.replace(/\*[ ]*$/gm, "*
");

                    out.push({ "line": fieldDeclaration, "doc": docLine });
                }
            }
        }

        return out;
    }

    function getFieldDeclaration(line) {
        var regex = /^([^\{;]+)(.*?)$/gm;
        var m;

        while ((m = regex.exec(line)) !== null) {
            if (m.index === regex.lastIndex) {
                regex.lastIndex++;
            }

            if (typeof m[1] === "string" && m[1] !== null) {
                return cleanSingleLine(m[1]);
            }
        }

        return "";
    }

    function getDocDescription(docLines) {
        var regex = /^(\t| )*?\*(\t| )+([^@].*)$/gm;
        var m;
        var out = [];

        while ((m = regex.exec(docLines)) !== null) {
            if (m.index === regex.lastIndex) {
                regex.lastIndex++;
            }

            if (typeof m[3] === "string" && m[3] !== null) {
                out.push(cleanLine(m[3]));
            }
        }

        return cleanLine(out.join(" ").replace(/<(\/)?p>/gi, "\n\n"));
    }

    function getDocTags(docLines) {
        var regex = /^(\t| )*?\*(\t| )*?@([a-zA-Z]+)(.*)$/gm;
        var m;
        var out = [];

        while ((m = regex.exec(docLines)) !== null) {
            if (m.index === regex.lastIndex) {
                regex.lastIndex++;
            }

            if (typeof m[3] === "string" && m[3] !== null) {
                if (typeof m[4] === "string" && m[4] !== null) {
                    out.push({ "key": cleanSingleLine(m[3]), "value": cleanSingleLine(m[4]) });
                }
            }
        }

        return out;
    }

    function cleanLine(line) {
        // trim leading and trailing spaces
        line = line.trim();

        // clear spaces after line breaks and tabs
        line = line.replace(/([\n\r\t])[ ]+/gm, "$1");

        // make consecutive spaces one
        line = line.replace(/[ ]{2,}/g, " ");

        return line;
    }

    function cleanSingleLine(line) {
        // perform normal line cleaning
        line = cleanLine(line);

        // replace line breaks and tabs with spaces
        line = line.replace(/(\n|\r|\t)/g, " ");

        return line;
    }

    function addToBuffer(buffer, key, value) {
        if (typeof buffer[key] === "undefined" || buffer[key] === null) {
            buffer[key] = [];
        }
        buffer[key].push(value);
    }

    String.prototype.tokenize = function(splitByRegex, limit) {
        var counter,
            i,
            m,
            start,
            tokens;

        tokens = [];
        counter = 1;
        start = 0;

        while ((m = splitByRegex.exec(this)) !== null) {
            if (m.index === splitByRegex.lastIndex) {
                splitByRegex.lastIndex++;
            }

            if (counter < limit) {
                tokens.push(this.substring(start, m.index));
                start = m.index + m[0].length;
            }

            counter++;
        }

        // add the remainder as a single part
        tokens.push(this.substring(start));

        // fill the array to match the limit if necessary
        for (i = tokens.length; i < limit; i++) {
            tokens.push("");
        }

        return tokens;
    };

    String.prototype.repeat = function(count) {
        return new Array(count + 1).join(this);
    };

};
<|endoftext|>