<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.enabled", false);
user_pref("media.peerconnection.identity.timeout", 1);
user_pref("media.peerconnection.turn.disable", true);
user_pref("media.navigator.video.enabled", false);

// Improve WebRTC IP leak issue
user_pref("media.peerconnection.ice.default_address_only", true);
user_pref("media.peerconnection.ice.no_host", 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 web notifications
user_pref("dom.webnotifications.enabled", false);
user_pref("dom.webnotifications.serviceworker.enabled", false);

// 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");

// Lock down allowed extension directories
user_pref("extensions.enabledScopes", 1);
user_pref("extensions.autoDisableScopes", 15);

// Clear localStorage and UUID when a WebExtension is uninstalled
user_pref("extensions.webextensions.keepStorageOnUninstall", false);
user_pref("extensions.webextensions.keepUuidOnUninstall", false);

// 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 GMP (Gecko Media Plugins)
user_pref("media.gmp-provider.enabled", false);
user_pref("media.gmp.trial-create.enabled", false);

// Disable widevine CDM
user_pref("media.gmp-widevinecdm.visible", false);
user_pref("media.gmp-widevinecdm.enabled", false);
user_pref("media.gmp-widevinecdm.autoupdate", false);

// 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-gmpopenh264.autoupdate", 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);
user_pref("media.gmp-eme-adobe.visible", false);
user_pref("media.gmp-eme-adobe.autoupdate", 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);
user_pref("browser.uitour.url", "");

// 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>Remove outdated Firefox preferences<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.enabled", false);
user_pref("media.peerconnection.identity.timeout", 1);
user_pref("media.peerconnection.turn.disable", true);
user_pref("media.navigator.video.enabled", false);

// Improve WebRTC IP leak issue
user_pref("media.peerconnection.ice.default_address_only", true);
user_pref("media.peerconnection.ice.no_host", 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.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 web notifications
user_pref("dom.webnotifications.enabled", false);
user_pref("dom.webnotifications.serviceworker.enabled", false);

// Disable SPDY (SPDY would be deprecated in 2016)
user_pref("network.http.spdy.enabled", 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");

// Lock down allowed extension directories
user_pref("extensions.enabledScopes", 1);
user_pref("extensions.autoDisableScopes", 15);

// Clear localStorage and UUID when a WebExtension is uninstalled
user_pref("extensions.webextensions.keepStorageOnUninstall", false);
user_pref("extensions.webextensions.keepUuidOnUninstall", false);

// 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 GMP (Gecko Media Plugins)
user_pref("media.gmp-provider.enabled", false);
user_pref("media.gmp.trial-create.enabled", false);

// Disable widevine CDM
user_pref("media.gmp-widevinecdm.visible", false);
user_pref("media.gmp-widevinecdm.enabled", false);
user_pref("media.gmp-widevinecdm.autoupdate", false);

// 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-gmpopenh264.autoupdate", 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);
user_pref("media.gmp-eme-adobe.visible", false);
user_pref("media.gmp-eme-adobe.autoupdate", false);

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

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

// 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);

// 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 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);
user_pref("browser.uitour.url", "");

// 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>'use strict';

/****************************************************************************
* Initial setup
****************************************************************************/

// var configuration = {
//   'iceServers': [{
//     'urls': 'stun:stun.l.google.com:19302'
//   }]
// };

var configuration = null;

// var roomURL = document.getElementById('url');
var video = document.querySelector('video');
var photo = document.getElementById('photo');
var photoContext = photo.getContext('2d');
var trail = document.getElementById('trail');
var snapBtn = document.getElementById('snap');
var sendBtn = document.getElementById('send');
var snapAndSendBtn = document.getElementById('snapAndSend');

var photoContextW;
var photoContextH;

// Attach event handlers
snapBtn.addEventListener('click', snapPhoto);
sendBtn.addEventListener('click', sendPhoto);
snapAndSendBtn.addEventListener('click', snapAndSend);

// Disable send buttons by default.
sendBtn.disabled = true;
snapAndSendBtn.disabled = true;

// Create a random room if not already present in the URL.
var isInitiator;
var room = window.location.hash.substring(1);
if (!room) {
  room = window.location.hash = randomToken();
}

/****************************************************************************
* Signaling server
****************************************************************************/

// Connect to the signaling server
var socket = io.connect();

socket.on('ipaddr', function(ipaddr) {
  console.log('Server IP address is: ' + ipaddr);
  // updateRoomURL(ipaddr);
});

socket.on('created', function(room, clientId) {
  console.log('Created room', room, '- my client ID is', clientId);
  isInitiator = true;
  grabWebCamVideo();
});

socket.on('joined', function(room, clientId) {
  console.log('This peer has joined room', room, 'with client ID', clientId);
  isInitiator = false;
  createPeerConnection(isInitiator, configuration);
  grabWebCamVideo();
});

socket.on('full', function(room) {
  alert('Room ' + room + ' is full. We will create a new room for you.');
  window.location.hash = '';
  window.location.reload();
});

socket.on('ready', function() {
  console.log('Socket is ready');
  createPeerConnection(isInitiator, configuration);
});

socket.on('log', function(array) {
  console.log.apply(console, array);
});

socket.on('message', function(message) {
  console.log('Client received message:', message);
  signalingMessageCallback(message);
});

// Joining a room.
socket.emit('create or join', room);

if (location.hostname.match(/localhost|127\.0\.0/)) {
  socket.emit('ipaddr');
}

// Leaving rooms and disconnecting from peers.
socket.on('disconnect', function(reason) {
  console.log(`Disconnected: ${reason}.`);
  sendBtn.disabled = true;
  snapAndSendBtn.disabled = true;
});

socket.on('bye', function(room) {
  console.log(`Peer leaving room ${room}.`);
  sendBtn.disabled = true;
  snapAndSendBtn.disabled = true;
  // If peer did not create the room, re-enter to be creator.
  if (!isInitiator) {
    window.location.reload();
  }
});

window.addEventListener('unload', function() {
  console.log(`Unloading window. Notifying peers in ${room}.`);
  socket.emit('bye', room);
});

/**
* Send message to signaling server
*/
function sendMessage(message) {
  console.log('Client sending message: ', message);
  socket.emit('message', message);
}

/**
* Updates URL on the page so that users can copy&paste it to their peers.
*/
// function updateRoomURL(ipaddr) {
//   var url;
//   if (!ipaddr) {
//     url = location.href;
//   } else {
//     url = location.protocol + '//' + ipaddr + ':2013/#' + room;
//   }
//   roomURL.innerHTML = url;
// }

/****************************************************************************
* User media (webcam)
****************************************************************************/

function grabWebCamVideo() {
  console.log('Getting user media (video) ...');
  navigator.mediaDevices.getUserMedia({
    audio: false,
    video: true
  })
  .then(gotStream)
  .catch(function(e) {
    alert('getUserMedia() error: ' + e.name);
  });
}

function gotStream(stream) {
  console.log('getUserMedia video stream URL:', stream);
  window.stream = stream; // stream available to console
  video.srcObject = stream;
  video.onloadedmetadata = function() {
    photo.width = photoContextW = video.videoWidth;
    photo.height = photoContextH = video.videoHeight;
    console.log('gotStream with width and height:', photoContextW, photoContextH);
  };
  show(snapBtn);
}

/****************************************************************************
* WebRTC peer connection and data channel
****************************************************************************/

var peerConn;
var dataChannel;

function signalingMessageCallback(message) {
  if (message.type === 'offer') {
    console.log('Got offer. Sending answer to peer.');
    peerConn.setRemoteDescription(new RTCSessionDescription(message), function() {},
                                  logError);
    peerConn.createAnswer(onLocalSessionCreated, logError);

  } else if (message.type === 'answer') {
    console.log('Got answer.');
    peerConn.setRemoteDescription(new RTCSessionDescription(message), function() {},
                                  logError);

  } else if (message.type === 'candidate') {
    peerConn.addIceCandidate(new RTCIceCandidate({
      candidate: message.candidate,
      sdpMLineIndex: message.label,
      sdpMid: message.id
    }));

  }
}

function createPeerConnection(isInitiator, config) {
  console.log('Creating Peer connection as initiator?', isInitiator, 'config:',
              config);
  peerConn = new RTCPeerConnection(config);

// send any ice candidates to the other peer
peerConn.onicecandidate = function(event) {
  console.log('icecandidate event:', event);
  if (event.candidate) {
    sendMessage({
      type: 'candidate',
      label: event.candidate.sdpMLineIndex,
      id: event.candidate.sdpMid,
      candidate: event.candidate.candidate
    });
  } else {
    console.log('End of candidates.');
  }
};

if (isInitiator) {
  console.log('Creating Data Channel');
  dataChannel = peerConn.createDataChannel('photos');
  onDataChannelCreated(dataChannel);

  console.log('Creating an offer');
  peerConn.createOffer(onLocalSessionCreated, logError);
} else {
  peerConn.ondatachannel = function(event) {
    console.log('ondatachannel:', event.channel);
    dataChannel = event.channel;
    onDataChannelCreated(dataChannel);
  };
}
}

function onLocalSessionCreated(desc) {
  console.log('local session created:', desc);
  peerConn.setLocalDescription(desc, function() {
    console.log('sending local desc:', peerConn.localDescription);
    sendMessage(peerConn.localDescription);
  }, logError);
}

function onDataChannelCreated(channel) {
  console.log('onDataChannelCreated:', channel);

  channel.onopen = function() {
    console.log('CHANNEL opened!!!');
    sendBtn.disabled = false;
    snapAndSendBtn.disabled = false;
  };

  channel.onclose = function () {
    console.log('Channel closed.');
    sendBtn.disabled = true;
    snapAndSendBtn.disabled = true;
  }

  channel.onmessage = (adapter.browserDetails.browser === 'firefox') ?
  receiveDataFirefoxFactory() : receiveDataChromeFactory();
}

function receiveDataChromeFactory() {
  var buf, count;

  return function onmessage(event) {
    if (typeof event.data === 'string') {
      buf = window.buf = new Uint8ClampedArray(parseInt(event.data));
      count = 0;
      console.log('Expecting a total of ' + buf.byteLength + ' bytes');
      return;
    }

    var data = new Uint8ClampedArray(event.data);
    buf.set(data, count);

    count += data.byteLength;
    console.log('count: ' + count);

    if (count === buf.byteLength) {
// we're done: all data chunks have been received
console.log('Done. Rendering photo.');
renderPhoto(buf);
}
};
}

function receiveDataFirefoxFactory() {
  var count, total, parts;

  return function onmessage(event) {
    if (typeof event.data === 'string') {
      total = parseInt(event.data);
      parts = [];
      count = 0;
      console.log('Expecting a total of ' + total + ' bytes');
      return;
    }

    parts.push(event.data);
    count += event.data.size;
    console.log('Got ' + event.data.size + ' byte(s), ' + (total - count) +
                ' to go.');

    if (count === total) {
      console.log('Assembling payload');
      var buf = new Uint8ClampedArray(total);
      var compose = function(i, pos) {
        var reader = new FileReader();
        reader.onload = function() {
          buf.set(new Uint8ClampedArray(this.result), pos);
          if (i + 1 === parts.length) {
            console.log('Done. Rendering photo.');
            renderPhoto(buf);
          } else {
            compose(i + 1, pos + this.result.byteLength);
          }
        };
        reader.readAsArrayBuffer(parts[i]);
      };
      compose(0, 0);
    }
  };
}

/****************************************************************************
* Aux functions, mostly UI-related
****************************************************************************/

function snapPhoto() {
  photoContext.drawImage(video, 0, 0, photo.width, photo.height);
  show(photo, sendBtn);
}

function sendPhoto() {
// Split data channel message in chunks of this byte length.
var CHUNK_LEN = 64000;
console.log('width and height ', photoContextW, photoContextH);
var img = photoContext.getImageData(0, 0, photoContextW, photoContextH),
len = img.data.byteLength,
n = len / CHUNK_LEN | 0;

console.log('Sending a total of ' + len + ' byte(s)');

if (!dataChannel) {
  logError('Connection has not been initiated. ' +
    'Get two peers in the same room first');
  return;
} else if (dataChannel.readyState === 'closed') {
  logError('Connection was lost. Peer closed the connection.');
  return;
}

dataChannel.send(len);

// split the photo and send in chunks of about 64KB
for (var i = 0; i < n; i++) {
  var start = i * CHUNK_LEN,
  end = (i + 1) * CHUNK_LEN;
  console.log(start + ' - ' + (end - 1));
  dataChannel.send(img.data.subarray(start, end));
}

// send the reminder, if any
if (len % CHUNK_LEN) {
  console.log('last ' + len % CHUNK_LEN + ' byte(s)');
  dataChannel.send(img.data.subarray(n * CHUNK_LEN));
}
}

function snapAndSend() {
  snapPhoto();
  sendPhoto();
}

function renderPhoto(data) {
  var canvas = document.createElement('canvas');
  canvas.width = photoContextW;
  canvas.height = photoContextH;
  canvas.classList.add('incomingPhoto');
  // trail is the element holding the incoming images
  trail.insertBefore(canvas, trail.firstChild);

  var context = canvas.getContext('2d');
  var img = context.createImageData(photoContextW, photoContextH);
  img.data.set(data);
  context.putImageData(img, 0, 0);
}

function show() {
  Array.prototype.forEach.call(arguments, function(elem) {
    elem.style.display = null;
  });
}

function hide() {
  Array.prototype.forEach.call(arguments, function(elem) {
    elem.style.display = 'none';
  });
}

function randomToken() {
  return Math.floor((1 + Math.random()) * 1e16).toString(16).substring(1);
}

function logError(err) {
  if (!err) return;
  if (typeof err === 'string') {
    console.warn(err);
  } else {
    console.warn(err.toString(), err);
  }
}
<commit_msg>Updated createOffer to use promises instead of callbacks as they were deprecated. Fixes sending first offer null description.<commit_after>'use strict';

/****************************************************************************
* Initial setup
****************************************************************************/

// var configuration = {
//   'iceServers': [{
//     'urls': 'stun:stun.l.google.com:19302'
//   }]
// };

var configuration = null;

// var roomURL = document.getElementById('url');
var video = document.querySelector('video');
var photo = document.getElementById('photo');
var photoContext = photo.getContext('2d');
var trail = document.getElementById('trail');
var snapBtn = document.getElementById('snap');
var sendBtn = document.getElementById('send');
var snapAndSendBtn = document.getElementById('snapAndSend');

var photoContextW;
var photoContextH;

// Attach event handlers
snapBtn.addEventListener('click', snapPhoto);
sendBtn.addEventListener('click', sendPhoto);
snapAndSendBtn.addEventListener('click', snapAndSend);

// Disable send buttons by default.
sendBtn.disabled = true;
snapAndSendBtn.disabled = true;

// Create a random room if not already present in the URL.
var isInitiator;
var room = window.location.hash.substring(1);
if (!room) {
  room = window.location.hash = randomToken();
}

/****************************************************************************
* Signaling server
****************************************************************************/

// Connect to the signaling server
var socket = io.connect();

socket.on('ipaddr', function(ipaddr) {
  console.log('Server IP address is: ' + ipaddr);
  // updateRoomURL(ipaddr);
});

socket.on('created', function(room, clientId) {
  console.log('Created room', room, '- my client ID is', clientId);
  isInitiator = true;
  grabWebCamVideo();
});

socket.on('joined', function(room, clientId) {
  console.log('This peer has joined room', room, 'with client ID', clientId);
  isInitiator = false;
  createPeerConnection(isInitiator, configuration);
  grabWebCamVideo();
});

socket.on('full', function(room) {
  alert('Room ' + room + ' is full. We will create a new room for you.');
  window.location.hash = '';
  window.location.reload();
});

socket.on('ready', function() {
  console.log('Socket is ready');
  createPeerConnection(isInitiator, configuration);
});

socket.on('log', function(array) {
  console.log.apply(console, array);
});

socket.on('message', function(message) {
  console.log('Client received message:', message);
  signalingMessageCallback(message);
});

// Joining a room.
socket.emit('create or join', room);

if (location.hostname.match(/localhost|127\.0\.0/)) {
  socket.emit('ipaddr');
}

// Leaving rooms and disconnecting from peers.
socket.on('disconnect', function(reason) {
  console.log(`Disconnected: ${reason}.`);
  sendBtn.disabled = true;
  snapAndSendBtn.disabled = true;
});

socket.on('bye', function(room) {
  console.log(`Peer leaving room ${room}.`);
  sendBtn.disabled = true;
  snapAndSendBtn.disabled = true;
  // If peer did not create the room, re-enter to be creator.
  if (!isInitiator) {
    window.location.reload();
  }
});

window.addEventListener('unload', function() {
  console.log(`Unloading window. Notifying peers in ${room}.`);
  socket.emit('bye', room);
});

/**
* Send message to signaling server
*/
function sendMessage(message) {
  console.log('Client sending message: ', message);
  socket.emit('message', message);
}

/**
* Updates URL on the page so that users can copy&paste it to their peers.
*/
// function updateRoomURL(ipaddr) {
//   var url;
//   if (!ipaddr) {
//     url = location.href;
//   } else {
//     url = location.protocol + '//' + ipaddr + ':2013/#' + room;
//   }
//   roomURL.innerHTML = url;
// }

/****************************************************************************
* User media (webcam)
****************************************************************************/

function grabWebCamVideo() {
  console.log('Getting user media (video) ...');
  navigator.mediaDevices.getUserMedia({
    audio: false,
    video: true
  })
  .then(gotStream)
  .catch(function(e) {
    alert('getUserMedia() error: ' + e.name);
  });
}

function gotStream(stream) {
  console.log('getUserMedia video stream URL:', stream);
  window.stream = stream; // stream available to console
  video.srcObject = stream;
  video.onloadedmetadata = function() {
    photo.width = photoContextW = video.videoWidth;
    photo.height = photoContextH = video.videoHeight;
    console.log('gotStream with width and height:', photoContextW, photoContextH);
  };
  show(snapBtn);
}

/****************************************************************************
* WebRTC peer connection and data channel
****************************************************************************/

var peerConn;
var dataChannel;

function signalingMessageCallback(message) {
  if (message.type === 'offer') {
    console.log('Got offer. Sending answer to peer.');
    peerConn.setRemoteDescription(new RTCSessionDescription(message), function() {},
                                  logError);
    peerConn.createAnswer(onLocalSessionCreated, logError);

  } else if (message.type === 'answer') {
    console.log('Got answer.');
    peerConn.setRemoteDescription(new RTCSessionDescription(message), function() {},
                                  logError);

  } else if (message.type === 'candidate') {
    peerConn.addIceCandidate(new RTCIceCandidate({
      candidate: message.candidate,
      sdpMLineIndex: message.label,
      sdpMid: message.id
    }));

  }
}

function createPeerConnection(isInitiator, config) {
  console.log('Creating Peer connection as initiator?', isInitiator, 'config:',
              config);
  peerConn = new RTCPeerConnection(config);

// send any ice candidates to the other peer
peerConn.onicecandidate = function(event) {
  console.log('icecandidate event:', event);
  if (event.candidate) {
    sendMessage({
      type: 'candidate',
      label: event.candidate.sdpMLineIndex,
      id: event.candidate.sdpMid,
      candidate: event.candidate.candidate
    });
  } else {
    console.log('End of candidates.');
  }
};

if (isInitiator) {
  console.log('Creating Data Channel');
  dataChannel = peerConn.createDataChannel('photos');
  onDataChannelCreated(dataChannel);

  console.log('Creating an offer');
  peerConn.createOffer().then(function(offer) {
    return peerConn.setLocalDescription(offer);
  })
  .then(() => {
    console.log('sending local desc:', peerConn.localDescription);
    sendMessage(peerConn.localDescription);
  })
  .catch(logError);

} else {
  peerConn.ondatachannel = function(event) {
    console.log('ondatachannel:', event.channel);
    dataChannel = event.channel;
    onDataChannelCreated(dataChannel);
  };
}
}

function onLocalSessionCreated(desc) {
  console.log('local session created:', desc);
  peerConn.setLocalDescription(desc).then(function() {
    console.log('sending local desc:', peerConn.localDescription);
    sendMessage(peerConn.localDescription);
  }).catch(logError);
}

function onDataChannelCreated(channel) {
  console.log('onDataChannelCreated:', channel);

  channel.onopen = function() {
    console.log('CHANNEL opened!!!');
    sendBtn.disabled = false;
    snapAndSendBtn.disabled = false;
  };

  channel.onclose = function () {
    console.log('Channel closed.');
    sendBtn.disabled = true;
    snapAndSendBtn.disabled = true;
  }

  channel.onmessage = (adapter.browserDetails.browser === 'firefox') ?
  receiveDataFirefoxFactory() : receiveDataChromeFactory();
}

function receiveDataChromeFactory() {
  var buf, count;

  return function onmessage(event) {
    if (typeof event.data === 'string') {
      buf = window.buf = new Uint8ClampedArray(parseInt(event.data));
      count = 0;
      console.log('Expecting a total of ' + buf.byteLength + ' bytes');
      return;
    }

    var data = new Uint8ClampedArray(event.data);
    buf.set(data, count);

    count += data.byteLength;
    console.log('count: ' + count);

    if (count === buf.byteLength) {
// we're done: all data chunks have been received
console.log('Done. Rendering photo.');
renderPhoto(buf);
}
};
}

function receiveDataFirefoxFactory() {
  var count, total, parts;

  return function onmessage(event) {
    if (typeof event.data === 'string') {
      total = parseInt(event.data);
      parts = [];
      count = 0;
      console.log('Expecting a total of ' + total + ' bytes');
      return;
    }

    parts.push(event.data);
    count += event.data.size;
    console.log('Got ' + event.data.size + ' byte(s), ' + (total - count) +
                ' to go.');

    if (count === total) {
      console.log('Assembling payload');
      var buf = new Uint8ClampedArray(total);
      var compose = function(i, pos) {
        var reader = new FileReader();
        reader.onload = function() {
          buf.set(new Uint8ClampedArray(this.result), pos);
          if (i + 1 === parts.length) {
            console.log('Done. Rendering photo.');
            renderPhoto(buf);
          } else {
            compose(i + 1, pos + this.result.byteLength);
          }
        };
        reader.readAsArrayBuffer(parts[i]);
      };
      compose(0, 0);
    }
  };
}

/****************************************************************************
* Aux functions, mostly UI-related
****************************************************************************/

function snapPhoto() {
  photoContext.drawImage(video, 0, 0, photo.width, photo.height);
  show(photo, sendBtn);
}

function sendPhoto() {
// Split data channel message in chunks of this byte length.
var CHUNK_LEN = 64000;
console.log('width and height ', photoContextW, photoContextH);
var img = photoContext.getImageData(0, 0, photoContextW, photoContextH),
len = img.data.byteLength,
n = len / CHUNK_LEN | 0;

console.log('Sending a total of ' + len + ' byte(s)');

if (!dataChannel) {
  logError('Connection has not been initiated. ' +
    'Get two peers in the same room first');
  return;
} else if (dataChannel.readyState === 'closed') {
  logError('Connection was lost. Peer closed the connection.');
  return;
}

dataChannel.send(len);

// split the photo and send in chunks of about 64KB
for (var i = 0; i < n; i++) {
  var start = i * CHUNK_LEN,
  end = (i + 1) * CHUNK_LEN;
  console.log(start + ' - ' + (end - 1));
  dataChannel.send(img.data.subarray(start, end));
}

// send the reminder, if any
if (len % CHUNK_LEN) {
  console.log('last ' + len % CHUNK_LEN + ' byte(s)');
  dataChannel.send(img.data.subarray(n * CHUNK_LEN));
}
}

function snapAndSend() {
  snapPhoto();
  sendPhoto();
}

function renderPhoto(data) {
  var canvas = document.createElement('canvas');
  canvas.width = photoContextW;
  canvas.height = photoContextH;
  canvas.classList.add('incomingPhoto');
  // trail is the element holding the incoming images
  trail.insertBefore(canvas, trail.firstChild);

  var context = canvas.getContext('2d');
  var img = context.createImageData(photoContextW, photoContextH);
  img.data.set(data);
  context.putImageData(img, 0, 0);
}

function show() {
  Array.prototype.forEach.call(arguments, function(elem) {
    elem.style.display = null;
  });
}

function hide() {
  Array.prototype.forEach.call(arguments, function(elem) {
    elem.style.display = 'none';
  });
}

function randomToken() {
  return Math.floor((1 + Math.random()) * 1e16).toString(16).substring(1);
}

function logError(err) {
  if (!err) return;
  if (typeof err === 'string') {
    console.warn(err);
  } else {
    console.warn(err.toString(), err);
  }
}
<|endoftext|>


<commit_before>// author: Sandeep Mogla

angular.module('starter.controllers', ['jett.ionic.filter.bar', 'ngTagsInput', 'ngCordova'])

.controller('AppCtrl', function($scope, $ionicModal, $timeout, $ionicFilterBar, $ionicSideMenuDelegate) {

    // With the new view caching in Ionic, Controllers are only called
    // when they are recreated or on app start, instead of every page change.
    // To listen for when this page is active (for example, to refresh data),
    // listen for the $ionicView.enter event:
    //$scope.$on('$ionicView.enter', function(e) {
    //
    //});

    // Create the login modal that we will use later
    //    $ionicModal.fromTemplateUrl('templates/login.html', {
    //        scope: $scope
    //    }).then(function(modal) {
    //        $scope.modal = modal;
    //    });

    $scope.showFilterBar = function() {
        filterBarInstance = $ionicFilterBar.show({
            items: $scope.feeds,
            update: function(filteredItems) {
                $scope.feeds = filteredItems;
            },
            //filterProperties: 'description'
        });
    };

    $scope.showMenus = function() {
        console.log("showMenus");
        $ionicSideMenuDelegate.toggleLeft();
    };

})

.controller('NotificationsCtrl', function($scope, $http, $timeout, $templateCache, $cordovaNetwork) {
    $scope.feeds = [];
    $scope.groups = ["Test"];

    //$scope.method = 'JSONP';-
    //$scope.url = 'http://localhost:8081/api/listJobs?callback=JSON_CALLBACK';

    $scope.url = 'http://nodejs-clientfeeds.rhcloud.com/api/all';
    $scope.response = null;

    //$scope.fetch = function() {
    //    $http({
    //        method: $scope.method,
    //        url: $scope.url,
    //        cache: $templateCache
    //    }).

    var promise = $http.get($scope.url);

    promise.success(function(data, status, headers, config) {
        // getting device infor from $cordovaDevice
        $scope.feeds = data.data;
        console.log("success");
    });

    promise.error(function(data, status, headers, config) {
        alert("error" + data + " s : " + status);
        console.log("error");
    });

    //    then(function(response) {
    //        $scope.status = response.status;
    //        $scope.data = response.data;
    //    }, function(response) {
    //        $scope.data = response.data || "Request failed";
    //        $scope.status = response.status;
    //    });
    //};

    $scope.doRefresh = function() {
        console.log('Refreshing!');

        $timeout(function() {
            //simulate async response
            $scope.feeds.push('Feed ' + Math.floor(Math.random() * 1000) + 4);

            //Stop the ion-refresher from spinning
            $scope.$broadcast('scroll.refreshComplete');

        }, 1000);

    }

})

.controller('DetailCtrl', function($scope, $stateParams) {})

.controller('SignUpCtrl', function($scope, $stateParams) {

})

.controller('SettingsCtrl', function($scope, $stateParams) {
    var tagsData = [{
        id: 1,
        tag: 'Apple'
    }, {
        id: 2,
        tag: 'Banana'
    }, {
        id: 3,
        tag: 'Cherry'
    }, {
        id: 4,
        tag: 'Cantelope'
    }, {
        id: 5,
        tag: 'Grapefruit'
    }, {
        id: 6,
        tag: 'Grapes',
        selected: true
    }, {
        id: 7,
        tag: 'Lemon'
    }, {
        id: 8,
        tag: 'Lime'
    }, {
        id: 9,
        tag: 'Melon',
        selected: true
    }, {
        id: 10,
        tag: 'Orange'
    }, {
        id: 11,
        tag: 'Strawberry'
    }, {
        id: 11,
        tag: 'Watermelon'
    }];

    $scope.items = tagsData;

    $scope.tags = [{
        text: 'Tag1'
    }, {
        text: 'Tag2'
    }, {
        text: 'Tag3'
    }];

})

.controller('HomeCtrl', function($scope, $stateParams) {

})

.controller('FavouritesCtrl', function($scope, $stateParams) {

})

.controller('LoginCtrl', function($scope, $timeout, $stateParams, $location, $ionicModal) {
    // Form data for the login modal
    $scope.loginData = {};

    // Triggered in the login modal to close it
    $scope.closeLogin = function() {
        //$scope.modal.hide();
    };

    // Perform the login action when the user submits the login form
    $scope.doLogin = function() {
        console.log('Doing login', $scope.loginData);

        // Simulate a login delay. Remove this and replace with your login
        // code if using a login system
        $timeout(function() {
            $scope.closeLogin();
            $location.path("/app/notifications");
        }, 1000);
    };
})

.service('LoginService', function($q) {
    return {
        loginUser: function(name, pw) {
            var deferred = $q.defer();
            var promise = deferred.promise;

            if (name == 'user' && pw == 'secret') {
                deferred.resolve('Welcome ' + name + '!');
            } else {
                deferred.reject('Wrong credentials.');
            }
            promise.success = function(fn) {
                promise.then(fn);
                return promise;
            }
            promise.error = function(fn) {
                promise.then(null, fn);
                return promise;
            }
            return promise;
        }
    }
});<commit_msg>updated controller to add dummy feed at top to simulate top adding<commit_after>// author: Sandeep Mogla

angular.module('starter.controllers', ['jett.ionic.filter.bar', 'ngTagsInput', 'ngCordova'])

.controller('AppCtrl', function($scope, $ionicModal, $timeout, $ionicFilterBar, $ionicSideMenuDelegate, NotifyDataService) {

  // With the new view caching in Ionic, Controllers are only called
  // when they are recreated or on app start, instead of every page change.
  // To listen for when this page is active (for example, to refresh data),
  // listen for the $ionicView.enter event:
  //$scope.$on('$ionicView.enter', function(e) {
  //
  //});

  // Create the login modal that we will use later
  //    $ionicModal.fromTemplateUrl('templates/login.html', {
  //        scope: $scope
  //    }).then(function(modal) {
  //        $scope.modal = modal;
  //    });

  $scope.$on('NotificationEvent', function(args) {
    $scope.newNts = NotifyDataService.getNewNotifications();
  });

  $scope.showFilterBar = function() {
    filterBarInstance = $ionicFilterBar.show({
      items: $scope.feeds,
      update: function(filteredItems) {
        $scope.feeds = filteredItems;
      },
      //filterProperties: 'description'
    });
  };

  $scope.showMenus = function() {
    console.log("showMenus");
    $ionicSideMenuDelegate.toggleLeft();
  };

})

.controller('NotificationsCtrl', function($scope, $http, $timeout, $templateCache, $cordovaNetwork, NotifyDataService) {
  $scope.feeds = [];
  $scope.groups = ["Test"];

  //$scope.method = 'JSONP';-
  //$scope.url = 'http://localhost:8081/api/listJobs?callback=JSON_CALLBACK';

  $scope.url = 'http://nodejs-clientfeeds.rhcloud.com/api/all';
  $scope.response = null;

  var promise = $http.get($scope.url);

  promise.success(function(data, status, headers, config) {
    $scope.feeds = data.data;
    if (data.data != null && data.data.hasOwnProperty('length')) {
      NotifyDataService.setNewNotifications(data.data.length);
      console.log("NotifyDataService.getNewNotifications() :: " + NotifyDataService.getNewNotifications());
      $scope.$emit('NotificationEvent', NotifyDataService.getNewNotifications());
    }
    console.log("success");
  });

  promise.error(function(data, status, headers, config) {
    alert("error" + data + " s : " + status);
    console.log("error");
  });

  //    then(function(response) {
  //        $scope.status = response.status;
  //        $scope.data = response.data;
  //    }, function(response) {
  //        $scope.data = response.data || "Request failed";
  //        $scope.status = response.status;
  //    });
  //};

  function prepend(value, array) {
    var newArray = array.slice(0);
    newArray.unshift(value);
    return newArray;
  }

  $scope.doRefresh = function() {
    console.log('Refreshing!');

    $timeout(function() {

      var _newFeeds = prepend('Feed ' + Math.floor(Math.random() * 1000) + 4, $scope.feeds);

      //simulate async response
      $scope.feeds = _newFeeds;

      //Stop the ion-refresher from spinning
      $scope.$broadcast('scroll.refreshComplete');

    }, 1000);

  }

})

.controller('DetailCtrl', function($scope, $stateParams) {})

.controller('SignUpCtrl', function($scope, $stateParams) {

})

.controller('SettingsCtrl', function($scope, $stateParams) {
  var tagsData = [{
    id: 1,
    tag: 'Apple'
  }, {
    id: 2,
    tag: 'Banana'
  }, {
    id: 3,
    tag: 'Cherry'
  }, {
    id: 4,
    tag: 'Cantelope'
  }, {
    id: 5,
    tag: 'Grapefruit'
  }, {
    id: 6,
    tag: 'Grapes',
    selected: true
  }, {
    id: 7,
    tag: 'Lemon'
  }, {
    id: 8,
    tag: 'Lime'
  }, {
    id: 9,
    tag: 'Melon',
    selected: true
  }, {
    id: 10,
    tag: 'Orange'
  }, {
    id: 11,
    tag: 'Strawberry'
  }, {
    id: 11,
    tag: 'Watermelon'
  }];

  $scope.items = tagsData;

  $scope.tags = [{
    text: 'Tag1'
  }, {
    text: 'Tag2'
  }, {
    text: 'Tag3'
  }];

})

.controller('HomeCtrl', function($scope, $stateParams) {

})

.controller('FavouritesCtrl', function($scope, $stateParams) {

})

.controller('LoginCtrl', function($scope, $timeout, $stateParams, $location, $ionicModal) {
  // Form data for the login modal
  $scope.loginData = {};

  // Triggered in the login modal to close it
  $scope.closeLogin = function() {
    //$scope.modal.hide();
  };

  // Perform the login action when the user submits the login form
  $scope.doLogin = function() {
    console.log('Doing login', $scope.loginData);

    // Simulate a login delay. Remove this and replace with your login
    // code if using a login system
    $timeout(function() {
      $scope.closeLogin();
      $location.path("/app/notifications");
    }, 1000);
  };
})

.service('LoginService', function($q) {
  return {
    loginUser: function(name, pw) {
      var deferred = $q.defer();
      var promise = deferred.promise;

      if (name == 'user' && pw == 'secret') {
        deferred.resolve('Welcome ' + name + '!');
      } else {
        deferred.reject('Wrong credentials.');
      }
      promise.success = function(fn) {
        promise.then(fn);
        return promise;
      }
      promise.error = function(fn) {
        promise.then(null, fn);
        return promise;
      }
      return promise;
    }
  }
})

.service('NotifyDataService', function() {
  var _newNotifications = 0;

  return {
    getNewNotifications: function() {
      return _newNotifications;
    },
    setNewNotifications: function(value) {
      _newNotifications = value;
    }
  };
});
<|endoftext|>


<commit_before>'use strict';
var util = require('util');
var yeoman = require('yeoman-generator');
var path = require("path");
var fs = require("fs");
var scriptBase = require('../script-base');

var Generator = module.exports = function Generator(args, options, config) {
    scriptBase.apply(this, arguments);
    this.setUpPaths();
    this._isInModule();
};

util.inherits(Generator, scriptBase);

Generator.prototype.createStructure = function createStructure() {
    this.mkdir("assets");
    this.mkdir("assets/skins");
    this.mkdir("assets/skins/sam");
    this.template("assets/skins/sam/moduleName-skin.css", "assets/skins/sam/" + this.moduleName + "-skin.css");
    this.template("assets/moduleName-core.css", "assets/" + this.moduleName + "-core.css");
};

Generator.prototype.updateMeta = function updateMeta() {
    var metaFile = JSON.parse(this.readFileAsString(this.metaFilePath));
    metaFile[this.moduleName].skinnable = true;
    this.write(this.metaFilePath, JSON.stringify(metaFile, null, 4));
};

Generator.prototype.advices = function advices() {
    console.log("Please...");
    console.log("shift your module");
    console.log("shift your loader");
};
<commit_msg>fix add assets<commit_after>'use strict';
var util = require('util');
var yeoman = require('yeoman-generator');
var path = require("path");
var fs = require("fs");
var scriptBase = require('../script-base');

var Generator = module.exports = function Generator(args, options, config) {
    scriptBase.apply(this, arguments);
    this.setUpPaths();
    if(this.context.where !== "module" || this.context.position !== "root"){
        this.log.error('You are not at the root of your module\n');
        process.exit(1);
    }
};

util.inherits(Generator, scriptBase);

Generator.prototype.createStructure = function createStructure() {
    this.mkdir("assets");
    this.mkdir("assets/skins");
    this.mkdir("assets/skins/sam");
    this.template("assets/skins/sam/moduleName-skin.css", "assets/skins/sam/" + this.moduleName + "-skin.css");
    this.template("assets/moduleName-core.css", "assets/" + this.moduleName + "-core.css");
};

Generator.prototype.updateMeta = function updateMeta() {
    var metaFile = JSON.parse(this.readFileAsString(this.metaFilePath));
    metaFile[this.moduleName].skinnable = true;
    this.write(this.metaFilePath, JSON.stringify(metaFile, null, 4));
};

Generator.prototype.advices = function advices() {
    console.log("Please...");
    console.log("shift your module");
    console.log("shift your loader");
};
<|endoftext|>


<commit_before>/**
 * # ResultsView widget for nodeGame
 * Copyright(c) 2014 Stefano Balietti
 * MIT Licensed
 *
 * Shows current, previous and next state.
 *
 * www.nodegame.org
 * ---
 */
(function(node) {

    "use strict";

    node.widgets.register('ResultsView', ResultsView);

    var JSUS = node.JSUS;

    // ## Meta-data

    ResultsView.version = '0.0.1';
    ResultsView.description = 'Visually display results of games.';

    ResultsView.className = 'resultsView';

    // ## Dependencies
    ResultsView.dependencies = {
        JSUS: {},
    };

    function ResultsView(options) {
        var that;

        that = this;

        this.id = options.id;

    }

    ResultsView.prototype.refresh = function() {
        // Ask server for games:
        node.socket.send(node.msg.create({
            target: 'SERVERCOMMAND',
            text:   'INFO',
            data: {
                type: 'RESULTS'
            }
        }));

    };

    ResultsView.prototype.append = function() {

        // Query server:
        this.refresh();
    };

    ResultsView.prototype.listeners = function() {
        var that;

        that = this;

        // Listen for server reply:
        node.on.data('INFO_RESULTS', function(msg) {
        });
    };

})(node);
<commit_msg>Displaying links to data in monitor<commit_after>/**
 * # ResultsView widget for nodeGame
 * Copyright(c) 2014 Stefano Balietti
 * MIT Licensed
 *
 * Shows current, previous and next state.
 *
 * www.nodegame.org
 * ---
 */
(function(node) {

    "use strict";

    node.widgets.register('ResultsView', ResultsView);

    var JSUS = node.JSUS;

    // ## Meta-data

    ResultsView.version = '0.0.1';
    ResultsView.description = 'Visually display results of games.';

    ResultsView.className = 'resultsView';

    // ## Dependencies
    ResultsView.dependencies = {
        JSUS: {},
    };

    function ResultsView(options) {
        var that;

        that = this;

        this.id = options.id;
        this.links = document.createElement('div');

    }

    ResultsView.prototype.refresh = function() {
        // Ask server for games:
        node.socket.send(node.msg.create({
            target: 'SERVERCOMMAND',
            text:   'INFO',
            data: {
                type: 'RESULTS'
            }
        }));

    };

    ResultsView.prototype.append = function() {
        this.bodyDiv.appendChild(this.links);
        // Query server:
        this.refresh();
    };

    ResultsView.prototype.listeners = function() {
        var that;

        that = this;

        // Listen for server reply:
        node.on.data('INFO_RESULTS', function(msg) {
            that.displayData(msg.data);
        });
    };

    ResultsView.prototype.displayData = function(files) {
        var i, element, dataDir;
        for (i = 0; i < files.length; ++i) {
            element = document.createElement('a');
            // TODO: replace with basedir + 'data/'
            element.href = '../data/' + files[i];
            element.innerHTML = files[i];
            this.links.appendChild(element);
            this.links.appendChild(document.createElement('br'));
        }
    };

})(node);
<|endoftext|>


<commit_before>import QBtn from '../btn/QBtn.js'
import QIcon from '../icon/QIcon.js'
import QSpinner from '../spinner/QSpinner.js'
import QCircularProgress from '../circular-progress/QCircularProgress.js'

import { stopAndPrevent } from '../../utils/event.js'
import { humanStorageSize } from '../../utils/format.js'

export default {
  props: {
    label: String,

    color: String,
    textColor: String,

    dark: Boolean,

    square: Boolean,
    flat: Boolean,
    bordered: Boolean,

    multiple: Boolean,
    accept: String,
    maxFileSize: Number,
    maxTotalSize: Number,
    filter: Function,
    noThumbnails: Boolean,
    autoUpload: Boolean,

    disable: Boolean,
    readonly: Boolean
  },

  data () {
    return {
      files: [],
      queuedFiles: [],
      uploadedFiles: [],
      dnd: false,
      expanded: false,

      uploadSize: 0,
      uploadedSize: 0
    }
  },

  computed: {
    canUpload () {
      return this.editable === true && this.queuedFiles.length > 0
    },

    extensions () {
      if (this.accept !== void 0) {
        return this.accept.split(',').map(ext => {
          ext = ext.trim()
          // support "image/*"
          if (ext.endsWith('/*')) {
            ext = ext.slice(0, ext.length - 1)
          }
          return ext
        })
      }
    },

    uploadProgress () {
      return this.uploadSize === 0
        ? 0
        : this.uploadedSize / this.uploadSize
    },

    uploadProgressLabel () {
      return this.__getProgressLabel(this.uploadProgress)
    },

    uploadedSizeLabel () {
      return humanStorageSize(this.uploadedSize)
    },

    uploadSizeLabel () {
      return humanStorageSize(this.uploadSize)
    },

    colorClass () {
      const cls = []
      this.color !== void 0 && cls.push(`bg-${this.color}`)
      this.textColor !== void 0 && cls.push(`text-${this.textColor}`)
      return cls.join(' ')
    },

    editable () {
      return this.disable !== true && this.readonly !== true
    }
  },

  methods: {
    pickFiles () {
      this.editable && this.$refs.input.click()
    },

    addFiles (files) {
      if (this.editable && files) {
        this.__addFiles(null, files)
      }
    },

    reset () {
      if (!this.disable) {
        this.abort()
        this.uploadedSize = 0
        this.uploadSize = 0
        this.files = []
        this.queuedFiles = []
        this.uploadedFiles = []
      }
    },

    removeUploadedFiles () {
      if (!this.disable) {
        this.files = this.files.filter(f => f.__status !== 'uploaded')
        this.uploadedFiles = []
      }
    },

    removeQueuedFiles () {
      if (!this.disable) {
        const removedFiles = []

        this.files.forEach(file => {
          if (file.__status === 'idle' || file.__status === 'failed') {
            this.uploadSize -= file.size
            removedFiles.push(file)
          }
        })

        if (removedFiles.length > 0) {
          this.files = this.files.filter(f => f.__status !== 'idle' && f.__status !== 'failed')
          this.queuedFiles = []
          this.$emit('removed', removedFiles)
        }
      }
    },

    removeFile (file) {
      if (this.disable) { return }

      if (file.__status === 'uploaded') {
        this.uploadedFiles = this.uploadedFiles.filter(f => f.name !== file.name)
      }
      else if (file.__status === 'uploading') {
        file.__abort()
      }
      else {
        this.uploadSize -= file.size
      }

      this.files = this.files.filter(f => f.name !== file.name)
      this.queuedFiles = this.queuedFiles.filter(f => f.name !== file.name)
      this.__emit('removed', [ file ])
    },

    __emit (evt, payload) {
      this.$listeners[evt] !== void 0 && this.$emit(evt, payload)
    },

    __getProgressLabel (p) {
      return (p * 100).toFixed(2) + '%'
    },

    __updateFile (file, status, uploadedSize) {
      file.__status = status

      if (status === 'idle') {
        file.__uploaded = 0
        file.__progress = 0
        file.__sizeLabel = humanStorageSize(file.size)
        file.__progressLabel = '0.00%'
        return
      }
      if (status === 'failed') {
        this.$forceUpdate()
        return
      }

      file.__uploaded = status === 'uploaded'
        ? file.size
        : uploadedSize

      file.__progress = status === 'uploaded'
        ? 1
        : Math.min(0.9999, file.__uploaded / file.size)

      file.__progressLabel = this.__getProgressLabel(file.__progress)
      this.$forceUpdate()
    },

    __addFiles (e, files) {
      files = Array.prototype.slice.call(files || e.target.files)
      this.$refs.input.value = ''

      // make sure we don't duplicate files
      files = files.filter(file => !this.files.some(f => file.name === f.name))
      if (files.length === 0) { return }

      // filter file types
      if (this.accept !== void 0) {
        files = Array.prototype.filter.call(files, file => {
          return this.extensions.some(ext => (
            file.type.toUpperCase().startsWith(ext.toUpperCase()) ||
            file.name.toUpperCase().endsWith(ext.toUpperCase())
          ))
        })
        if (files.length === 0) { return }
      }

      // filter max file size
      if (this.maxFileSize !== void 0) {
        files = Array.prototype.filter.call(files, file => file.size <= this.maxFileSize)
        if (files.length === 0) { return }
      }

      if (this.maxTotalSize !== void 0) {
        let size = 0
        for (let i = 0; i < files.length; i++) {
          size += files[i].size
          if (size > this.maxTotalSize) {
            if (i > 0) {
              files = files.slice(0, i - 1)
              break
            }
            else {
              return
            }
          }
        }
        if (files.length === 0) { return }
      }

      // do we have custom filter function?
      if (typeof this.filter === 'function') {
        files = this.filter(files)
      }

      if (files.length === 0) { return }

      let filesReady = [] // List of image load promises

      files.forEach(file => {
        this.__updateFile(file, 'idle')
        this.uploadSize += file.size

        if (this.noThumbnails !== true && file.type.toUpperCase().startsWith('IMAGE')) {
          const reader = new FileReader()
          let p = new Promise((resolve, reject) => {
            reader.onload = e => {
              let img = new Image()
              img.src = e.target.result
              file.__img = img
              resolve(true)
            }
            reader.onerror = e => { reject(e) }
          })

          reader.readAsDataURL(file)
          filesReady.push(p)
        }
      })

      Promise.all(filesReady).then(() => {
        this.files = this.files.concat(files)
        this.queuedFiles = this.queuedFiles.concat(files)
        this.__emit('added', files)
        this.autoUpload === true && this.upload()
      })
    },

    __onDragOver (e) {
      stopAndPrevent(e)
      this.dnd = true
    },

    __onDragLeave (e) {
      stopAndPrevent(e)
      this.dnd = false
    },

    __onDrop (e) {
      stopAndPrevent(e)
      let files = e.dataTransfer.files

      if (files.length > 0) {
        files = this.multiple ? files : [ files[0] ]
        this.__addFiles(null, files)
      }

      this.dnd = false
    },

    __getBtn (h, show, icon, fn) {
      if (show === true) {
        return h(QBtn, {
          props: {
            icon: this.$q.iconSet.uploader[icon],
            flat: true,
            dense: true
          },
          on: { click: fn }
        })
      }
    },

    __getHeader (h) {
      if (this.$scopedSlots.header !== void 0) {
        return this.$scopedSlots.header(this)
      }

      return h('div', {
        staticClass: 'q-uploader__header-content flex flex-center no-wrap q-gutter-xs'
      }, [
        this.__getBtn(h, this.queuedFiles.length > 0, 'removeQueue', this.removeQueuedFiles),
        this.__getBtn(h, this.uploadedFiles.length > 0, 'removeUploaded', this.removeUploadedFiles),

        this.isUploading === true
          ? h(QSpinner, { staticClass: 'q-uploader__spinner' })
          : null,

        h('div', { staticClass: 'col column justify-center' }, [
          this.label !== void 0
            ? h('div', { staticClass: 'q-uploader__title' }, [ this.label ])
            : null,

          h('div', { staticClass: 'q-uploader__subtitle' }, [
            this.uploadSizeLabel + ' / ' + this.uploadProgressLabel
          ])
        ]),

        this.__getBtn(h, this.editable, 'add', this.pickFiles),
        this.__getBtn(h, this.editable && this.queuedFiles.length > 0, 'upload', this.upload),
        this.__getBtn(h, this.editable && this.isUploading, 'clear', this.abort)
      ])
    },

    __getList (h) {
      if (this.$scopedSlots.list !== void 0) {
        return this.$scopedSlots.list(this)
      }

      return this.files.map(file => h('div', {
        key: file.name,
        staticClass: 'q-uploader__file relative-position',
        class: {
          'q-uploader__file--img': file.__img !== void 0,
          'q-uploader__file--failed': file.__status === 'failed',
          'q-uploader__file--uploaded': file.__status === 'uploaded'
        },
        style: file.__img !== void 0 ? {
          backgroundImage: 'url(' + file.__img.src + ')'
        } : null
      }, [
        h('div', {
          staticClass: 'q-uploader__file-header row flex-center no-wrap'
        }, [
          file.__status === 'failed'
            ? h(QIcon, {
              staticClass: 'q-uploader__file-status',
              props: {
                name: this.$q.iconSet.type.negative,
                color: 'negative'
              }
            })
            : null,

          h('div', { staticClass: 'q-uploader__file-header-content col' }, [
            h('div', { staticClass: 'q-uploader__title' }, [ file.name ]),
            h('div', {
              staticClass: 'q-uploader__subtitle row items-center no-wrap'
            }, [
              file.__sizeLabel + ' / ' + file.__progressLabel
            ])
          ]),

          file.__status === 'uploading'
            ? h(QCircularProgress, {
              props: {
                value: file.__progress,
                min: 0,
                max: 1,
                indeterminate: file.__progress === 0
              }
            })
            : h(QBtn, {
              props: {
                round: true,
                dense: true,
                flat: true,
                icon: this.$q.iconSet.uploader[file.__status === 'uploaded' ? 'done' : 'clear']
              },
              on: {
                click: () => { this.removeFile(file) }
              }
            })
        ])
      ]))
    }
  },

  beforeDestroy () {
    this.isDestroyed = true
    this.isUploading && this.abort()
  },

  render (h) {
    return h('div', {
      staticClass: 'q-uploader column no-wrap',
      class: {
        'q-uploader--dark': this.dark,
        'q-uploader--bordered': this.bordered,
        'q-uploader--square no-border-radius': this.square,
        'q-uploader--flat no-shadow': this.flat,
        'disabled q-uploader--disable': this.disable
      },
      on: this.editable === true && this.isIdle === true
        ? { dragover: this.__onDragOver }
        : null
    }, [
      h('input', {
        ref: 'input',
        staticClass: 'q-uploader__input',
        attrs: {
          type: 'file',
          accept: this.accept,
          ...(this.multiple ? { multiple: true } : {})
        },
        on: {
          change: this.__addFiles
        }
      }),

      h('div', {
        staticClass: 'q-uploader__header',
        class: this.colorClass
      }, [
        this.__getHeader(h)
      ]),

      h('div', {
        staticClass: 'q-uploader__list scroll'
      }, this.__getList(h)),

      this.dnd === true ? h('div', {
        staticClass: 'q-uploader__dnd absolute-full',
        on: {
          dragenter: stopAndPrevent,
          dragover: stopAndPrevent,
          dragleave: this.__onDragLeave,
          drop: this.__onDrop
        }
      }) : null,

      this.isBusy === true ? h('div', {
        staticClass: 'q-uploader__overlay absolute-full flex flex-center'
      }, [
        h(QSpinner)
      ]) : null
    ])
  }
}
<commit_msg>Restore "hide-upload-button" option from 0.17<commit_after>import QBtn from '../btn/QBtn.js'
import QIcon from '../icon/QIcon.js'
import QSpinner from '../spinner/QSpinner.js'
import QCircularProgress from '../circular-progress/QCircularProgress.js'

import { stopAndPrevent } from '../../utils/event.js'
import { humanStorageSize } from '../../utils/format.js'

export default {
  props: {
    label: String,

    color: String,
    textColor: String,

    dark: Boolean,

    square: Boolean,
    flat: Boolean,
    bordered: Boolean,

    multiple: Boolean,
    accept: String,
    maxFileSize: Number,
    maxTotalSize: Number,
    filter: Function,
    noThumbnails: Boolean,
    autoUpload: Boolean,
    hideUploadButton: Boolean,

    disable: Boolean,
    readonly: Boolean
  },

  data () {
    return {
      files: [],
      queuedFiles: [],
      uploadedFiles: [],
      dnd: false,
      expanded: false,

      uploadSize: 0,
      uploadedSize: 0
    }
  },

  computed: {
    canUpload () {
      return this.editable === true && this.queuedFiles.length > 0
    },

    extensions () {
      if (this.accept !== void 0) {
        return this.accept.split(',').map(ext => {
          ext = ext.trim()
          // support "image/*"
          if (ext.endsWith('/*')) {
            ext = ext.slice(0, ext.length - 1)
          }
          return ext
        })
      }
    },

    uploadProgress () {
      return this.uploadSize === 0
        ? 0
        : this.uploadedSize / this.uploadSize
    },

    uploadProgressLabel () {
      return this.__getProgressLabel(this.uploadProgress)
    },

    uploadedSizeLabel () {
      return humanStorageSize(this.uploadedSize)
    },

    uploadSizeLabel () {
      return humanStorageSize(this.uploadSize)
    },

    colorClass () {
      const cls = []
      this.color !== void 0 && cls.push(`bg-${this.color}`)
      this.textColor !== void 0 && cls.push(`text-${this.textColor}`)
      return cls.join(' ')
    },

    editable () {
      return this.disable !== true && this.readonly !== true
    }
  },

  methods: {
    pickFiles () {
      this.editable && this.$refs.input.click()
    },

    addFiles (files) {
      if (this.editable && files) {
        this.__addFiles(null, files)
      }
    },

    reset () {
      if (!this.disable) {
        this.abort()
        this.uploadedSize = 0
        this.uploadSize = 0
        this.files = []
        this.queuedFiles = []
        this.uploadedFiles = []
      }
    },

    removeUploadedFiles () {
      if (!this.disable) {
        this.files = this.files.filter(f => f.__status !== 'uploaded')
        this.uploadedFiles = []
      }
    },

    removeQueuedFiles () {
      if (!this.disable) {
        const removedFiles = []

        this.files.forEach(file => {
          if (file.__status === 'idle' || file.__status === 'failed') {
            this.uploadSize -= file.size
            removedFiles.push(file)
          }
        })

        if (removedFiles.length > 0) {
          this.files = this.files.filter(f => f.__status !== 'idle' && f.__status !== 'failed')
          this.queuedFiles = []
          this.$emit('removed', removedFiles)
        }
      }
    },

    removeFile (file) {
      if (this.disable) { return }

      if (file.__status === 'uploaded') {
        this.uploadedFiles = this.uploadedFiles.filter(f => f.name !== file.name)
      }
      else if (file.__status === 'uploading') {
        file.__abort()
      }
      else {
        this.uploadSize -= file.size
      }

      this.files = this.files.filter(f => f.name !== file.name)
      this.queuedFiles = this.queuedFiles.filter(f => f.name !== file.name)
      this.__emit('removed', [ file ])
    },

    __emit (evt, payload) {
      this.$listeners[evt] !== void 0 && this.$emit(evt, payload)
    },

    __getProgressLabel (p) {
      return (p * 100).toFixed(2) + '%'
    },

    __updateFile (file, status, uploadedSize) {
      file.__status = status

      if (status === 'idle') {
        file.__uploaded = 0
        file.__progress = 0
        file.__sizeLabel = humanStorageSize(file.size)
        file.__progressLabel = '0.00%'
        return
      }
      if (status === 'failed') {
        this.$forceUpdate()
        return
      }

      file.__uploaded = status === 'uploaded'
        ? file.size
        : uploadedSize

      file.__progress = status === 'uploaded'
        ? 1
        : Math.min(0.9999, file.__uploaded / file.size)

      file.__progressLabel = this.__getProgressLabel(file.__progress)
      this.$forceUpdate()
    },

    __addFiles (e, files) {
      files = Array.prototype.slice.call(files || e.target.files)
      this.$refs.input.value = ''

      // make sure we don't duplicate files
      files = files.filter(file => !this.files.some(f => file.name === f.name))
      if (files.length === 0) { return }

      // filter file types
      if (this.accept !== void 0) {
        files = Array.prototype.filter.call(files, file => {
          return this.extensions.some(ext => (
            file.type.toUpperCase().startsWith(ext.toUpperCase()) ||
            file.name.toUpperCase().endsWith(ext.toUpperCase())
          ))
        })
        if (files.length === 0) { return }
      }

      // filter max file size
      if (this.maxFileSize !== void 0) {
        files = Array.prototype.filter.call(files, file => file.size <= this.maxFileSize)
        if (files.length === 0) { return }
      }

      if (this.maxTotalSize !== void 0) {
        let size = 0
        for (let i = 0; i < files.length; i++) {
          size += files[i].size
          if (size > this.maxTotalSize) {
            if (i > 0) {
              files = files.slice(0, i - 1)
              break
            }
            else {
              return
            }
          }
        }
        if (files.length === 0) { return }
      }

      // do we have custom filter function?
      if (typeof this.filter === 'function') {
        files = this.filter(files)
      }

      if (files.length === 0) { return }

      let filesReady = [] // List of image load promises

      files.forEach(file => {
        this.__updateFile(file, 'idle')
        this.uploadSize += file.size

        if (this.noThumbnails !== true && file.type.toUpperCase().startsWith('IMAGE')) {
          const reader = new FileReader()
          let p = new Promise((resolve, reject) => {
            reader.onload = e => {
              let img = new Image()
              img.src = e.target.result
              file.__img = img
              resolve(true)
            }
            reader.onerror = e => { reject(e) }
          })

          reader.readAsDataURL(file)
          filesReady.push(p)
        }
      })

      Promise.all(filesReady).then(() => {
        this.files = this.files.concat(files)
        this.queuedFiles = this.queuedFiles.concat(files)
        this.__emit('added', files)
        this.autoUpload === true && this.upload()
      })
    },

    __onDragOver (e) {
      stopAndPrevent(e)
      this.dnd = true
    },

    __onDragLeave (e) {
      stopAndPrevent(e)
      this.dnd = false
    },

    __onDrop (e) {
      stopAndPrevent(e)
      let files = e.dataTransfer.files

      if (files.length > 0) {
        files = this.multiple ? files : [ files[0] ]
        this.__addFiles(null, files)
      }

      this.dnd = false
    },

    __getBtn (h, show, icon, fn) {
      if (show === true) {
        return h(QBtn, {
          props: {
            icon: this.$q.iconSet.uploader[icon],
            flat: true,
            dense: true
          },
          on: { click: fn }
        })
      }
    },

    __getHeader (h) {
      if (this.$scopedSlots.header !== void 0) {
        return this.$scopedSlots.header(this)
      }

      return h('div', {
        staticClass: 'q-uploader__header-content flex flex-center no-wrap q-gutter-xs'
      }, [
        this.__getBtn(h, this.queuedFiles.length > 0, 'removeQueue', this.removeQueuedFiles),
        this.__getBtn(h, this.uploadedFiles.length > 0, 'removeUploaded', this.removeUploadedFiles),

        this.isUploading === true
          ? h(QSpinner, { staticClass: 'q-uploader__spinner' })
          : null,

        h('div', { staticClass: 'col column justify-center' }, [
          this.label !== void 0
            ? h('div', { staticClass: 'q-uploader__title' }, [ this.label ])
            : null,

          h('div', { staticClass: 'q-uploader__subtitle' }, [
            this.uploadSizeLabel + ' / ' + this.uploadProgressLabel
          ])
        ]),

        this.__getBtn(h, this.editable && (this.multiple || this.queuedFiles.length == 0), 'add', this.pickFiles),
        this.__getBtn(h, !this.hideUploadButton && this.editable && this.queuedFiles.length > 0, 'upload', this.upload),
        this.__getBtn(h, this.editable && this.isUploading, 'clear', this.abort)
      ])
    },

    __getList (h) {
      if (this.$scopedSlots.list !== void 0) {
        return this.$scopedSlots.list(this)
      }

      return this.files.map(file => h('div', {
        key: file.name,
        staticClass: 'q-uploader__file relative-position',
        class: {
          'q-uploader__file--img': file.__img !== void 0,
          'q-uploader__file--failed': file.__status === 'failed',
          'q-uploader__file--uploaded': file.__status === 'uploaded'
        },
        style: file.__img !== void 0 ? {
          backgroundImage: 'url(' + file.__img.src + ')'
        } : null
      }, [
        h('div', {
          staticClass: 'q-uploader__file-header row flex-center no-wrap'
        }, [
          file.__status === 'failed'
            ? h(QIcon, {
              staticClass: 'q-uploader__file-status',
              props: {
                name: this.$q.iconSet.type.negative,
                color: 'negative'
              }
            })
            : null,

          h('div', { staticClass: 'q-uploader__file-header-content col' }, [
            h('div', { staticClass: 'q-uploader__title' }, [ file.name ]),
            h('div', {
              staticClass: 'q-uploader__subtitle row items-center no-wrap'
            }, [
              file.__sizeLabel + ' / ' + file.__progressLabel
            ])
          ]),

          file.__status === 'uploading'
            ? h(QCircularProgress, {
              props: {
                value: file.__progress,
                min: 0,
                max: 1,
                indeterminate: file.__progress === 0
              }
            })
            : h(QBtn, {
              props: {
                round: true,
                dense: true,
                flat: true,
                icon: this.$q.iconSet.uploader[file.__status === 'uploaded' ? 'done' : 'clear']
              },
              on: {
                click: () => { this.removeFile(file) }
              }
            })
        ])
      ]))
    }
  },

  beforeDestroy () {
    this.isDestroyed = true
    this.isUploading && this.abort()
  },

  render (h) {
    return h('div', {
      staticClass: 'q-uploader column no-wrap',
      class: {
        'q-uploader--dark': this.dark,
        'q-uploader--bordered': this.bordered,
        'q-uploader--square no-border-radius': this.square,
        'q-uploader--flat no-shadow': this.flat,
        'disabled q-uploader--disable': this.disable
      },
      on: this.editable === true && this.isIdle === true
        ? { dragover: this.__onDragOver }
        : null
    }, [
      h('input', {
        ref: 'input',
        staticClass: 'q-uploader__input',
        attrs: {
          type: 'file',
          accept: this.accept,
          ...(this.multiple ? { multiple: true } : {})
        },
        on: {
          change: this.__addFiles
        }
      }),

      h('div', {
        staticClass: 'q-uploader__header',
        class: this.colorClass
      }, [
        this.__getHeader(h)
      ]),

      h('div', {
        staticClass: 'q-uploader__list scroll'
      }, this.__getList(h)),

      this.dnd === true ? h('div', {
        staticClass: 'q-uploader__dnd absolute-full',
        on: {
          dragenter: stopAndPrevent,
          dragover: stopAndPrevent,
          dragleave: this.__onDragLeave,
          drop: this.__onDrop
        }
      }) : null,

      this.isBusy === true ? h('div', {
        staticClass: 'q-uploader__overlay absolute-full flex flex-center'
      }, [
        h(QSpinner)
      ]) : null
    ])
  }
}
<|endoftext|>