<commit_before>// Mozilla Firefox User Preferences

// -----------------------------------------------------------------------------
// General
// -----------------------------------------------------------------------------

// Show a blank page when Firefox starts
user_pref("browser.startup.homepage", "about:blank");
user_pref("browser.startup.page", 0);
user_pref("browser.rights.3.shown", true);
user_pref("browser.startup.homepage_override.mstone", "ignore");
user_pref("startup.homepage_welcome_url", "");
user_pref("startup.homepage_welcome_url.additional", "");
user_pref("startup.homepage_override_url", "");
user_pref("browser.laterrun.enabled", false);
user_pref("browser.usedOnWindows10.introURL", "");

// Show a blank page when opening new tab
user_pref("browser.newtab.url", "about:blank");   // Deprecated since Firefox 41
user_pref("browser.newtabpage.enabled", false);
user_pref("browser.newtabpage.enhanced", false);
user_pref("browser.newtab.preload", false);
user_pref("browser.newtabpage.introShown", true);
user_pref("browser.newtabpage.directory.ping", "data:text/plain,");
user_pref("browser.newtabpage.directory.source", "data:text/plain,");

// Always ask me where to save download files
user_pref("browser.download.useDownloadDir", false);

// Disable downloading on desktop
user_pref("browser.download.folderList", 2);

// -----------------------------------------------------------------------------
// Search
// -----------------------------------------------------------------------------

// Set default region for search engines
user_pref("browser.search.region", "JP");
user_pref("browser.search.countryCode", "JP");
user_pref("browser.search.isUS", false);

// Disable geographically specific results/search engines
user_pref("browser.search.geoSpecificDefaults", false);
user_pref("browser.search.geoSpecificDefaults.url", "");

// -----------------------------------------------------------------------------
// Content
// -----------------------------------------------------------------------------

// Set minimum font size
user_pref("font.minimum-size.ja", 10);
user_pref("font.minimum-size.ko", 10);
user_pref("font.minimum-size.zh-CN", 10);
user_pref("font.minimum-size.zh-HK", 10);
user_pref("font.minimum-size.zh-TW", 10);

// Choose preferred language for displaying pages
user_pref("intl.accept_languages", "ja, zh-tw, zh, en-us, en");

// Disable Geolocation
user_pref("geo.enabled", false);
user_pref("geo.wifi.uri", "https://127.0.0.1");
user_pref("geo.wifi.logging.enabled", false);
user_pref("geo.wifi.xhr.timeout", 1);
user_pref("browser.search.geoip.url", "");
user_pref("browser.search.geoip.timeout", 1);

// Disable WebRTC
user_pref("media.peerconnection.enabled", false);
user_pref("media.peerconnection.use_document_iceservers", false);
user_pref("media.peerconnection.video.enabled", false);
user_pref("media.peerconnection.identity.timeout", 1);
user_pref("media.peerconnection.turn.disable", true);
user_pref("media.peerconnection.ice.default_address_only", true);

// Disable Beacon
user_pref("beacon.enabled", false);

// Ignore the `ping` attribute for `<a>` elements
user_pref("browser.send_pings", false);
user_pref("browser.send_pings.require_same_host", true);

// Disable camera controlling
user_pref("camera.control.autofocus_moving_callback.enabled", false);
user_pref("camera.control.face_detection.enabled", false);

// Decrease the maximum number of popups from a single non-click event (Default is 20)
user_pref("dom.popup_maximum", 2);

// Disallow JavaScript to raise or lower windows
user_pref("dom.disable_window_flip", true);

// Disallow JavaScript to move or resize existing windows
user_pref("dom.disable_window_move_resize", true);

// Disable SPDY (SPDY would be deprecated in 2016)
user_pref("network.http.spdy.enabled", false);
user_pref("network.http.spdy.enabled.v3-1", false);
user_pref("network.http.spdy.enabled.deps", false);

// -----------------------------------------------------------------------------
// Privacy
// -----------------------------------------------------------------------------

// Request that sites not track me
user_pref("privacy.donottrackheader.enabled", true);
user_pref("privacy.donottrackheader.value", 1);

// Use Tracking Protection in Private Windows
user_pref("privacy.trackingprotection.enabled", true);
user_pref("privacy.trackingprotection.pbmode.enabled", true);
user_pref("privacy.trackingprotection.introCount", 20);

// Never accept third-party cookies
user_pref("network.cookie.cookieBehavior", 1);

// Set the maximum number of pages to be retained in the history database
user_pref("places.history.expiration.max_pages", 10000);
user_pref("places.history.expiration.transient_current_max_pages", 10000);

// -----------------------------------------------------------------------------
// Security
// -----------------------------------------------------------------------------

// Block reported attack sites
user_pref("browser.safebrowsing.malware.enabled", true);

// Block reported web forgeries
user_pref("browser.safebrowsing.enabled", true);

// Do not remember passwords for sites
user_pref("signon.rememberSignons", false);

// -----------------------------------------------------------------------------
// Advanced
// -----------------------------------------------------------------------------

// Disable Firefox Health Report
user_pref("datareporting.healthreport.uploadEnabled", false);
user_pref("datareporting.healthreport.service.enabled", false);
user_pref("datareporting.healthreport.documentServerURI", "");
user_pref("datareporting.healthreport.about.reportUrl", "data:text/plain,");
user_pref("datareporting.healthreport.about.reportUrlInified", "data:text/plain,");
user_pref("datareporting.policy.dataSubmissionEnabled", false);
user_pref("datareporting.policy.dataSubmissionEnabled.v2", false);

// Disable Crash Reporter
user_pref("breakpad.reportURL", "");
user_pref("browser.tabs.crashReporting.sendReport", false); // Added in Firefox 44

// Disable Telemetry
user_pref("toolkit.telemetry.enabled", false);
user_pref("toolkit.telemetry.unified", false);
user_pref("toolkit.telemetry.unifiedIsOptIn", true);
user_pref("toolkit.telemetry.archive.enabled", false);
user_pref("toolkit.telemetry.cachedClientID", "");
user_pref("toolkit.telemetry.server", "");

// Disable Telemetry Experiments
user_pref("experiments.enabled", false);
user_pref("experiments.supported", false);
user_pref("experiments.activeExperiment", false);
user_pref("experiments.manifest.uri", "");
user_pref("network.allow-experiments", false);

// Enable OCSP stapling
user_pref("security.ssl.enable_ocsp_stapling", true);

// Query OCSP responder servers to confirm current validity of certificates
user_pref("security.OCSP.enabled", 1);

// -----------------------------------------------------------------------------
// Add-ons and Plugins
// -----------------------------------------------------------------------------

// Disable enforced add-on signing since Firefox 43
user_pref("xpinstall.signatures.required", false);

// Hide the "Get Add-ons" pane in the Add-ons page
user_pref("extensions.getAddons.showPane", false);

// Disable extension discovery
user_pref("extensions.webservice.discoverURL", "http://127.0.0.1");

// Ask me to activate plugins by default
user_pref("plugin.default.state", 1);
user_pref("plugin.defaultXpi.state", 1);

// Enable click-to-play and set the session permission
user_pref("plugins.click_to_play", true);
user_pref("plugin.sessionPermissionNow.intervalInMinutes", 0);

// Click to play Adobe Flash
user_pref("plugin.state.flash", 1);

// Disable all DRM content
user_pref("media.eme.enabled", false);
user_pref("browser.eme.ui.enabled", false);
user_pref("media.eme.apiVisible", false);

// Disable OpenH264 Video Codec by Cisco
user_pref("media.gmp-gmpopenh264.enabled", false);
user_pref("media.gmp-manager.url", "data:text/plain,");

// Disable Primetime Content Decryption Module by Adobe
user_pref("media.gmp-eme-adobe.enabled", false);

// -----------------------------------------------------------------------------
// Others
// -----------------------------------------------------------------------------

// Disable Heartbeat
user_pref("browser.selfsupport.enabled", false);
user_pref("browser.selfsupport.url", "");

// Disable Firefox Hello
user_pref("loop.enabled", false);
user_pref("loop.server", "");
user_pref("loop.feedback.formURL", "");
user_pref("loop.feedback.manualFormURL", "");
user_pref("loop.facebook.appId", "");
user_pref("loop.facebook.enabled", false);
user_pref("loop.facebook.fallbackUrl", "");
user_pref("loop.facebook.shareUrl", "");
user_pref("loop.logDomains", false);

// Disable Pocket
user_pref("extensions.pocket.enabled", false);
user_pref("extensions.pocket.api", "");
user_pref("extensions.pocket.site", "");
user_pref("extensions.pocket.oAuthConsumerKey", "");

// Disable Reader View
user_pref("reader.parse-on-load.enabled", false);

// Disable capturing page thumbnails
user_pref("browser.pagethumbnails.capturing_disabled", true);
user_pref("pageThumbs.enabled", false);

// Disable Taskbar Jump List in Windows 7 and above
user_pref("browser.taskbar.lists.frequent.enabled", false);
user_pref("browser.taskbar.lists.recent.enabled", false);

// Disable "Slow startup" warnings
user_pref("browser.slowStartup.notificationDisabled", true);
user_pref("browser.slowStartup.maxSamples", 0);
user_pref("browser.slowStartup.samples", 0);

// Disable `about:config` warnings
user_pref("general.warnOnAboutConfig", false);

// Skip the installation delay
user_pref("security.dialog_enable_delay", 0);

// Disable closing browser with last tab
user_pref("browser.tabs.closeWindowWithLastTab", false);

// Disable UI animation
user_pref("browser.fullscreen.animate", false);
user_pref("browser.tabs.animate", false);

// Disable UnifiedComplete
user_pref("browser.urlbar.unifiedcomplete", false);

// Disable search suggestions in URL bar
user_pref("browser.urlbar.suggest.searches", false);
user_pref("browser.urlbar.userMadeSearchSuggestionsChoice", true);

// Disable WebIDE to prevent remote debugging and addon downloads
user_pref("devtools.webide.enabled", false);
user_pref("devtools.webide.autoinstallADBHelper", false);
user_pref("devtools.webide.autoinstallFxdtAdapters", false);
user_pref("devtools.debugger.remote-enabled", false);

// Disable UI tour
user_pref("browser.uitour.enabled", false);

// Prevent links from launching Windows Store (Windows 8+)
user_pref("network.protocol-handler.external.ms-windows-store", false);

// Disable Microsoft Family Safety cert (Winodws 8+)
user_pref("security.family_safety.mode", 0);

// Set the cache directory location
// user_pref("browser.cache.disk.parent_directory", "");
<commit_msg>Disable audio auto-play in background for Firefox<commit_after>// Mozilla Firefox User Preferences

// -----------------------------------------------------------------------------
// General
// -----------------------------------------------------------------------------

// Show a blank page when Firefox starts
user_pref("browser.startup.homepage", "about:blank");
user_pref("browser.startup.page", 0);
user_pref("browser.rights.3.shown", true);
user_pref("browser.startup.homepage_override.mstone", "ignore");
user_pref("startup.homepage_welcome_url", "");
user_pref("startup.homepage_welcome_url.additional", "");
user_pref("startup.homepage_override_url", "");
user_pref("browser.laterrun.enabled", false);
user_pref("browser.usedOnWindows10.introURL", "");

// Show a blank page when opening new tab
user_pref("browser.newtab.url", "about:blank");   // Deprecated since Firefox 41
user_pref("browser.newtabpage.enabled", false);
user_pref("browser.newtabpage.enhanced", false);
user_pref("browser.newtab.preload", false);
user_pref("browser.newtabpage.introShown", true);
user_pref("browser.newtabpage.directory.ping", "data:text/plain,");
user_pref("browser.newtabpage.directory.source", "data:text/plain,");

// Always ask me where to save download files
user_pref("browser.download.useDownloadDir", false);

// Disable downloading on desktop
user_pref("browser.download.folderList", 2);

// -----------------------------------------------------------------------------
// Search
// -----------------------------------------------------------------------------

// Set default region for search engines
user_pref("browser.search.region", "JP");
user_pref("browser.search.countryCode", "JP");
user_pref("browser.search.isUS", false);

// Disable geographically specific results/search engines
user_pref("browser.search.geoSpecificDefaults", false);
user_pref("browser.search.geoSpecificDefaults.url", "");

// -----------------------------------------------------------------------------
// Content
// -----------------------------------------------------------------------------

// Set minimum font size
user_pref("font.minimum-size.ja", 10);
user_pref("font.minimum-size.ko", 10);
user_pref("font.minimum-size.zh-CN", 10);
user_pref("font.minimum-size.zh-HK", 10);
user_pref("font.minimum-size.zh-TW", 10);

// Choose preferred language for displaying pages
user_pref("intl.accept_languages", "ja, zh-tw, zh, en-us, en");

// Disable Geolocation
user_pref("geo.enabled", false);
user_pref("geo.wifi.uri", "https://127.0.0.1");
user_pref("geo.wifi.logging.enabled", false);
user_pref("geo.wifi.xhr.timeout", 1);
user_pref("browser.search.geoip.url", "");
user_pref("browser.search.geoip.timeout", 1);

// Disable WebRTC
user_pref("media.peerconnection.enabled", false);
user_pref("media.peerconnection.use_document_iceservers", false);
user_pref("media.peerconnection.video.enabled", false);
user_pref("media.peerconnection.identity.timeout", 1);
user_pref("media.peerconnection.turn.disable", true);
user_pref("media.peerconnection.ice.default_address_only", true);

// Disable audio auto-play in non-active tabs
user_pref("media.block-autoplay-until-in-foreground", true);

// Disable Beacon
user_pref("beacon.enabled", false);

// Ignore the `ping` attribute for `<a>` elements
user_pref("browser.send_pings", false);
user_pref("browser.send_pings.require_same_host", true);

// Disable camera controlling
user_pref("camera.control.autofocus_moving_callback.enabled", false);
user_pref("camera.control.face_detection.enabled", false);

// Decrease the maximum number of popups from a single non-click event (Default is 20)
user_pref("dom.popup_maximum", 2);

// Disallow JavaScript to raise or lower windows
user_pref("dom.disable_window_flip", true);

// Disallow JavaScript to move or resize existing windows
user_pref("dom.disable_window_move_resize", true);

// Disable SPDY (SPDY would be deprecated in 2016)
user_pref("network.http.spdy.enabled", false);
user_pref("network.http.spdy.enabled.v3-1", false);
user_pref("network.http.spdy.enabled.deps", false);

// -----------------------------------------------------------------------------
// Privacy
// -----------------------------------------------------------------------------

// Request that sites not track me
user_pref("privacy.donottrackheader.enabled", true);
user_pref("privacy.donottrackheader.value", 1);

// Use Tracking Protection in Private Windows
user_pref("privacy.trackingprotection.enabled", true);
user_pref("privacy.trackingprotection.pbmode.enabled", true);
user_pref("privacy.trackingprotection.introCount", 20);

// Never accept third-party cookies
user_pref("network.cookie.cookieBehavior", 1);

// Set the maximum number of pages to be retained in the history database
user_pref("places.history.expiration.max_pages", 10000);
user_pref("places.history.expiration.transient_current_max_pages", 10000);

// -----------------------------------------------------------------------------
// Security
// -----------------------------------------------------------------------------

// Block reported attack sites
user_pref("browser.safebrowsing.malware.enabled", true);

// Block reported web forgeries
user_pref("browser.safebrowsing.enabled", true);

// Do not remember passwords for sites
user_pref("signon.rememberSignons", false);

// -----------------------------------------------------------------------------
// Advanced
// -----------------------------------------------------------------------------

// Disable Firefox Health Report
user_pref("datareporting.healthreport.uploadEnabled", false);
user_pref("datareporting.healthreport.service.enabled", false);
user_pref("datareporting.healthreport.documentServerURI", "");
user_pref("datareporting.healthreport.about.reportUrl", "data:text/plain,");
user_pref("datareporting.healthreport.about.reportUrlInified", "data:text/plain,");
user_pref("datareporting.policy.dataSubmissionEnabled", false);
user_pref("datareporting.policy.dataSubmissionEnabled.v2", false);

// Disable Crash Reporter
user_pref("breakpad.reportURL", "");
user_pref("browser.tabs.crashReporting.sendReport", false); // Added in Firefox 44

// Disable Telemetry
user_pref("toolkit.telemetry.enabled", false);
user_pref("toolkit.telemetry.unified", false);
user_pref("toolkit.telemetry.unifiedIsOptIn", true);
user_pref("toolkit.telemetry.archive.enabled", false);
user_pref("toolkit.telemetry.cachedClientID", "");
user_pref("toolkit.telemetry.server", "");

// Disable Telemetry Experiments
user_pref("experiments.enabled", false);
user_pref("experiments.supported", false);
user_pref("experiments.activeExperiment", false);
user_pref("experiments.manifest.uri", "");
user_pref("network.allow-experiments", false);

// Enable OCSP stapling
user_pref("security.ssl.enable_ocsp_stapling", true);

// Query OCSP responder servers to confirm current validity of certificates
user_pref("security.OCSP.enabled", 1);

// -----------------------------------------------------------------------------
// Add-ons and Plugins
// -----------------------------------------------------------------------------

// Disable enforced add-on signing since Firefox 43
user_pref("xpinstall.signatures.required", false);

// Hide the "Get Add-ons" pane in the Add-ons page
user_pref("extensions.getAddons.showPane", false);

// Disable extension discovery
user_pref("extensions.webservice.discoverURL", "http://127.0.0.1");

// Ask me to activate plugins by default
user_pref("plugin.default.state", 1);
user_pref("plugin.defaultXpi.state", 1);

// Enable click-to-play and set the session permission
user_pref("plugins.click_to_play", true);
user_pref("plugin.sessionPermissionNow.intervalInMinutes", 0);

// Click to play Adobe Flash
user_pref("plugin.state.flash", 1);

// Disable all DRM content
user_pref("media.eme.enabled", false);
user_pref("browser.eme.ui.enabled", false);
user_pref("media.eme.apiVisible", false);

// Disable OpenH264 Video Codec by Cisco
user_pref("media.gmp-gmpopenh264.enabled", false);
user_pref("media.gmp-manager.url", "data:text/plain,");

// Disable Primetime Content Decryption Module by Adobe
user_pref("media.gmp-eme-adobe.enabled", false);

// -----------------------------------------------------------------------------
// Others
// -----------------------------------------------------------------------------

// Disable Heartbeat
user_pref("browser.selfsupport.enabled", false);
user_pref("browser.selfsupport.url", "");

// Disable Firefox Hello
user_pref("loop.enabled", false);
user_pref("loop.server", "");
user_pref("loop.feedback.formURL", "");
user_pref("loop.feedback.manualFormURL", "");
user_pref("loop.facebook.appId", "");
user_pref("loop.facebook.enabled", false);
user_pref("loop.facebook.fallbackUrl", "");
user_pref("loop.facebook.shareUrl", "");
user_pref("loop.logDomains", false);

// Disable Pocket
user_pref("extensions.pocket.enabled", false);
user_pref("extensions.pocket.api", "");
user_pref("extensions.pocket.site", "");
user_pref("extensions.pocket.oAuthConsumerKey", "");

// Disable Reader View
user_pref("reader.parse-on-load.enabled", false);

// Disable capturing page thumbnails
user_pref("browser.pagethumbnails.capturing_disabled", true);
user_pref("pageThumbs.enabled", false);

// Disable Taskbar Jump List in Windows 7 and above
user_pref("browser.taskbar.lists.frequent.enabled", false);
user_pref("browser.taskbar.lists.recent.enabled", false);

// Disable "Slow startup" warnings
user_pref("browser.slowStartup.notificationDisabled", true);
user_pref("browser.slowStartup.maxSamples", 0);
user_pref("browser.slowStartup.samples", 0);

// Disable `about:config` warnings
user_pref("general.warnOnAboutConfig", false);

// Skip the installation delay
user_pref("security.dialog_enable_delay", 0);

// Disable closing browser with last tab
user_pref("browser.tabs.closeWindowWithLastTab", false);

// Disable UI animation
user_pref("browser.fullscreen.animate", false);
user_pref("browser.tabs.animate", false);

// Disable UnifiedComplete
user_pref("browser.urlbar.unifiedcomplete", false);

// Disable search suggestions in URL bar
user_pref("browser.urlbar.suggest.searches", false);
user_pref("browser.urlbar.userMadeSearchSuggestionsChoice", true);

// Disable WebIDE to prevent remote debugging and addon downloads
user_pref("devtools.webide.enabled", false);
user_pref("devtools.webide.autoinstallADBHelper", false);
user_pref("devtools.webide.autoinstallFxdtAdapters", false);
user_pref("devtools.debugger.remote-enabled", false);

// Disable UI tour
user_pref("browser.uitour.enabled", false);

// Prevent links from launching Windows Store (Windows 8+)
user_pref("network.protocol-handler.external.ms-windows-store", false);

// Disable Microsoft Family Safety cert (Winodws 8+)
user_pref("security.family_safety.mode", 0);

// Set the cache directory location
// user_pref("browser.cache.disk.parent_directory", "");
<|endoftext|>


<commit_before>if (typeof define !== 'function') { var define = require('amdefine')(module) }
define( function(require){

/*
    TableView
*/
var $ = require('jquery');
var t = require('es/templates');
var Backbone = require('backbone');

return Backbone.View.extend({
  initialize: function(o){
    this.sheet = this.model;
    this.num_row = this.sheet.rowCount();
    this.num_col = this.sheet.colCount();
    this.elements_initialized = false;
  },
  render: function(){
    this.initializeElements();
    this.renderTable();
    return this;
  },
  initializeElements: function(){
    if(this.elements_initialized) return;
    this.$el.html(t.sheet_table({id:this.getId()}));
    this.$table = $('#data-table-'+this.getId());
    this.$table_col_headers = $('#column-headers-'+this.getId());
    this.$table_row_headers = $('#row-headers-'+this.getId());
    this.elements_initialized = true;
  },
  getId: function(){
    return this.model.cid;
  },
  renderTable: function(){
    var html = t.table({num_row:this.num_row,num_col:this.num_col});
    this.$table.html(html);
    html = t.table_col_headers({num_col:this.num_col});
    this.$table_col_headers.html(html);
    html = t.table_row_headers({num_row:this.num_row});
    this.$table_row_headers.html(html);
  }
});

});
<commit_msg>*sigh* currently passing in require to templates that use helper functions. this sucks.<commit_after>if (typeof define !== 'function') { var define = require('amdefine')(module) }
define( function(require){

/*
    TableView
*/
var $ = require('jquery');
var t = require('es/templates');
var Backbone = require('backbone');

return Backbone.View.extend({
  initialize: function(o){
    this.sheet = this.model;
    this.num_row = this.sheet.rowCount();
    this.num_col = this.sheet.colCount();
    this.elements_initialized = false;
  },
  render: function(){
    this.initializeElements();
    this.renderTable();
    return this;
  },
  initializeElements: function(){
    if(this.elements_initialized) return;
    this.$el.html(t.sheet_table({id:this.getId()}));
    this.$table = $('#data-table-'+this.getId());
    this.$table_col_headers = $('#column-headers-'+this.getId());
    this.$table_row_headers = $('#row-headers-'+this.getId());
    this.elements_initialized = true;
  },
  getId: function(){
    return this.model.cid;
  },
  renderTable: function(){
    var html = t.table({num_row:this.num_row,num_col:this.num_col});
    this.$table.html(html);
    html = t.table_col_headers({num_col:this.num_col, require:require});
    this.$table_col_headers.html(html);
    html = t.table_row_headers({num_row:this.num_row});
    this.$table_row_headers.html(html);
  }
});

});
<|endoftext|>


<commit_before>// U3.W9:JQuery

// I worked on this challenge [by myself, with: ].
// This challenge took me [#] hours.

$(document).ready(function(){

//RELEASE 0:
  //link the image

//RELEASE 1:

  //Link this script and the jQuery library to the jQuery_example.html file and analyze what this code does.

$('body').css({'background-color': 'pink'})

//RELEASE 2:
  //Add code here to select elements of the DOM
bodyElement = $('body');

//RELEASE 3:
  // Add code here to modify the css and html of DOM elements

$('body>h1').html("Bobolinks")
$('body>h1').css("color", "blue")
$('body>h1').css("border", "1px dotted black")
$('body>h1').css("visibility", "visible")

//RELEASE 4: Event Listener
  // Add the code for the event listener here

$('img').on('mouseover', function(){
  // e.preventDefault()
  $(this).attr('src', 'http://stream.loe.org/images/120302/top/TOPBobolink%20in%20Flight%20Paul%20Higgins.jpg');
  $(this).animate({right:'+=10px'},'slow');
})

$('img').on('mouseleave', function(){
  // e.preventDefault()
  $(this).attr('src', 'devbootcamp-logo.png')
})

//RELEASE 5: Experiment on your own

})  // end of the document.ready function: do not remove or write DOM manipulation below this.<commit_msg>add jquery challenge<commit_after>// U3.W9:JQuery

// I worked on this challenge [with: Shawn Watson ].
// This challenge took me [1.5] hours.

$(document).ready(function(){

//RELEASE 0:
  //link the image

//RELEASE 1:

  //Link this script and the jQuery library to the jQuery_example.html file and analyze what this code does.

$('body').css({'background-color': 'pink'})

//RELEASE 2:
  //Add code here to select elements of the DOM
bodyElement = $('body');

//RELEASE 3:
  // Add code here to modify the css and html of DOM elements

$('body>h1').html("Bobolinks")
$('body>h1').css("color", "blue")
$('body>h1').css("border", "1px dotted black")
$('body>h1').css("visibility", "visible")

//RELEASE 4: Event Listener
  // Add the code for the event listener here

$('img').on('mouseover', function(){
  // e.preventDefault()
  $(this).attr('src', 'http://stream.loe.org/images/120302/top/TOPBobolink%20in%20Flight%20Paul%20Higgins.jpg');
  $(this).animate({right:'+=10px'},'slow');
})

$('img').on('mouseleave', function(){
  // e.preventDefault()
  $(this).attr('src', 'devbootcamp-logo.png')
})

//RELEASE 5: Experiment on your own
$('img').on('mouseover', function(){
  // e.preventDefault()
  $(this).attr('src', 'http://stream.loe.org/images/120302/top/TOPBobolink%20in%20Flight%20Paul%20Higgins.jpg');
  $(this).animate({right:'+=10px'},'slow');
})

// end of the document.ready function: do not remove or write DOM manipulation below this.

// REFLECTION

// What is jQuery?
// jQuery is a DOM manipulation and JavaScript library that comes with methods for
// helping to create powerful websites. You can use jQuery to add interaction,
// animations, and even widgets on your web page.

// What does jQuery do for you?
// jQuery makes your life easier! It helps you add mouse hover effects, and other
// client side web page functionalities and designs with a simple syntax library.

// What did you learn about the DOM while working on this challenge?
// i learned that there are so many libraries built to help you create a website:
// jQuery and bootstrap, for example. I learned that jQuery helps to simplify
// the syntax for finding and manipulating DOM elements.<|endoftext|>


<commit_before>// [VexFlow](http://vexflow.com) - Copyright (c) Mohit Muthanna 2010.
// Author: Cyril Silverman
//
// ## Description
//
// This file implements key signatures. A key signature sits on a stave
// and indicates the notes with implicit accidentals.

import { Vex } from './vex';
import { Flow } from './tables';
import { StaveModifier } from './stavemodifier';
import { Glyph } from './glyph';

export var KeySignature = (function() {
  function KeySignature(keySpec, cancelKeySpec, alterKeySpec) {
    if (arguments.length > 0) this.init(keySpec, cancelKeySpec, alterKeySpec);
  }

  KeySignature.category = 'keysignatures';

  // Space between natural and following accidental depending
  // on vertical position
  KeySignature.accidentalSpacing = {
    '#': {
      above: 6,
      below: 4
    },
    'b': {
      above: 4,
      below: 7
    },
    'n': {
      above: 3,
      below: -1
    },
    '##': {
      above: 6,
      below: 4
    },
    'bb': {
      above: 4,
      below: 7
    },
    'db': {
      above: 4,
      below: 7
    },
    'd': {
      above: 4,
      below: 7
    },
    'bbs': {
      above: 4,
      below: 7
    },
    '++': {
      above: 6,
      below: 4
    },
    '+': {
      above: 6,
      below: 4
    },
    '+-': {
      above: 6,
      below: 4
    },
    '++-': {
      above: 6,
      below: 4
    },
    'bs': {
      above: 4,
      below: 10
    },
    'bss': {
      above: 4,
      below: 10
    }
  };

  // ## Prototype Methods
  Vex.Inherit(KeySignature, StaveModifier, {
    // Create a new Key Signature based on a `key_spec`
    init: function(keySpec, cancelKeySpec, alterKeySpec) {
      KeySignature.superclass.init();

      this.setKeySig(keySpec, cancelKeySpec, alterKeySpec);
      this.setPosition(StaveModifier.Position.BEGIN);
      this.glyphFontScale = 38; // TODO(0xFE): Should this match StaveNote?
      this.glyphs = [];
      this.xPositions = []; // relative to this.x
      this.paddingForced = false;
    },

    getCategory: function() { return KeySignature.category; },

    // Add an accidental glyph to the `stave`. `acc` is the data of the
    // accidental to add. If the `next` accidental is also provided, extra
    // width will be added to the initial accidental for optimal spacing.
    convertToGlyph: function(accid, nextAccid) {
      var accidGlyphData = Flow.accidentalCodes(accid.type);
      var glyph = new Glyph(accidGlyphData.code, this.glyphFontScale);

      // Determine spacing between current accidental and the next accidental
      var extraWidth = 0;
      if (accid.type === "n" && nextAccid) {
        var isAbove = nextAccid.line >= accid.line;
        var spacing = KeySignature.accidentalSpacing[nextAccid.type];
        if (spacing) {
          extraWidth = isAbove ? spacing.above : spacing.below;
        }
      }

      var glyphWidth = accidGlyphData.width + extraWidth;
      var prevXPosition = this.xPositions[this.xPositions.length - 1];
      this.width += glyphWidth;
      // Place the glyph on the stave
      this.placeGlyphOnLine(glyph, this.stave, accid.line);
      this.glyphs.push(glyph);
      this.xPositions.push(prevXPosition + glyphWidth);
    },

    // Cancel out a key signature provided in the `spec` parameter. This will
    // place appropriate natural accidentals before the key signature.
    cancelKey: function(spec) {
      this.formatted = false;
      this.cancelKeySpec = spec;

      return this;
    },

    convertToCancelAccList: function(spec) {
      // Get the accidental list for the cancelled key signature
      var cancel_accList = Flow.keySignature(spec);

      // If the cancelled key has a different accidental type, ie: # vs b
      var different_types = this.accList.length > 0 && cancel_accList.length > 0 &&
                            cancel_accList[0].type !== this.accList[0].type;

      // Determine how many naturals needed to add
      var naturals = 0;
      if (different_types) {
        naturals = cancel_accList.length;
      } else {
        naturals = cancel_accList.length - this.accList.length;
      }

      // Return if no naturals needed
      if (naturals < 1) return;

      // Get the line position for each natural
      var cancelled = [];
      for (var i = 0; i < naturals; i++) {
        var index = i;
        if (!different_types) {
          index = cancel_accList.length - naturals + i;
        }

        var acc = cancel_accList[index];
        cancelled.push({type: "n", line: acc.line});
      }

      // Combine naturals with main accidental list for the key signature
      this.accList = cancelled.concat(this.accList);
    },

    // Deprecated
    addToStave: function(stave, firstGlyph) {
      this.paddingForced = true;
      stave.addModifier(this);

      return this;
    },

    // Apply the accidental staff line placement based on the `clef` and
    // the  accidental `type` for the key signature ('# or 'b').
    convertAccLines: function(clef, type) {
      var offset = 0.0; // if clef === "treble"
      var customLines; // when clef doesn't follow treble key sig shape

      switch (clef) {
        // Treble & Subbass both have offsets of 0, so are not included.
        case "soprano":
          if(type === "#") customLines = [2.5,0.5,2,0,1.5,-0.5,1];
          else offset = -1;
          break;
        case "mezzo-soprano":
          if(type === "b") customLines = [0,2,0.5,2.5,1,3,1.5];
          else offset = 1.5;
          break;
        case "alto":
          offset = 0.5;
          break;
        case "tenor":
          if(type === "#") customLines = [3, 1, 2.5, 0.5, 2, 0, 1.5];
          else offset = -0.5;
          break;
        case "baritone-f":
        case "baritone-c":
          if(type === "b") customLines = [0.5,2.5,1,3,1.5,3.5,2];
          else offset = 2;
          break;
        case "bass":
        case "french":
          offset = 1;
          break;
      }

      // If there's a special case, assign those lines/spaces:
      var i;
      if (typeof customLines !== "undefined") {
        for (i = 0; i < this.accList.length; ++i) {
          this.accList[i].line = customLines[i];
        }
      } else if (offset !== 0) {
        for (i = 0; i < this.accList.length; ++i) {
          this.accList[i].line += offset;
        }
      }
    },

    getPadding: function(index) {
      if (!this.formatted) this.format();

      return (
        this.glyphs.length === 0 || (!this.paddingForced && index < 2) ?
          0 : this.padding
      );
    },

    getWidth: function() {
      if (!this.formatted) this.format();

      return this.width;
    },

    setKeySig: function(keySpec, cancelKeySpec, alterKeySpec) {
      this.formatted = false;
      this.keySpec = keySpec;
      this.cancelKeySpec = cancelKeySpec;
      this.alterKeySpec = alterKeySpec;

      return this;
    },

    // Alter the accidentals of a key spec one by one.
    // Each alteration is a new accidental that replaces the
    // original accidental (or the canceled one).
    alterKey: function(alterKeySpec) {
      this.formatted = false;
      this.alterKeySpec = alterKeySpec;

      return this;
    },

    convertToAlterAccList: function(alterKeySpec) {
      var max = Math.min(alterKeySpec.length, this.accList.length);
      for (var i = 0; i < max; ++i) {
        if (alterKeySpec[i]) {
          this.accList[i].type = alterKeySpec[i];
        }
      }
    },

    format: function() {
      if (!this.stave) throw new Vex.RERR("KeySignatureError", "Can't draw key signature without stave.");

      this.width = 0;
      this.glyphs = [];
      this.xPositions = [0]; // initialize with initial x position
      this.accList = Flow.keySignature(this.keySpec);
      if (this.cancelKeySpec) {
        this.convertToCancelAccList(this.cancelKeySpec);
      }
      var firstAccidentalType = this.accList.length > 0 ? this.accList[0].type : null;
      if (this.alterKeySpec) {
        this.convertToAlterAccList(this.alterKeySpec);
      }

      if (this.accList.length > 0) {
        this.convertAccLines(this.stave.clef, firstAccidentalType);
        for (var i = 0; i < this.accList.length; ++i) {
          this.convertToGlyph(this.accList[i], this.accList[i+1]);
        }
      }

      this.formatted = true;
    },

    draw: function() {
      if (!this.x) throw new Vex.RERR("KeySignatureError", "Can't draw key signature without x.");
      if (!this.stave) throw new Vex.RERR("KeySignatureError", "Can't draw key signature without stave.");
      if (!this.formatted) this.format();

      for (var i = 0; i < this.glyphs.length; i++) {
        var glyph = this.glyphs[i];
        var x = this.x + this.xPositions[i];
        glyph.setStave(this.stave);
        glyph.setContext(this.stave.context);
        glyph.renderToStave(x);
      }
    }
  });

  return KeySignature;
}());
<commit_msg>Refine keySignature.convertToGlyph comments.<commit_after>// [VexFlow](http://vexflow.com) - Copyright (c) Mohit Muthanna 2010.
// Author: Cyril Silverman
//
// ## Description
//
// This file implements key signatures. A key signature sits on a stave
// and indicates the notes with implicit accidentals.

import { Vex } from './vex';
import { Flow } from './tables';
import { StaveModifier } from './stavemodifier';
import { Glyph } from './glyph';

export var KeySignature = (function() {
  function KeySignature(keySpec, cancelKeySpec, alterKeySpec) {
    if (arguments.length > 0) this.init(keySpec, cancelKeySpec, alterKeySpec);
  }

  KeySignature.category = 'keysignatures';

  // Space between natural and following accidental depending
  // on vertical position
  KeySignature.accidentalSpacing = {
    '#': {
      above: 6,
      below: 4
    },
    'b': {
      above: 4,
      below: 7
    },
    'n': {
      above: 3,
      below: -1
    },
    '##': {
      above: 6,
      below: 4
    },
    'bb': {
      above: 4,
      below: 7
    },
    'db': {
      above: 4,
      below: 7
    },
    'd': {
      above: 4,
      below: 7
    },
    'bbs': {
      above: 4,
      below: 7
    },
    '++': {
      above: 6,
      below: 4
    },
    '+': {
      above: 6,
      below: 4
    },
    '+-': {
      above: 6,
      below: 4
    },
    '++-': {
      above: 6,
      below: 4
    },
    'bs': {
      above: 4,
      below: 10
    },
    'bss': {
      above: 4,
      below: 10
    }
  };

  // ## Prototype Methods
  Vex.Inherit(KeySignature, StaveModifier, {
    // Create a new Key Signature based on a `key_spec`
    init: function(keySpec, cancelKeySpec, alterKeySpec) {
      KeySignature.superclass.init();

      this.setKeySig(keySpec, cancelKeySpec, alterKeySpec);
      this.setPosition(StaveModifier.Position.BEGIN);
      this.glyphFontScale = 38; // TODO(0xFE): Should this match StaveNote?
      this.glyphs = [];
      this.xPositions = []; // relative to this.x
      this.paddingForced = false;
    },

    getCategory: function() { return KeySignature.category; },

    // Add an accidental glyph to the `KeySignature` instance which represents
    // the provided `accid`. If a `nextAccid` is also provided, the appropriate
    // spacing will be included in the glyph's position
    convertToGlyph: function(accid, nextAccid) {
      var accidGlyphData = Flow.accidentalCodes(accid.type);
      var glyph = new Glyph(accidGlyphData.code, this.glyphFontScale);

      // Determine spacing between current accidental and the next accidental
      var extraWidth = 0;
      if (accid.type === "n" && nextAccid) {
        var isAbove = nextAccid.line >= accid.line;
        var spacing = KeySignature.accidentalSpacing[nextAccid.type];
        if (spacing) {
          extraWidth = isAbove ? spacing.above : spacing.below;
        }
      }

      var glyphWidth = accidGlyphData.width + extraWidth;
      var prevXPosition = this.xPositions[this.xPositions.length - 1];
      this.width += glyphWidth;
      // Place the glyph on the stave
      this.placeGlyphOnLine(glyph, this.stave, accid.line);
      this.glyphs.push(glyph);
      // Store the next accidental's x position
      this.xPositions.push(prevXPosition + glyphWidth);
    },

    // Cancel out a key signature provided in the `spec` parameter. This will
    // place appropriate natural accidentals before the key signature.
    cancelKey: function(spec) {
      this.formatted = false;
      this.cancelKeySpec = spec;

      return this;
    },

    convertToCancelAccList: function(spec) {
      // Get the accidental list for the cancelled key signature
      var cancel_accList = Flow.keySignature(spec);

      // If the cancelled key has a different accidental type, ie: # vs b
      var different_types = this.accList.length > 0 && cancel_accList.length > 0 &&
                            cancel_accList[0].type !== this.accList[0].type;

      // Determine how many naturals needed to add
      var naturals = 0;
      if (different_types) {
        naturals = cancel_accList.length;
      } else {
        naturals = cancel_accList.length - this.accList.length;
      }

      // Return if no naturals needed
      if (naturals < 1) return;

      // Get the line position for each natural
      var cancelled = [];
      for (var i = 0; i < naturals; i++) {
        var index = i;
        if (!different_types) {
          index = cancel_accList.length - naturals + i;
        }

        var acc = cancel_accList[index];
        cancelled.push({type: "n", line: acc.line});
      }

      // Combine naturals with main accidental list for the key signature
      this.accList = cancelled.concat(this.accList);
    },

    // Deprecated
    addToStave: function(stave, firstGlyph) {
      this.paddingForced = true;
      stave.addModifier(this);

      return this;
    },

    // Apply the accidental staff line placement based on the `clef` and
    // the  accidental `type` for the key signature ('# or 'b').
    convertAccLines: function(clef, type) {
      var offset = 0.0; // if clef === "treble"
      var customLines; // when clef doesn't follow treble key sig shape

      switch (clef) {
        // Treble & Subbass both have offsets of 0, so are not included.
        case "soprano":
          if(type === "#") customLines = [2.5,0.5,2,0,1.5,-0.5,1];
          else offset = -1;
          break;
        case "mezzo-soprano":
          if(type === "b") customLines = [0,2,0.5,2.5,1,3,1.5];
          else offset = 1.5;
          break;
        case "alto":
          offset = 0.5;
          break;
        case "tenor":
          if(type === "#") customLines = [3, 1, 2.5, 0.5, 2, 0, 1.5];
          else offset = -0.5;
          break;
        case "baritone-f":
        case "baritone-c":
          if(type === "b") customLines = [0.5,2.5,1,3,1.5,3.5,2];
          else offset = 2;
          break;
        case "bass":
        case "french":
          offset = 1;
          break;
      }

      // If there's a special case, assign those lines/spaces:
      var i;
      if (typeof customLines !== "undefined") {
        for (i = 0; i < this.accList.length; ++i) {
          this.accList[i].line = customLines[i];
        }
      } else if (offset !== 0) {
        for (i = 0; i < this.accList.length; ++i) {
          this.accList[i].line += offset;
        }
      }
    },

    getPadding: function(index) {
      if (!this.formatted) this.format();

      return (
        this.glyphs.length === 0 || (!this.paddingForced && index < 2) ?
          0 : this.padding
      );
    },

    getWidth: function() {
      if (!this.formatted) this.format();

      return this.width;
    },

    setKeySig: function(keySpec, cancelKeySpec, alterKeySpec) {
      this.formatted = false;
      this.keySpec = keySpec;
      this.cancelKeySpec = cancelKeySpec;
      this.alterKeySpec = alterKeySpec;

      return this;
    },

    // Alter the accidentals of a key spec one by one.
    // Each alteration is a new accidental that replaces the
    // original accidental (or the canceled one).
    alterKey: function(alterKeySpec) {
      this.formatted = false;
      this.alterKeySpec = alterKeySpec;

      return this;
    },

    convertToAlterAccList: function(alterKeySpec) {
      var max = Math.min(alterKeySpec.length, this.accList.length);
      for (var i = 0; i < max; ++i) {
        if (alterKeySpec[i]) {
          this.accList[i].type = alterKeySpec[i];
        }
      }
    },

    format: function() {
      if (!this.stave) throw new Vex.RERR("KeySignatureError", "Can't draw key signature without stave.");

      this.width = 0;
      this.glyphs = [];
      this.xPositions = [0]; // initialize with initial x position
      this.accList = Flow.keySignature(this.keySpec);
      if (this.cancelKeySpec) {
        this.convertToCancelAccList(this.cancelKeySpec);
      }
      var firstAccidentalType = this.accList.length > 0 ? this.accList[0].type : null;
      if (this.alterKeySpec) {
        this.convertToAlterAccList(this.alterKeySpec);
      }

      if (this.accList.length > 0) {
        this.convertAccLines(this.stave.clef, firstAccidentalType);
        for (var i = 0; i < this.accList.length; ++i) {
          this.convertToGlyph(this.accList[i], this.accList[i+1]);
        }
      }

      this.formatted = true;
    },

    draw: function() {
      if (!this.x) throw new Vex.RERR("KeySignatureError", "Can't draw key signature without x.");
      if (!this.stave) throw new Vex.RERR("KeySignatureError", "Can't draw key signature without stave.");
      if (!this.formatted) this.format();

      for (var i = 0; i < this.glyphs.length; i++) {
        var glyph = this.glyphs[i];
        var x = this.x + this.xPositions[i];
        glyph.setStave(this.stave);
        glyph.setContext(this.stave.context);
        glyph.renderToStave(x);
      }
    }
  });

  return KeySignature;
}());
<|endoftext|>


<commit_before>
kff.PageView = kff.createClass(
{
	extend: kff.View,
	mixins: kff.EventsMixin,
	statics:
	{
		precedingView: null,
		args: [{
			viewFactory: '@kff.ViewFactory'
		}]
	}
},
/** @lends kff.PageView.prototype */
{
	/**
	 * Class for the full page view. PageViews behave as normal views but can be used by FrontController as
	 * targets for routing.
	 *
	 * @constructs
	 * @augments kff.View
	 * @param {Object} options Options object (see kff.View for details)
	 */
	constructor: function(options)
	{
		options = options || {};
		this.$docElement = options.element ? $(options.element) : $(options.env.document);
		options.element = options.element || options.env.document.body;

		kff.View.call(this, options);
	},

	/**
	 * @see kff.View#delegateEvents
	 */
	delegateEvents: function(events, $element)
	{
		if(!$element) $element = this.$docElement;
		kff.PageView._super.delegateEvents.call(this, events, $element);
	},

	/**
	 * @see kff.View#undelegateEvents
	 */
	undelegateEvents: function(events, $element)
	{
		if(!$element) $element = this.$docElement;
		kff.PageView._super.undelegateEvents.call(this, events, $element);
	},

	/**
	 * Sets a new state of the view. Called by the front controller.
	 *
	 * @param {Object} state The state object (POJO)
	 */
	setState: function(state)
	{
		this.trigger('setState', state);
	},

	/**
	 * Initializes the view. Calls the render method. Should not be overloaded
	 * by subclasses.
	 *
	 * @private
	 * @param
	 */
	init: function()
	{
		this.renderAll();
		this.runAll();
	},

	/**
	 * Runs the view (i.e. binds events and models). It will be called automatically. Should not be called
	 * directly.
	 */
	runAll: function()
	{
		if(kff.View.prototype.runAll.call(this) !== false)
		{
			this.trigger('render');
		}
	},

	/**
	 * Destroys the view (destroys all subviews and unbinds previously bound DOM events.
	 * It will be called automatically. Should not be called directly.
	 *
	 * @param {Boolean} silent If true, the 'destroy' event won't be called
	 */
	destroyAll: function()
	{
		var ret = kff.View.prototype.destroyAll.call(this);

		if(ret !== false)
		{
			this.trigger('destroy');
		}
	}

});
<commit_msg>refactor(kff.PageView): remove overloaded Page methods previously used for async rendering<commit_after>
kff.PageView = kff.createClass(
{
	extend: kff.View,
	statics:
	{
		precedingView: null,
		args: [{
			viewFactory: '@kff.ViewFactory'
		}]
	}
},
/** @lends kff.PageView.prototype */
{
	/**
	 * Class for the full page view. PageViews behave as normal views but can be used by FrontController as
	 * targets for routing.
	 *
	 * @constructs
	 * @augments kff.View
	 * @param {Object} options Options object (see kff.View for details)
	 */
	constructor: function(options)
	{
		options = options || {};
		this.$docElement = options.element ? $(options.element) : $(options.env.document);
		options.element = options.element || options.env.document.body;

		kff.View.call(this, options);
	},

	/**
	 * @see kff.View#delegateEvents
	 */
	delegateEvents: function(events, $element)
	{
		if(!$element) $element = this.$docElement;
		kff.PageView._super.delegateEvents.call(this, events, $element);
	},

	/**
	 * @see kff.View#undelegateEvents
	 */
	undelegateEvents: function(events, $element)
	{
		if(!$element) $element = this.$docElement;
		kff.PageView._super.undelegateEvents.call(this, events, $element);
	}

});
<|endoftext|>


<commit_before>var mandrill = require("mandrill-api/mandrill"),
    log4js = require("log4js"),
    log = log4js.getLogger("megapis-email"),
    handlebars = require("handlebars"),
    fs = require("fs"),
    _ = require("lodash"),
    util = require("util");

var MegapisWorker = require("megapis-worker").MegapisWorker;

function MandrillWorker(config) {
    MandrillWorker.super_.call(this, config);
}

util.inherits(MandrillWorker, MegapisWorker);

exports.createWorker = function(config) {
    return new MandrillWorker(config);
};

MandrillWorker.prototype.getConfigKeys = function() {
    return ["message", "mandrill.apiKey"];
};

MandrillWorker.prototype.makeMessage = function(values) {
    // group values by source
    var grouped = _.groupBy(values, function(value) {
        if (!value.source) {
            value.source = "default";
        }
        return value.source;
    });
    log.info("found keys ", _.keys(grouped));

    var self = this;
    var html = [];
    // render each set of values with a custom template if available
    _.each(_.keys(grouped), function(source) {
        var filename = "config/"+self.config.id+"_"+source+".hbs";
        log.debug("looking for template "+filename+" for "+source+" messages");
        if (!fs.existsSync(filename)) {
            log.debug("template not found; using default");
            filename = "config/email_default.hbs";
        }
        var template = handlebars.compile(fs.readFileSync(filename, "utf-8"));
        var sourceHtml = "";
        // template should render a single value
        _.each(grouped[source], function(value) {
            sourceHtml += template(value);
        });

        html.push({
            "source": source,
            "name": self.getWorkerName(source),
            "html": sourceHtml.trim()
        });
        if (self.config.debug) {
            log.debug("source="+source+" name="+self.getWorkerName(source)+
                " html=\n"+sourceHtml.trim()+"-----");
        }
    });
    if (html.length === 0) {
        return null;
    }
    var messageTemplate = "config/"+this.config.id+".hbs";
    log.debug("generating message with "+messageTemplate);
    var template = handlebars.compile(fs.readFileSync(messageTemplate, "utf-8"));
    return template({"sources": html});
};

MandrillWorker.prototype.sendMessage = function(html) {
    var mandrillClient = new mandrill.Mandrill(this.config.mandrill.apiKey);
    // message should have subject, from_email, and to
    // see https://mandrillapp.com/api/docs/messages.nodejs.html
    var message = this.config.message;
    message.html = html;
    var data = {
        "message": message,
        "async": false
    };
    mandrillClient.messages.send(data, function(result) {
        log.info("sent message ", result);
    },
    function(e) {
        log.error("A mandrill error occurred: " + e.name + "" - "" + e.message);
    });
};

MandrillWorker.prototype.run = function(callback) {
    var self = this;
    var config = this.config;
    log.info("getting values for "+config.id);
    this.getAndDelete(config.id, function(err, values) {
        var message = self.makeMessage(values);
        if (message) {
            self.sendMessage(message);
        } else {
            log.info("nothing to send");
        }
        callback();
    });
};
<commit_msg>fix email callback<commit_after>var mandrill = require("mandrill-api/mandrill"),
    log4js = require("log4js"),
    log = log4js.getLogger("megapis-email"),
    handlebars = require("handlebars"),
    fs = require("fs"),
    _ = require("lodash"),
    util = require("util");

var MegapisWorker = require("megapis-worker").MegapisWorker;

function MandrillWorker(config) {
    MandrillWorker.super_.call(this, config);
}

util.inherits(MandrillWorker, MegapisWorker);

exports.createWorker = function(config) {
    return new MandrillWorker(config);
};

MandrillWorker.prototype.getConfigKeys = function() {
    return ["message", "mandrill.apiKey"];
};

MandrillWorker.prototype.makeMessage = function(values) {
    // group values by source
    var grouped = _.groupBy(values, function(value) {
        if (!value.source) {
            value.source = "default";
        }
        return value.source;
    });
    log.info("found keys ", _.keys(grouped));

    var self = this;
    var html = [];
    // render each set of values with a custom template if available
    _.each(_.keys(grouped), function(source) {
        var filename = "config/"+self.config.id+"_"+source+".hbs";
        log.debug("looking for template "+filename+" for "+source+" messages");
        if (!fs.existsSync(filename)) {
            log.debug("template not found; using default");
            filename = "config/email_default.hbs";
        }
        var template = handlebars.compile(fs.readFileSync(filename, "utf-8"));
        var sourceHtml = "";
        // template should render a single value
        _.each(grouped[source], function(value) {
            sourceHtml += template(value);
        });

        html.push({
            "source": source,
            "name": self.getWorkerName(source),
            "html": sourceHtml.trim()
        });
        if (self.config.debug) {
            log.debug("source="+source+" name="+self.getWorkerName(source)+
                " html=\n"+sourceHtml.trim()+"-----");
        }
    });
    if (html.length === 0) {
        return null;
    }
    var messageTemplate = "config/"+this.config.id+".hbs";
    log.debug("generating message with "+messageTemplate);
    var template = handlebars.compile(fs.readFileSync(messageTemplate, "utf-8"));
    return template({"sources": html});
};

MandrillWorker.prototype.sendMessage = function(html, callback) {
    var mandrillClient = new mandrill.Mandrill(this.config.mandrill.apiKey);
    // message should have subject, from_email, and to
    // see https://mandrillapp.com/api/docs/messages.nodejs.html
    var message = this.config.message;
    message.html = html;
    var data = {
        "message": message,
        "async": false
    };
    mandrillClient.messages.send(data, function(result) {
        log.info("sent message ", result);
        callback();
    },
    function(e) {
        log.error("A mandrill error occurred: " + e.name + "" - "" + e.message);
        callback(e.message);
    });
};

MandrillWorker.prototype.run = function(callback) {
    var self = this;
    var config = this.config;
    log.info("getting values for "+config.id);
    this.getAndDelete(config.id, function(err, values) {
        var message = self.makeMessage(values);
        if (message) {
            self.sendMessage(message, callback);
        } else {
            log.info("nothing to send");
            callback();
        }
    });
};
<|endoftext|>


<commit_before>/**
 * Throws an error based on the warning
 * If the last argument is a DOM node, it
 * coerces it to a string before throwing.
 * @returns {undefined}
 */
const throwError = function (...args) {
  const last = args[args.length - 1]
  if ( last.outerHTML ) {
    args[args.length - 1] = `Element: \n  ${last.outerHTML}`
  }

  const error = new Error(args.join(' '))
  error.element = last

  throw error
}

/**
 * Show a warning
 * @returns {undefined}
 */
const showWarning = function (...args) {
  console.warn(...args)
}

/**
 * Creates a reporter function based on deprecated options
 * @arg {object} opts - The options passed by the user
 * @returns {function} The reporter
 */
const mkReporter = function (opts) {
  const {
    doThrow        = false
  , warningPrefix  = ''
  } = opts

  return function (info) {
    const {
      msg
    , owner
    , DOMNode
    } = info

    // build warning
    const warning = [
      owner
    , warningPrefix.concat(msg)
    ].concat(DOMNode || [])

    if ( doThrow ) {
      throwError(...warning)
    } else {
      showWarning(...warning)
    }
  }
}

/**
 * Generate a deprecation warning when a key is present
 * in the options object
 * @arg {object} opts - the options object under scrutiny
 * @arg {string} name - the name of the deprecated option
 * @arg {string} msg  - an optional reason for the deprecation
 * @returns {undefined}
 */
const deprecate = function (opts, name, msg = '') {
  if ( name in opts ) {
    console.warn(`react-a11y: the \`${name}\` options is deprecated. ${msg}`)
  }
}

/**
 * Make a certain option mandatory
 * @arg {object} opts - the options object under scrutiny
 * @arg {string} name - the name of the mandatory option
 * @arg {string} msg  - an optional reason
 * @returns {undefined}
 */
const mandatory = function (opts, name, msg = '') {
  if ( !(name in opts) ) {
    throw new Error(`react-a11y: the \`${name}\` option is mandatory. ${msg}`)
  }
}

// always resolve to true
const always = () => true

// deprecation message
const msg = 'Use the `reporter` option to change how warnings are displayed.'

/**
 * Normalize and validate the options that the user passed in.
 * @arg {object} opts - The opts the user passed in
 * @returns {object} the validated options
 */
export default function (opts = {}) {
  deprecate(opts, 'includeSrcNode', msg)
  deprecate(opts, 'throw',          msg)
  deprecate(opts, 'warningPrefix',  msg)

  const {
    reporter     = mkReporter(opts) // make a reporter based on options
  , filterFn     = always
  , plugins      = []
  , rules        = {}
  } = opts

  return {
    filterFn
  , reporter
  , plugins
  , rules
  }
}

<commit_msg>show url in default reporter<commit_after>/**
 * Throws an error based on the warning
 * If the last argument is a DOM node, it
 * coerces it to a string before throwing.
 * @returns {undefined}
 */
const throwError = function (...args) {
  const last = args[args.length - 1]
  if ( last.outerHTML ) {
    args[args.length - 1] = `Element: \n  ${last.outerHTML}`
  }

  const error = new Error(args.join(' '))
  error.element = last

  throw error
}

/**
 * Show a warning
 * @returns {undefined}
 */
const showWarning = function (...args) {
  console.warn(...args)
}

/**
 * Creates a reporter function based on deprecated options
 * @arg {object} opts - The options passed by the user
 * @returns {function} The reporter
 */
const mkReporter = function (opts) {
  const {
    doThrow        = false
  , warningPrefix  = ''
  } = opts

  return function (info) {
    const {
      msg
    , owner
    , DOMNode
    , url
    , tagName
    , severity
    } = info

    // build warning
    const warning = [
      owner
    , warningPrefix.concat(msg)
    , `see ${url}`
    ].concat(DOMNode || tagName)

    if ( doThrow || severity === 'error' ) {
      throwError(...warning)
    } else {
      showWarning(...warning)
    }
  }
}

/**
 * Generate a deprecation warning when a key is present
 * in the options object
 * @arg {object} opts - the options object under scrutiny
 * @arg {string} name - the name of the deprecated option
 * @arg {string} msg  - an optional reason for the deprecation
 * @returns {undefined}
 */
const deprecate = function (opts, name, msg = '') {
  if ( name in opts ) {
    console.warn(`react-a11y: the \`${name}\` options is deprecated. ${msg}`)
  }
}

/**
 * Make a certain option mandatory
 * @arg {object} opts - the options object under scrutiny
 * @arg {string} name - the name of the mandatory option
 * @arg {string} msg  - an optional reason
 * @returns {undefined}
 */
const mandatory = function (opts, name, msg = '') {
  if ( !(name in opts) ) {
    throw new Error(`react-a11y: the \`${name}\` option is mandatory. ${msg}`)
  }
}

// always resolve to true
const always = () => true

// deprecation message
const msg = 'Use the `reporter` option to change how warnings are displayed.'

/**
 * Normalize and validate the options that the user passed in.
 * @arg {object} opts - The opts the user passed in
 * @returns {object} the validated options
 */
export default function (opts = {}) {
  deprecate(opts, 'includeSrcNode', msg)
  deprecate(opts, 'throw',          msg)
  deprecate(opts, 'warningPrefix',  msg)

  const {
    reporter     = mkReporter(opts) // make a reporter based on options
  , filterFn     = always
  , plugins      = []
  , rules        = {}
  } = opts

  return {
    filterFn
  , reporter
  , plugins
  , rules
  }
}

<|endoftext|>


<commit_before>
/**
 * Fluder 0.1.0
 * A unidirectional data flow tool based on flux.
 *
 * url: https://github.com/coderwin/Fluder
 * author: chenjiancj2011@outlook.com
 * weibo: imChenJian
 * date: 2016-08-10
 */

/**
 * workflow Queue
 */
import Queue from './queue'

/**
 * catchError
 */
import {catchError} from './tools'

/**
 * custom Event
 */
import EventEmitter from 'events'

/**
 * Fluder
 */
export default new class Fluder {

    /**
     * 构造函数
     * @return {object} 返回Fluder实例对象
     */
    constructor() {

        /**
         * store handlers 注册Map
         * @type {Object}
         */
        this._registers = {}

        /**
         * dispatch栈
         * @type {Array}
         */
        this._dispatchStack = []

        /**
         * 初始化
         */
        this._init()
    }

    _init() {

        /**
         * 中间件，集中处理action payload和storeId
         */
        this._middleware = new Queue(true).then((payload)=>{

            /**
             * 中间件队列执行完后触发Store handler的调用
             */
            this._invoke(payload)
        })
    }

    /**
     * action对handler的调用(内部调用)
     * @param  {object} payload 此时的payload包含action和action对应的storeId
     * @return {void}           无返回值
     */
    _invoke(payload) {

        /**
         * storeId: 用于map到register里面注册的handler
         * @type {string}
         */
        let {
            storeId
        } = payload

        /**
         * store和它对应的handler
         * @type {object}
         */
        let {
            store,
            handlers
        } = this._registers[storeId]

        /**
         * action payload
         * @type {object}
         */
        payload = payload.payload

        /**
         * 在当前storeId的store Map到对应的handler
         * @type {function}
         */
        let handler = handlers[payload.type]

        if (typeof handler === 'function') {

            /**
             * TODO
             * result应该为store数据的copy，暂时没做深度copy，后续把Store改写成Immutable数据结构
             * view-controller里面对result的修改不会影响到store里的数据
             */
            let result
            let _result = handler.call(store, payload)

            /**
             * 可以没有返回值，只是set Store里面的值
             * 这里把payload传给change的Store，可以做相应的渲染优化[局部渲染]
             */
            // if (result !== undefined) {
            store.emitChange(payload, result)
            // }
        }
    }

    /**
     * 更新Action栈以及记录当前ActionID
     */
    _startDispatch(storeId) {
        this._dispatchStack || (this._dispatchStack = [])
        this._dispatchStack.push(storeId)
        this._currentDispatch = storeId
    }

    /**
     * Action执行完更新Action栈以及删除当前ActionID
     */
    _endDispatch() {
        this._dispatchStack.pop()
        this._currentDispatch = null
    }

    /**
     * Store和handler注册
     * @param  {string} storeId store/action唯一标示
     * @param  {object} storeHandler  store和handler
     * @return {void}           无返回值
     */
    register(storeId, storeHandler){
        this._registers[storeId] = storeHandler
    }

    /**
     * 中间件入队
     * @param  {function} middleware  中间件处理函数
     * @return {void}           无返回值
     */
    enqueue(middleware){
        this._middleware.enqueue(middleware)
    }

    /**
     * 触发action(内部调用)
     * @param  {string} storeId store/action唯一标示
     * @param  {object} action  action数据
     * @return {void}           无返回值
     */
    dispatch(storeId, payload) {

        /**
         * 在当前Action触发的Store handler回调函数中再次发起了当前Action，这样会造成A-A循环调用,出现栈溢出
         */
        if (this._currentDispatch == storeId) {

            throw Error('action '+(payload.value&&payload.value.actionType)+' __invoke__ myself!')
        }

        /**
         * 在当前Action触发的Store handler回调函数中再次触发了当前Action栈中的Action，出现A-B-C-A式循环调用，也会出现栈溢出
         */
        if (this._dispatchStack.indexOf(storeId) != -1) {

            throw Error(this._dispatchStack.join(' -> ') + storeId + ' : action __invoke__ to a circle!')
        }

        /**
         * 更新Action栈以及记录当前ActionID
         */
        this._startDispatch(storeId)

        /**
         * Action的触发必须有ActionType，原因是ActionType和Store handlers Map的key一一对应
         */
        if (!payload.type) {

            throw new Error('action type does not exist in \n' + JSON.stringify(payload, null, 2))
        }

        try {

            /**
             * 发出action的时候 统一走一遍中间件
             */
            this._middleware.execute({
                storeId,
                payload
            })
        } catch (e) {

            /**
             * 执行handler的时候出错end掉当前dispatch
             */
            this._endDispatch()

            /**
             * 抛出错误信息
             */
            catchError(e)
        }

        /**
         * Action执行完更新Action栈以及删除当前ActionID
         */
        this._endDispatch()
    }
}
<commit_msg>Object.freeze storeId and payload(shallow Immutable)<commit_after>
/**
 * Fluder 0.1.0
 * A unidirectional data flow tool based on flux.
 *
 * url: https://github.com/coderwin/Fluder
 * author: chenjiancj2011@outlook.com
 * weibo: imChenJian
 * date: 2016-08-10
 */

/**
 * workflow Queue
 */
import Queue from './queue'

/**
 * catchError
 */
import {catchError} from './tools'

/**
 * custom Event
 */
import EventEmitter from 'events'

/**
 * Fluder
 */
export default new class Fluder {

    /**
     * 构造函数
     * @return {object} 返回Fluder实例对象
     */
    constructor() {

        /**
         * store handlers 注册Map
         * @type {Object}
         */
        this._registers = {}

        /**
         * dispatch栈
         * @type {Array}
         */
        this._dispatchStack = []

        /**
         * 初始化
         */
        this._init()
    }

    _init() {

        /**
         * 中间件，集中处理action payload和storeId
         */
        this._middleware = new Queue(true).after((payload)=>{

            /**
             * 中间件队列执行完后触发Store handler的调用
             */
            this._invoke(payload)
        })
    }

    /**
     * action对handler的调用(内部调用)
     * @param  {object} payload 此时的payload包含action和action对应的storeId
     * @return {void}           无返回值
     */
    _invoke(payload) {

        /**
         * storeId: 用于map到register里面注册的handler
         * @type {string}
         */
        let {
            storeId
        } = payload

        /**
         * store和它对应的handler
         * @type {object}
         */
        let {
            store,
            handlers
        } = this._registers[storeId]

        /**
         * action payload
         * @type {object}
         */
        payload = payload.payload

        /**
         * 在当前storeId的store Map到对应的handler
         * @type {function}
         */
        let handler = handlers[payload.type]

        if (typeof handler === 'function') {

            /**
             * TODO
             * result应该为store数据的copy，暂时没做深度copy，后续把Store改写成Immutable数据结构
             * view-controller里面对result的修改不会影响到store里的数据
             */
            let result
            let _result = handler.call(store, payload)

            /**
             * 可以没有返回值，只是set Store里面的值
             * 这里把payload传给change的Store，可以做相应的渲染优化[局部渲染]
             */
            // if (result !== undefined) {
            store.emitChange(payload, result)
            // }
        }
    }

    /**
     * 更新Action栈以及记录当前ActionID
     */
    _startDispatch(storeId) {
        this._dispatchStack || (this._dispatchStack = [])
        this._dispatchStack.push(storeId)
        this._currentDispatch = storeId
    }

    /**
     * Action执行完更新Action栈以及删除当前ActionID
     */
    _endDispatch() {
        this._dispatchStack.pop()
        this._currentDispatch = null
    }

    /**
     * Store和handler注册
     * @param  {string} storeId store/action唯一标示
     * @param  {object} storeHandler  store和handler
     * @return {void}           无返回值
     */
    register(storeId, storeHandler){
        this._registers[storeId] = storeHandler
    }

    /**
     * 中间件入队
     * @param  {function} middleware  中间件处理函数
     * @return {void}           无返回值
     */
    enqueue(middleware){
        this._middleware.enqueue(middleware)
    }

    /**
     * 触发action(内部调用)
     * @param  {string} storeId store/action唯一标示
     * @param  {object} action  action数据
     * @return {void}           无返回值
     */
    dispatch(storeId, payload) {

        /**
         * 在当前Action触发的Store handler回调函数中再次发起了当前Action，这样会造成A-A循环调用,出现栈溢出
         */
        if (this._currentDispatch == storeId) {

            throw Error('action '+(payload.value&&payload.value.actionType)+' __invoke__ myself!')
        }

        /**
         * 在当前Action触发的Store handler回调函数中再次触发了当前Action栈中的Action，出现A-B-C-A式循环调用，也会出现栈溢出
         */
        if (this._dispatchStack.indexOf(storeId) != -1) {

            throw Error(this._dispatchStack.join(' -> ') + storeId + ' : action __invoke__ to a circle!')
        }

        /**
         * 更新Action栈以及记录当前ActionID
         */
        this._startDispatch(storeId)

        /**
         * Action的触发必须有ActionType，原因是ActionType和Store handlers Map的key一一对应
         */
        if (!payload.type) {

            throw new Error('action type does not exist in \n' + JSON.stringify(payload, null, 2))
        }

        try {

            /**
             * 发出action的时候 统一走一遍中间件
             *
             * {
             *     storeId,
             *     payload
             * }
             *
             * shallow Immutable
             */
            this._middleware.execute(Object.freeze({
                storeId,
                payload
            }))
        } catch (e) {

            /**
             * 执行handler的时候出错end掉当前dispatch
             */
            this._endDispatch()

            /**
             * 抛出错误信息
             */
            catchError(e)
        }

        /**
         * Action执行完更新Action栈以及删除当前ActionID
         */
        this._endDispatch()
    }
}
<|endoftext|>


<commit_before>/**
 * @preserve format.js v1.0.0 (c) 2013 knowledgecode | MIT licensed
 */
/*global define, module */
/*jslint browser: true, plusplus: true, regexp: true */
(function () {
    'use strict';

    /**
     * @name format
     * @function
     * @param {string} str
     * @return {string} processed string
     */
    var format = function (str) {
        var matcher = /(^|[^%])(%%)*(%[\-0]?[1-9]\d*|%)[doxs]/g,
            re = /(?:%)(-|0|)(\d*)([doxs])/,
            index = 0,
            i = 1,
            radix = { 'd': 10, 'o': 8, 'x': 16 },
            value = '',
            result,
            pieces,
            arg,
            len,
            space;

        while ((result = matcher.exec(str)) !== null) {
            value += str.substring(index, result.index);
            index = matcher.lastIndex;
            pieces = re.exec(result[0]);
            if (pieces[3] === 's') {
                arg = arguments[i];
            } else {
                arg = arguments[i].toString(radix[pieces[3]]);
            }
            len = parseInt(pieces[2], 10) - arg.length;
            space = [];
            if (len > 0) {
                space[len] = '';
            }
            switch (pieces[1]) {
            case '-':
                arg += space.join(' ');
                break;
            case '0':
                if (arguments[i] < 0) {
                    arg = '-' + space.join('0') + arg.substring(1);
                } else {
                    arg = space.join('0') + arg;
                }
                break;
            default:
                arg = space.join(' ') + arg;
                break;
            }
            value += result[0].replace(pieces[0], arg);
            i++;
        }
        value += str.substring(index);
//alert(value);
        return value.replace(/%%/g, '%');
    };

    // export
    if (typeof define === 'function' &&
            typeof module === 'object' && module.exports) {
        module.exports = format;
    } else {
        window.format = format;
    }

}());
<commit_msg>Delete comment<commit_after>/**
 * @preserve format.js v1.0.0 (c) 2013 knowledgecode | MIT licensed
 */
/*global define, module */
/*jslint browser: true, plusplus: true, regexp: true */
(function () {
    'use strict';

    /**
     * @name format
     * @function
     * @param {string} str
     * @return {string} processed string
     */
    var format = function (str) {
        var matcher = /(^|[^%])(%%)*(%[\-0]?[1-9]\d*|%)[doxs]/g,
            re = /(?:%)(-|0|)(\d*)([doxs])/,
            index = 0,
            i = 1,
            radix = { 'd': 10, 'o': 8, 'x': 16 },
            value = '',
            result,
            pieces,
            arg,
            len,
            space;

        while ((result = matcher.exec(str)) !== null) {
            value += str.substring(index, result.index);
            index = matcher.lastIndex;
            pieces = re.exec(result[0]);
            if (pieces[3] === 's') {
                arg = arguments[i];
            } else {
                arg = arguments[i].toString(radix[pieces[3]]);
            }
            len = parseInt(pieces[2], 10) - arg.length;
            space = [];
            if (len > 0) {
                space[len] = '';
            }
            switch (pieces[1]) {
            case '-':
                arg += space.join(' ');
                break;
            case '0':
                if (arguments[i] < 0) {
                    arg = '-' + space.join('0') + arg.substring(1);
                } else {
                    arg = space.join('0') + arg;
                }
                break;
            default:
                arg = space.join(' ') + arg;
                break;
            }
            value += result[0].replace(pieces[0], arg);
            i++;
        }
        value += str.substring(index);
        return value.replace(/%%/g, '%');
    };

    // export
    if (typeof define === 'function' &&
            typeof module === 'object' && module.exports) {
        module.exports = format;
    } else {
        window.format = format;
    }

}());
<|endoftext|>


<commit_before>import $ from 'jquery';
import Sortable from 'sortablejs';
import '../../utils/jquery-utils';

export default class CollectionsField {
    constructor() {
        this.lists = $();

        $('[data-type="collection"]').each((index, list) => this.addList(list));
        $('body').on('mutation._grav', this._onAddedNodes.bind(this));

    }

    addList(list) {
        list = $(list);
        this.lists = this.lists.add(list);

        list.on('click', '> .collection-actions [data-action="add"]', (event) => this.addItem(event));
        list.on('click', '> ul > li > .item-actions [data-action="delete"]', (event) => this.removeItem(event));

        list.find('[data-collection-holder]').each((index, container) => {
            container = $(container);
            if (container.data('collection-sort')) { return; }

            container.data('collection-sort', new Sortable(container.get(0), {
                forceFallback: false,
                animation: 150,
                filter: '.CodeMirror, .grav-editor-resizer',
                onUpdate: () => this.reindex(container)
            }));
        });
    }

    addItem(event) {
        let button = $(event.currentTarget);
        let list = button.closest('[data-type="collection"]');
        let template = $(list.find('> [data-collection-template="new"]').data('collection-template-html'));

        list.find('> [data-collection-holder]').append(template);
        this.reindex(list);
        // button.data('key-index', keyIndex + 1);

        // process markdown editors
        /* var field = template.find('[name]').filter('textarea');
        if (field.length && field.data('grav-mdeditor') && typeof MDEditors !== 'undefined') {
            MDEditors.add(field);
        }*/
    }

    removeItem(event) {
        let button = $(event.currentTarget);
        let item = button.closest('[data-collection-item]');
        let list = button.closest('[data-type="collection"]');

        item.remove();
        this.reindex(list);
    }

    reindex(list) {
        list = $(list).closest('[data-type="collection"]');

        let items = list.find('> ul > [data-collection-item]');

        items.each((index, item) => {
            item = $(item);
            item.attr('data-collection-key', index);

            ['name', 'data-grav-field-name', 'id', 'for'].forEach((prop) => {
                item.find('[' + prop + ']').each(function() {
                    let element = $(this);
                    let indexes = [];

                    element.parents('[data-collection-key]').map((idx, parent) => indexes.push($(parent).attr('data-collection-key')));
                    indexes.reverse();

                    let replaced = element.attr(prop).replace(/\[(\d+|\*)\]/g, (/* str, p1, offset */) => {
                        return `[${indexes.shift()}]`;
                    });

                    replaced = replaced.replace(/\.(\d+|\*)\./g, (/* str, p1, offset */) => {
                        return `.${indexes.shift()}.`;
                    });

                    element.attr(prop, replaced);
                });
            });
        });
    }

    _onAddedNodes(event, target/* , record, instance */) {
        let collections = $(target).find('[data-type="collection"]');
        if (!collections.length) { return; }

        collections.each((index, collection) => {
            collection = $(collection);
            if (!~this.lists.index(collection)) {
                this.addList(collection);
            }
        });
    }
}

export let Instance = new CollectionsField();
<commit_msg>Added logic to support non-sortable collections<commit_after>import $ from 'jquery';
import Sortable from 'sortablejs';
import '../../utils/jquery-utils';

export default class CollectionsField {
    constructor() {
        this.lists = $();

        $('[data-type="collection"]').each((index, list) => this.addList(list));
        $('body').on('mutation._grav', this._onAddedNodes.bind(this));

    }

    addList(list) {
        list = $(list);
        this.lists = this.lists.add(list);

        list.on('click', '> .collection-actions [data-action="add"]', (event) => this.addItem(event));
        list.on('click', '> ul > li > .item-actions [data-action="delete"]', (event) => this.removeItem(event));

        list.find('[data-collection-holder]').each((index, container) => {
            container = $(container);
            if (container.data('collection-sort') || container[0].hasAttribute('data-collection-nosort')) { return; }

            container.data('collection-sort', new Sortable(container.get(0), {
                forceFallback: false,
                animation: 150,
                filter: '.CodeMirror, .grav-editor-resizer',
                onUpdate: () => this.reindex(container)
            }));
        });
    }

    addItem(event) {
        let button = $(event.currentTarget);
        let list = button.closest('[data-type="collection"]');
        let template = $(list.find('> [data-collection-template="new"]').data('collection-template-html'));

        list.find('> [data-collection-holder]').append(template);
        this.reindex(list);
        // button.data('key-index', keyIndex + 1);

        // process markdown editors
        /* var field = template.find('[name]').filter('textarea');
        if (field.length && field.data('grav-mdeditor') && typeof MDEditors !== 'undefined') {
            MDEditors.add(field);
        }*/
    }

    removeItem(event) {
        let button = $(event.currentTarget);
        let item = button.closest('[data-collection-item]');
        let list = button.closest('[data-type="collection"]');

        item.remove();
        this.reindex(list);
    }

    reindex(list) {
        list = $(list).closest('[data-type="collection"]');

        let items = list.find('> ul > [data-collection-item]');

        items.each((index, item) => {
            item = $(item);
            item.attr('data-collection-key', index);

            ['name', 'data-grav-field-name', 'id', 'for'].forEach((prop) => {
                item.find('[' + prop + ']').each(function() {
                    let element = $(this);
                    let indexes = [];

                    element.parents('[data-collection-key]').map((idx, parent) => indexes.push($(parent).attr('data-collection-key')));
                    indexes.reverse();

                    let replaced = element.attr(prop).replace(/\[(\d+|\*)\]/g, (/* str, p1, offset */) => {
                        return `[${indexes.shift()}]`;
                    });

                    replaced = replaced.replace(/\.(\d+|\*)\./g, (/* str, p1, offset */) => {
                        return `.${indexes.shift()}.`;
                    });

                    element.attr(prop, replaced);
                });
            });
        });
    }

    _onAddedNodes(event, target/* , record, instance */) {
        let collections = $(target).find('[data-type="collection"]');
        if (!collections.length) { return; }

        collections.each((index, collection) => {
            collection = $(collection);
            if (!~this.lists.index(collection)) {
                this.addList(collection);
            }
        });
    }
}

export let Instance = new CollectionsField();
<|endoftext|>


<commit_before><commit_msg>apps.administrator...ManuallyCreateUsers: Autoscroll dup msg.<commit_after><|endoftext|>