<commit_before>/*
Copyright 2015 Mozilla Foundation

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
/* import-globals-from pdfHandler.js */

'use strict';
/**
 * This file is one part of the Referer persistency implementation. The other
 * part resides in chromecom.js.
 *
 * This file collects request headers for every http(s) request, and temporarily
 * stores the request headers in a dictionary. Upon completion of the request
 * (success or failure), the headers are discarded.
 * pdfHandler.js will call saveReferer(details) when it is about to redirect to
 * the viewer. Upon calling saveReferer, the Referer header is extracted from
 * the request headers and saved.
 *
 * When the viewer is opened, it opens a port ("chromecom-referrer"). This port
 * is used to set up the webRequest listeners that stick the Referer headers to
 * the HTTP requests created by this extension. When the port is disconnected,
 * the webRequest listeners and the referrer information is discarded.
 *
 * See setReferer in chromecom.js for more explanation of this logic.
 */

// Remembers the request headers for every http(s) page request for the duration
// of the request.
var g_requestHeaders = {};
// g_referrers[tabId][frameId] = referrer of PDF frame.
var g_referrers = {};

(function() {
  var requestFilter = {
    urls: ['*://*/*'],
    types: ['main_frame', 'sub_frame'],
  };
  chrome.webRequest.onSendHeaders.addListener(function(details) {
    g_requestHeaders[details.requestId] = details.requestHeaders;
  }, requestFilter, ['requestHeaders']);
  chrome.webRequest.onBeforeRedirect.addListener(forgetHeaders, requestFilter);
  chrome.webRequest.onCompleted.addListener(forgetHeaders, requestFilter);
  chrome.webRequest.onErrorOccurred.addListener(forgetHeaders, requestFilter);
  function forgetHeaders(details) {
    delete g_requestHeaders[details.requestId];
  }
})();

/**
 * @param {object} details - onHeadersReceived event data.
 */
function saveReferer(details) {
  var referer = g_requestHeaders[details.requestId] &&
      getHeaderFromHeaders(g_requestHeaders[details.requestId], 'referer');
  referer = referer && referer.value || '';
  if (!g_referrers[details.tabId]) {
    g_referrers[details.tabId] = {};
  }
  g_referrers[details.tabId][details.frameId] = referer;
}

chrome.tabs.onRemoved.addListener(function(tabId) {
  delete g_referrers[tabId];
});

// This method binds a webRequest event handler which adds the Referer header
// to matching PDF resource requests (only if the Referer is non-empty). The
// handler is removed as soon as the PDF viewer frame is unloaded.
chrome.runtime.onConnect.addListener(function onReceivePort(port) {
  if (port.name !== 'chromecom-referrer') {
    return;
  }
  // Note: sender.frameId is only set in Chrome 41+.
  if (!('frameId' in port.sender)) {
    port.disconnect();
    return;
  }
  var tabId = port.sender.tab.id;
  var frameId = port.sender.frameId;

  // If the PDF is viewed for the first time, then the referer will be set here.
  var referer = g_referrers[tabId] && g_referrers[tabId][frameId] || '';
  port.onMessage.addListener(function(data) {
    // If the viewer was opened directly (without opening a PDF URL first), then
    // the background script does not know about g_referrers, but the viewer may
    // know about the referer if stored in the history state (see chromecom.js).
    if (data.referer) {
      referer = data.referer;
    }
    chrome.webRequest.onBeforeSendHeaders.removeListener(onBeforeSendHeaders);
    if (referer) {
      // Only add a blocking request handler if the referer has to be rewritten.
      chrome.webRequest.onBeforeSendHeaders.addListener(onBeforeSendHeaders, {
        urls: [data.requestUrl],
        types: ['xmlhttprequest'],
        tabId: tabId,
      }, ['blocking', 'requestHeaders']);
    }
    // Acknowledge the message, and include the latest referer for this frame.
    port.postMessage(referer);
  });

  // The port is only disconnected when the other end reloads.
  port.onDisconnect.addListener(function() {
    if (g_referrers[tabId]) {
      delete g_referrers[tabId][frameId];
    }
    chrome.webRequest.onBeforeSendHeaders.removeListener(onBeforeSendHeaders);
    chrome.webRequest.onHeadersReceived.removeListener(exposeOnHeadersReceived);
  });

  // Expose some response headers for fetch API calls from PDF.js;
  // This is a work-around for https://crbug.com/784528
  chrome.webRequest.onHeadersReceived.addListener(exposeOnHeadersReceived, {
    urls: ['https://*/*'],
    types: ['xmlhttprequest'],
    tabId: tabId,
  }, ['blocking', 'responseHeaders']);

  function onBeforeSendHeaders(details) {
    if (details.frameId !== frameId) {
      return undefined;
    }
    var headers = details.requestHeaders;
    var refererHeader = getHeaderFromHeaders(headers, 'referer');
    if (!refererHeader) {
      refererHeader = { name: 'Referer', };
      headers.push(refererHeader);
    } else if (refererHeader.value &&
        refererHeader.value.lastIndexOf('chrome-extension:', 0) !== 0) {
      // Sanity check. If the referer is set, and the value is not the URL of
      // this extension, then the request was not initiated by this extension.
      return undefined;
    }
    refererHeader.value = referer;
    return { requestHeaders: headers, };
  }

  function exposeOnHeadersReceived(details) {
    if (details.frameId !== frameId) {
      return undefined;
    }
    var headers = details.responseHeaders;
    var aceh = getHeaderFromHeaders(headers, 'access-control-expose-headers');
    // List of headers that PDF.js uses in src/display/network_utils.js
    var acehValue =
      'accept-ranges,content-encoding,content-length,content-disposition';
    if (aceh) {
      aceh.value += ',' + acehValue;
    } else {
      aceh = { name: 'Access-Control-Expose-Headers', value: acehValue, };
      headers.push(aceh);
    }
    return { responseHeaders: headers, };
  }
});
<commit_msg>[CRX] Preserve referrer in Chrome 72+<commit_after>/*
Copyright 2015 Mozilla Foundation

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
/* import-globals-from pdfHandler.js */

'use strict';
/**
 * This file is one part of the Referer persistency implementation. The other
 * part resides in chromecom.js.
 *
 * This file collects request headers for every http(s) request, and temporarily
 * stores the request headers in a dictionary. Upon completion of the request
 * (success or failure), the headers are discarded.
 * pdfHandler.js will call saveReferer(details) when it is about to redirect to
 * the viewer. Upon calling saveReferer, the Referer header is extracted from
 * the request headers and saved.
 *
 * When the viewer is opened, it opens a port ("chromecom-referrer"). This port
 * is used to set up the webRequest listeners that stick the Referer headers to
 * the HTTP requests created by this extension. When the port is disconnected,
 * the webRequest listeners and the referrer information is discarded.
 *
 * See setReferer in chromecom.js for more explanation of this logic.
 */

// Remembers the request headers for every http(s) page request for the duration
// of the request.
var g_requestHeaders = {};
// g_referrers[tabId][frameId] = referrer of PDF frame.
var g_referrers = {};

var extraInfoSpecWithHeaders; // = ['requestHeaders', 'extraHeaders']

(function() {
  var requestFilter = {
    urls: ['*://*/*'],
    types: ['main_frame', 'sub_frame'],
  };
  function registerListener(extraInfoSpec) {
    extraInfoSpecWithHeaders = extraInfoSpec;
    // May throw if the given extraInfoSpec is unsupported.
    chrome.webRequest.onSendHeaders.addListener(function(details) {
      g_requestHeaders[details.requestId] = details.requestHeaders;
    }, requestFilter, extraInfoSpec);
  }
  try {
    registerListener(['requestHeaders', 'extraHeaders']);
  } catch (e) {
    // "extraHeaders" is not supported in Chrome 71 and earlier.
    registerListener(['requestHeaders']);
  }
  chrome.webRequest.onBeforeRedirect.addListener(forgetHeaders, requestFilter);
  chrome.webRequest.onCompleted.addListener(forgetHeaders, requestFilter);
  chrome.webRequest.onErrorOccurred.addListener(forgetHeaders, requestFilter);
  function forgetHeaders(details) {
    delete g_requestHeaders[details.requestId];
  }
})();

/**
 * @param {object} details - onHeadersReceived event data.
 */
function saveReferer(details) {
  var referer = g_requestHeaders[details.requestId] &&
      getHeaderFromHeaders(g_requestHeaders[details.requestId], 'referer');
  referer = referer && referer.value || '';
  if (!g_referrers[details.tabId]) {
    g_referrers[details.tabId] = {};
  }
  g_referrers[details.tabId][details.frameId] = referer;
}

chrome.tabs.onRemoved.addListener(function(tabId) {
  delete g_referrers[tabId];
});

// This method binds a webRequest event handler which adds the Referer header
// to matching PDF resource requests (only if the Referer is non-empty). The
// handler is removed as soon as the PDF viewer frame is unloaded.
chrome.runtime.onConnect.addListener(function onReceivePort(port) {
  if (port.name !== 'chromecom-referrer') {
    return;
  }
  // Note: sender.frameId is only set in Chrome 41+.
  if (!('frameId' in port.sender)) {
    port.disconnect();
    return;
  }
  var tabId = port.sender.tab.id;
  var frameId = port.sender.frameId;

  // If the PDF is viewed for the first time, then the referer will be set here.
  var referer = g_referrers[tabId] && g_referrers[tabId][frameId] || '';
  port.onMessage.addListener(function(data) {
    // If the viewer was opened directly (without opening a PDF URL first), then
    // the background script does not know about g_referrers, but the viewer may
    // know about the referer if stored in the history state (see chromecom.js).
    if (data.referer) {
      referer = data.referer;
    }
    chrome.webRequest.onBeforeSendHeaders.removeListener(onBeforeSendHeaders);
    if (referer) {
      // Only add a blocking request handler if the referer has to be rewritten.
      chrome.webRequest.onBeforeSendHeaders.addListener(onBeforeSendHeaders, {
        urls: [data.requestUrl],
        types: ['xmlhttprequest'],
        tabId: tabId,
      }, ['blocking', ...extraInfoSpecWithHeaders]);
    }
    // Acknowledge the message, and include the latest referer for this frame.
    port.postMessage(referer);
  });

  // The port is only disconnected when the other end reloads.
  port.onDisconnect.addListener(function() {
    if (g_referrers[tabId]) {
      delete g_referrers[tabId][frameId];
    }
    chrome.webRequest.onBeforeSendHeaders.removeListener(onBeforeSendHeaders);
    chrome.webRequest.onHeadersReceived.removeListener(exposeOnHeadersReceived);
  });

  // Expose some response headers for fetch API calls from PDF.js;
  // This is a work-around for https://crbug.com/784528
  chrome.webRequest.onHeadersReceived.addListener(exposeOnHeadersReceived, {
    urls: ['https://*/*'],
    types: ['xmlhttprequest'],
    tabId: tabId,
  }, ['blocking', 'responseHeaders']);

  function onBeforeSendHeaders(details) {
    if (details.frameId !== frameId) {
      return undefined;
    }
    var headers = details.requestHeaders;
    var refererHeader = getHeaderFromHeaders(headers, 'referer');
    if (!refererHeader) {
      refererHeader = { name: 'Referer', };
      headers.push(refererHeader);
    } else if (refererHeader.value &&
        refererHeader.value.lastIndexOf('chrome-extension:', 0) !== 0) {
      // Sanity check. If the referer is set, and the value is not the URL of
      // this extension, then the request was not initiated by this extension.
      return undefined;
    }
    refererHeader.value = referer;
    return { requestHeaders: headers, };
  }

  function exposeOnHeadersReceived(details) {
    if (details.frameId !== frameId) {
      return undefined;
    }
    var headers = details.responseHeaders;
    var aceh = getHeaderFromHeaders(headers, 'access-control-expose-headers');
    // List of headers that PDF.js uses in src/display/network_utils.js
    var acehValue =
      'accept-ranges,content-encoding,content-length,content-disposition';
    if (aceh) {
      aceh.value += ',' + acehValue;
    } else {
      aceh = { name: 'Access-Control-Expose-Headers', value: acehValue, };
      headers.push(aceh);
    }
    return { responseHeaders: headers, };
  }
});
<|endoftext|>


<commit_before>/* Colors in use for lots of zone related stuffs */
var colors = [
    "rgb(240,240,240)",
    "rgb(102,204,0)",
    "rgb(000,200,255)",
    "rgb(51,102,255)",
    "rgb(255,153,0)",
    "rgb(255,0,0)",
    "rgb(166,0,0)",
    "rgb(119,0,119)"];

var playercolors = [
    { r: 255, g: 20, b: 20 },
    { r: 20, g: 20, b: 255 },
    { r: 20, g: 255, b: 255 },
    { r: 255, g: 20, b: 255 },
    { r: 255, g: 255, b: 20 },
    { r: 80, g: 80, b: 80 }
];

/* conver rgb colors to hex, openlayers uses those */
function colorToHex(c) {
    var m = /rgba?\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)/.exec(c);
    return m ? '#' + (1 << 24 | m[1] << 16 | m[2] << 8 | m[3]).toString(16).substr(1) : c;
}

/* Used by flot axis */
var axisformatters = {
    speed: function(val, axis) {
        return (val).toFixed(axis.tickDecimals) + ' km/h';
    },
    cadence: function(val, axis) {
        return (val).toFixed(axis.tickDecimals) + ' RPM';
    },
    altitude: function(val, axis) {
        return (val).toFixed(axis.tickDecimals) + ' m';
    },
    length: function(val, axis) {
        return (Math.round(val*100)/100) + ' km';
    },
    distance: function(val, axis) {
        return (Math.round(val*100)/100) + ' km';
    },
    power: function(val, axis) {
        return (val).toFixed(axis.tickDecimals) + ' W';
    },
    hr: function(val, axis) {
        return (val).toFixed(axis.tickDecimals) + ' BPM';
    },
    temp: function(val, axis) {
        return (val).toFixed(axis.tickDecimals) + ' ℃';
    },
    percent: function(val, axis) {
        return (val).toFixed(axis.tickDecimals) + ' %';
    },
    time: function(val, axis) {
        var hours = Math.floor(val / 60);
        var minutes = Math.floor(val - hours * 60);
	var seconds = Math.round(60 * (val - Math.floor(val)));

	var r = "";

        if (hours)
            r += hours + 'h ';

	if (minutes)
	    r += minutes + 'm ';

	if (seconds)
	    r += seconds + 's';
	return $.trim(r);
    },
    duration: function(val, axis) {
        var days    = parseInt((val / (60*60*24)));
        var hours   = parseInt((val % (60*60*24)) / (60*60));
        var minutes = parseInt((val % (60*60)) / (60));

        var result = [];

        if (days > 0) {
            result.push(days + "d");
        }
        if (hours > 0) {
            result.push(hours + "h");
        }
        if (minutes > 0) {
            result.push(minutes + "m");
        }
        if (result.length == 0) {
            return "0m"
        }
        if (result.length == 1) {
            return result[0];
        }
        return result.slice(0, result.length-1).join(", ") + " " + result[result.length-1];
    }
};

function choose (set) {
    return set[Math.floor(Math.random() * set.length)];
}

jQuery.fn.autoscroll = function() {
    $('html,body').animate({scrollTop: this.offset().top}, 500);
}
$(function() {
    function popupfadein(evt) {
        var itemId = this.getAttribute("id").split("_")[1];

        var popupBox = $("#mouseover_" + itemId).show();
        var boxWidth = 300;
        var boxHeight = popupBox.height();
        popupBox.css({
            left: evt.clientX + 10 + (evt.clientX + boxWidth > window.innerWidth ? - boxWidth - 50 + window.innerWidth - evt.clientX : 0) + "px",
            top: evt.clientY + 10 + (evt.clientY + boxHeight > window.innerHeight ? - boxHeight - 50 + window.innerHeight - evt.clientY : 0) + "px",
            width: boxWidth + "px"
        });
    }

    function popupfadeout(evt) {
        var itemId = this.getAttribute("id").split("_")[1];

        var popupBox = $("#mouseover_" + itemId).hide();
    }

    $(".hoverpoint").hoverIntent({timeout: 0, interval: 500, over: popupfadein, out: popupfadeout});

    function profilepopupfadein(evt) {
        var itemId = this.getAttribute("id").split("_")[1];

        var popupBox = $("#profile_" + itemId).show();
        var boxWidth = 300;
        popupBox.css({ left: evt.clientX + 10 + (evt.clientX + boxWidth > window.innerWidth ? - boxWidth - 50 + window.innerWidth - evt.clientX : 0) + "px", top: evt.clientY + 10 + "px", width: boxWidth + "px" });
    }

    function profilepopupfadeout(evt) {
        var itemId = this.getAttribute("id").split("_")[1];

        var popupBox = $("#profile_" + itemId).hide();
    }
    $(".profilehoverpoint").hoverIntent({timeout: 0, interval: 500, over: profilepopupfadein, out: profilepopupfadeout});

    var profile_avatar = $('#profile_avatar');
    if(profile_avatar) {
        profile_avatar.bind('mouseenter', function() {
            $('#avatar_replace').css('display', 'block');
        }).bind('mouseleave', function() {
            $('#avatar_replace').css('display', 'none');
        });
    }
    var ymap = document.getElementById('ymap');
    if(ymap) {
        var map = new YMap(ymap);
        // Add map type control
        map.addTypeControl();
        // Add map zoom (long) control
        map.addZoomLong();
        // Add the Pan Control
        map.addPanControl();
        for(var i = 0; i < _geo.length; ++i) {
            var g = _geo[i];
            var yPoint = new YGeoPoint(g[0], g[1]);
            // Display the map centered on a geocoded location
            map.drawZoomAndCenter(yPoint, 12);
            // Create a new marker for an address
            var myMarker = new YMarker(yPoint);
            // Create some content to go inside the SmartWindow
            var myMarkerContent = g[2];
            // When the marker is clicked, show the SmartWindow
            YEvent.Capture(myMarker, EventsList.MouseClick, function() {
                myMarker.openSmartWindow(myMarkerContent);
            });
            // Put the marker on the map
            map.addOverlay(myMarker);
            var pageLink = document.getElementById('loc_' + i);
            if(pageLink) {
                pageLink.onclick = function() {
                    var geoIndex = parseInt(this.id.replace('loc_', ''), 10);
                    var g = _geo[geoIndex];
                    var yPoint = new YGeoPoint(g[0], g[1]);
                    map.drawZoomAndCenter(yPoint, 12);
                };
            }
        }
    }
});
	$('#listSearch').bind("focus", function (evt) {
		if ($(this).val() == $(this).data("text")) {
			$(this).data("text", $(this).val());
			$(this).val("");
            $(this).animate({ width: '90px' }, 250);
		}
	});
	$('#listSearch').bind("blur", function (evt) {
		if ($(this).val().length == 0)
			$(this).val($(this).data("text"));
            $(this).animate({ width: '60px' }, 250);
	});
	$('#listSearch').data("text", $('#listSearch').val());
	$('#listSearch').attr("id", "listSearch-" + Math.random());
// Declare namespace
var TURAN = {};
<commit_msg>remove old js cruft, fix nav search input js<commit_after>/* Colors in use for lots of zone related stuffs */
var colors = [
    "rgb(240,240,240)",
    "rgb(102,204,0)",
    "rgb(000,200,255)",
    "rgb(51,102,255)",
    "rgb(255,153,0)",
    "rgb(255,0,0)",
    "rgb(166,0,0)",
    "rgb(119,0,119)"];

var playercolors = [
    { r: 255, g: 20, b: 20 },
    { r: 20, g: 20, b: 255 },
    { r: 20, g: 255, b: 255 },
    { r: 255, g: 20, b: 255 },
    { r: 255, g: 255, b: 20 },
    { r: 80, g: 80, b: 80 }
];

/* conver rgb colors to hex, openlayers uses those */
function colorToHex(c) {
    var m = /rgba?\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)/.exec(c);
    return m ? '#' + (1 << 24 | m[1] << 16 | m[2] << 8 | m[3]).toString(16).substr(1) : c;
}

/* Used by flot axis */
var axisformatters = {
    speed: function(val, axis) {
        return (val).toFixed(axis.tickDecimals) + ' km/h';
    },
    cadence: function(val, axis) {
        return (val).toFixed(axis.tickDecimals) + ' RPM';
    },
    altitude: function(val, axis) {
        return (val).toFixed(axis.tickDecimals) + ' m';
    },
    length: function(val, axis) {
        return (Math.round(val*100)/100) + ' km';
    },
    distance: function(val, axis) {
        return (Math.round(val*100)/100) + ' km';
    },
    power: function(val, axis) {
        return (val).toFixed(axis.tickDecimals) + ' W';
    },
    hr: function(val, axis) {
        return (val).toFixed(axis.tickDecimals) + ' BPM';
    },
    temp: function(val, axis) {
        return (val).toFixed(axis.tickDecimals) + ' ℃';
    },
    percent: function(val, axis) {
        return (val).toFixed(axis.tickDecimals) + ' %';
    },
    time: function(val, axis) {
        var hours = Math.floor(val / 60);
        var minutes = Math.floor(val - hours * 60);
	var seconds = Math.round(60 * (val - Math.floor(val)));

	var r = "";

        if (hours)
            r += hours + 'h ';

	if (minutes)
	    r += minutes + 'm ';

	if (seconds)
	    r += seconds + 's';
	return $.trim(r);
    },
    duration: function(val, axis) {
        var days    = parseInt((val / (60*60*24)));
        var hours   = parseInt((val % (60*60*24)) / (60*60));
        var minutes = parseInt((val % (60*60)) / (60));

        var result = [];

        if (days > 0) {
            result.push(days + "d");
        }
        if (hours > 0) {
            result.push(hours + "h");
        }
        if (minutes > 0) {
            result.push(minutes + "m");
        }
        if (result.length == 0) {
            return "0m"
        }
        if (result.length == 1) {
            return result[0];
        }
        return result.slice(0, result.length-1).join(", ") + " " + result[result.length-1];
    }
};

function choose (set) {
    return set[Math.floor(Math.random() * set.length)];
}

jQuery.fn.autoscroll = function() {
    $('html,body').animate({scrollTop: this.offset().top}, 500);
}
$(function() {
    var profile_avatar = $('#profile_avatar');
    if(profile_avatar) {
        profile_avatar.bind('mouseenter', function() {
            $('#avatar_replace').css('display', 'block');
        }).bind('mouseleave', function() {
            $('#avatar_replace').css('display', 'none');
        });
    }
});
$(function() { // Stuff for for search input field
	$('#listSearch').bind("focus", function (evt) {
		if ($(this).val() == $(this).data("text")) {
			$(this).data("text", $(this).val());
			$(this).val("");
            $(this).animate({ width: '90px' }, 250);
		}
	});
	$('#listSearch').bind("blur", function (evt) {
		if ($(this).val().length == 0)
			$(this).val($(this).data("text"));
            $(this).animate({ width: '60px' }, 250);
	});
	$('#listSearch').data("text", $('#listSearch').val());
	$('#listSearch').attr("id", "listSearch-" + Math.random());
});
// Declare namespace
var Turan = {};
<|endoftext|>


<commit_before>var participants = [];		//people currently running an instance of the hangout
var queue = [];				//list of participant ids waiting to speak. 0 is the current speaker
var timeOut = -1;				//unix time of when the current speaker's turn will end
var currentSpeakerId;		//id of the current speaker; on updates if it doesn't match queue, manager sets new time

var greenDot = gapi.hangout.av.effects.createImageResource('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAAAXNSR0IArs4c6QAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB90BDwMNIxL4LdEAAAAdaVRYdENvbW1lbnQAAAAAAENyZWF0ZWQgd2l0aCBHSU1QZC5lBwAAAuNJREFUeNrtmy2M2mAYx3/tTsxchliWnDi1kAxBhsE0aTBkbkPg0Ljhlp1CYnF3Dj03wbBnSJOaMywTXLKcOkGyICBnJrhsog9Lj7Xlo3y078s/qSx9/z+e9/N5XoMdy3Z5BpwBOaAgTw44B14CM2AC3ANDYCDPEBg5Fo+7bJ+xI9MvgBJQB97H/Lke0AH6jsU0sQBsFwMoAs0tmI6C0QJuHIs/iQAgxsvAFwnpfWgM1IDruCCMmObfAl+B1xxGd0DVsfi+VwC2y3MJxU8kQ22g6Vj83jkA2+UN8AM4IVmaAXnH4nadl8w1zX+Q6Slp5pE2DaWN2wdgu3wEuiRfXWnr9gDYLp+BS9KjS2lz/DFAaKbJvF8Nx+JqYwDSn7qkWxXH4tvaAGS0H6KGcmGzgxExzz8kdLTfdIo8DVonhA2CLYXMz6fI1koRIMvbAWqqsLhsNgI2Nj8PuLbfx94h699ALXaBssLmEW/lwAiQf//XHre0h9IYeDWPAn8EFDUwj3gsBnWBJvqo+aQLyBneBL2UcSym8wgooZ9K/i5Q1xBAHcCQc/sZeurExEta6KozEy9Lo6tyJl6qSlcVjgCOXcDL0uqqcxPIaAwgY6K5TA33AH5NTLzKDF11b6LO0fcmGpqoewC6igZHAMcuACONAYxMqcPraWi+51g8zhdCHQ0BdOYLIYC+hgD6/wBIBWZPs/Cf+iMAQrKniqrl3wvMdYOXNlJdY/H6FIDkymoaAKhFZYev8VLIqupOPBIIQMhUFQZQXSyu/u9ARCoo2gqabwcVVYedCDVRK1s0IyT7HQhAqqnyCgHIh1WSh54JSl1dRQHzlagK8shDUamwbKTYfCOqSnQpAIFwBVyk0PzFsjphWOPCRMqKphurmF8LgEBIQ/F0ZVnYbwxAIOh7ZcY3O5wmbLHUxiuGvl33Re2vzcXKDcqHs8C7PW+lx/LNbBzzsSNgIRr0vDobAkO/y9MRMBJ9ff4vhV3btM/DqFkAAAAASUVORK5CYII=');
var greenDotOverlay = greenDot.createOverlay(
	{'scale':
		{'magnitude': 0.15, 'reference': gapi.hangout.av.effects.ScaleReference.WIDTH}});

var yellowDot = gapi.hangout.av.effects.createImageResource('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAAAXNSR0IArs4c6QAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB90BDwMXCA5pLkoAAAAdaVRYdENvbW1lbnQAAAAAAENyZWF0ZWQgd2l0aCBHSU1QZC5lBwAAAuFJREFUeNrtmyts21AUhj97BiPVCiZLBUVzpAVUKykuqca2gLDgsoVNKwoMDWtZ8NhAFloSXJJpIJXmooJIVkCjkoFbbcAnk5vZzsN52Pfml8yS+P5fzn2ecy3WrMB3XgAHQBk4lqcMHAKvAQU8APfAAOjLMwCGrqee1tk+a02mXwGnwDnwIePPdYE20HM9Nc4tgMB3LOAEaKzAdBqMJnDjeupPLgCI8TPgq4T0JjQCasB1VhBWRvPvgG/AG7ajO6DqeurHRgEEvvNSQvEz+VALaLie+r12AIHvvAV+Ag75kgKOXE/dLvIle0HzH2V6ypt5pE0DaePqAQS+8wnokH91pK2rAxD4zhfgkuLoUtqcfQwQmkUyH1Xd9dTV0gCkP3Uotiqup74vDEBG+wF6qJw0O1gp8/xjTkf7ZafIvbh1QtIg2NTI/GSKbM4VAbK87aOnjqeXzVbMxubXFtf2m9g7lKIbqOkucKaxecTbWWwEyL8fbHBLuy2NAHcSBdEIODHAPOLxJK4LNDBHjWddQM7wHjBL+66nxpMIOMU8nUa7wLmBAM4BLDm3V5gpxyZMWpiqA5swS2OqyjZhqspUHe8A7LpAmKU1VYc2sG8wgH0bw2UbuAeI6sEmrMwwVfc2+hx9L6OBjb4HoPOovwOw6wIwNBjA0JY6vK6B5ruup54mC6G2gQDak4UQQM9AAL1/AKQCs2tY+I+jEQAJ2VNN1YzuBSa6IUwb6a6ReH0OQHJlNQMA1NKyw9eEKWRddSceiQUgZKoaA6hOF1f/dyAiFRQtDc234oqqd0VScZ+WDx5p9O8fJVWSJ54JSl1dRQPzlbQK8tRDUamwrBfYfD2tSnQmAIFwBVwU0PzFrDrhxEEwYWAsUtF0fR7zCwEQCEUonq7MCvulAQgEc6/MRGaHvZwtlloyz98u+kXjr81lyg3Ki0vA+w1vpUfyzlIW85kjYCoazLw6mwDDvMvTKTByfX3+L4KL/xorzXR/AAAAAElFTkSuQmCC');
	var yellowDotOverlay = yellowDot.createOverlay(
	{'scale':
		{'magnitude': 0.15, 'reference': gapi.hangout.av.effects.ScaleReference.WIDTH}});

var buttonDisabled = false;	//true when button is clicked to prevent joining the queue several times

gapi.hangout.onApiReady.add(function(eventObj){
	if (eventObj.isApiReady) {
		buttonDisabled = false;
		participants = gapi.hangout.getEnabledParticipants();
		onDataChange();

		gapi.hangout.onParticipantsChanged.add(onParticipantsChange);
		gapi.hangout.data.onStateChanged.add(onDataChange);

		setInterval(updateTimeOutText, 200);
	}
});

function onParticipantsChange(eventObj) {
	participants = eventObj.participants;

	onServerUpdate();
};

function onDataChange (eventObj){
	var state = gapi.hangout.data.getState();
	queue = JSON.parse(state.queue);
	try{
		timeOut = JSON.parse(state.timeOut) - (JSON.parse(state.serverTime) - new Date().getTime());
	}
	catch (e){
		console.log(e);
		timeOut = -1;
	}

	onServerUpdate();
}

function onServerUpdate(){
	console.log("onServerUpdate called");
	buttonDisabled = false;
	printParticipants();
	clearOverlay();
	var queuePosition = queue.indexOf(gapi.hangout.getParticipantId());
	if (queuePosition == 0){
		placeGreenDot();
		document.getElementById('queueButton').innerHTML = "I Am Done";
	}
	else if (queuePosition > 0){
		placeYellowDot();
		document.getElementById('queueButton').innerHTML = "Put Hand Down";
	}
	else{
		document.getElementById('queueButton').innerHTML = "Raise My Hand";
	}

	//lowest order participant manages time updates
	//and checks for disconnected queue memembers
	if (isManager()){
		console.log("managing update...");
		var validSpeaker = false;
		var updateRequired = false;
		while (queue.length > 0 && !validSpeaker){

			var speakerInHangout = false;
			for (var i = 0; i < participants.length; i++){
				if (participants[i].id == queue[0]){
					if (!participants[i].hasAppEnabled){
						console.log("speaker doesn't have app on");
						queue.shift();
						updateRequired = true;
						speakerInHangout = true;
					}
					else{
						console.log("speaker running app");
						validSpeaker = true;
						speakerInHangout = true;
					}
				}
			}
			if (!speakerInHangout){
				console.log("speaker not in hangout");
				queue.shift();
				updateRequire = true;
			}
		}
		if (timeOut == -1 || new Date().getTime() > timeOut){
			console.log('new timeout value required');
			updateRequired = true;
		}
		if (updateRequired){
			console.log("sending new update");
			sendNewSpeaker();
		}
	}
}

//sends queue and updated timestamps
function sendNewSpeaker(){
	timeOut = new Date().getTime() + 1000*60;
	gapi.hangout.data.submitDelta({	'queue':JSON.stringify(queue),
									'timeOut': JSON.stringify(timeOut),
									'serverTime': JSON.stringify(new Date().getTime())});
}

function printParticipants(){
	if (queue.length > 0){
		document.getElementById('speakerName').innerHTML = getParticipantNameById(queue[0]);
		document.getElementById('speakerDisplay').style.display = 'inline';
	}
	else {
		document.getElementById('speakerDisplay').style.display = 'none';
	}

	var queueHTML = ""
	if (queue.length > 1){
		for (var i = 1; i < queue.length; i++){
			queueHTML = queueHTML + getParticipantNameById(queue[i]) + '
';
		}
		document.getElementById('queueMembers').innerHTML = queueHTML;
		document.getElementById('queueMembers').style.display = 'inline';
	}
	else {
		document.getElementById('queueMembers').style.display = 'none';
	}

	var memberHTML = "";
	for (var i = 0; i < participants.length; i++){
		if (queue.indexOf(participants[i].id) == -1){
			memberHTML = memberHTML + participants[i].person.displayName + '
';
		}
	}
	document.getElementById('otherParticipants').innerHTML = memberHTML;
}

function getParticipantNameById(id){
	for (var i = 0; i < participants.length; i++){
		if (participants[i].id == id){
			return participants[i].person.displayName;
		}
	}
	return '';
}

function buttonClick(){
	if (!buttonDisabled){
		buttonDisabled = true;
		var queuePosition = queue.indexOf(gapi.hangout.getParticipantId());
		if (queuePosition == 0){
			queue.shift();
			gapi.hangout.data.submitDelta({'queue':JSON.stringify(queue)});
		}
		else if (queuePosition == -1){
			queue.push(gapi.hangout.getParticipantId());
			gapi.hangout.data.submitDelta({'queue':JSON.stringify(queue)});
		}
	}
}

function placeGreenDot(){
	greenDotOverlay.setPosition(.4, -.35);
	greenDotOverlay.setVisible(true);
}

function placeYellowDot(){
	yellowDotOverlay.setPosition(.4, -.35);
	yellowDotOverlay.setVisible(true);
}

function clearOverlay(){
	greenDotOverlay.setVisible(false);
	yellowDotOverlay.setVisible(false);
}

//only one participant should be manager; this code might need updating
//might not be necessary at all - ok to send multiple identical updates
function isManager(){
	return true;
	return (participants[0].id == gapi.hangout.getParticipantId());
}

//what happens if manager is alt tabbed?
function updateTimeOutText(){
	if (timeOut != -1 && queue.length > 0){
		var timeDif = timeOut - new Date().getTime();
		document.getElementById('timeLeft').innerHTML = '  - ' + Math.max(0, Math.round(timeDif/1000)) + ' secounds';
		if (isManager() && timeDif < 0 || timeDif < 2000){
			console.log("Time up!");
			//force update iff queue is empty
			if (queue.length > 1){
				queue.shift();
			}
			sendNewSPeaker();
		}
	}
	else{
		document.getElementById('timeLeft').innerHTML = '';
	}
}<commit_msg>removed mistaken caps<commit_after>var participants = [];		//people currently running an instance of the hangout
var queue = [];				//list of participant ids waiting to speak. 0 is the current speaker
var timeOut = -1;				//unix time of when the current speaker's turn will end
var currentSpeakerId;		//id of the current speaker; on updates if it doesn't match queue, manager sets new time

var greenDot = gapi.hangout.av.effects.createImageResource('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAAAXNSR0IArs4c6QAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB90BDwMNIxL4LdEAAAAdaVRYdENvbW1lbnQAAAAAAENyZWF0ZWQgd2l0aCBHSU1QZC5lBwAAAuNJREFUeNrtmy2M2mAYx3/tTsxchliWnDi1kAxBhsE0aTBkbkPg0Ljhlp1CYnF3Dj03wbBnSJOaMywTXLKcOkGyICBnJrhsog9Lj7Xlo3y078s/qSx9/z+e9/N5XoMdy3Z5BpwBOaAgTw44B14CM2AC3ANDYCDPEBg5Fo+7bJ+xI9MvgBJQB97H/Lke0AH6jsU0sQBsFwMoAs0tmI6C0QJuHIs/iQAgxsvAFwnpfWgM1IDruCCMmObfAl+B1xxGd0DVsfi+VwC2y3MJxU8kQ22g6Vj83jkA2+UN8AM4IVmaAXnH4nadl8w1zX+Q6Slp5pE2DaWN2wdgu3wEuiRfXWnr9gDYLp+BS9KjS2lz/DFAaKbJvF8Nx+JqYwDSn7qkWxXH4tvaAGS0H6KGcmGzgxExzz8kdLTfdIo8DVonhA2CLYXMz6fI1koRIMvbAWqqsLhsNgI2Nj8PuLbfx94h699ALXaBssLmEW/lwAiQf//XHre0h9IYeDWPAn8EFDUwj3gsBnWBJvqo+aQLyBneBL2UcSym8wgooZ9K/i5Q1xBAHcCQc/sZeurExEta6KozEy9Lo6tyJl6qSlcVjgCOXcDL0uqqcxPIaAwgY6K5TA33AH5NTLzKDF11b6LO0fcmGpqoewC6igZHAMcuACONAYxMqcPraWi+51g8zhdCHQ0BdOYLIYC+hgD6/wBIBWZPs/Cf+iMAQrKniqrl3wvMdYOXNlJdY/H6FIDkymoaAKhFZYev8VLIqupOPBIIQMhUFQZQXSyu/u9ARCoo2gqabwcVVYedCDVRK1s0IyT7HQhAqqnyCgHIh1WSh54JSl1dRQHzlagK8shDUamwbKTYfCOqSnQpAIFwBVyk0PzFsjphWOPCRMqKphurmF8LgEBIQ/F0ZVnYbwxAIOh7ZcY3O5wmbLHUxiuGvl33Re2vzcXKDcqHs8C7PW+lx/LNbBzzsSNgIRr0vDobAkO/y9MRMBJ9ff4vhV3btM/DqFkAAAAASUVORK5CYII=');
var greenDotOverlay = greenDot.createOverlay(
	{'scale':
		{'magnitude': 0.15, 'reference': gapi.hangout.av.effects.ScaleReference.WIDTH}});

var yellowDot = gapi.hangout.av.effects.createImageResource('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAAAXNSR0IArs4c6QAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB90BDwMXCA5pLkoAAAAdaVRYdENvbW1lbnQAAAAAAENyZWF0ZWQgd2l0aCBHSU1QZC5lBwAAAuFJREFUeNrtmyts21AUhj97BiPVCiZLBUVzpAVUKykuqca2gLDgsoVNKwoMDWtZ8NhAFloSXJJpIJXmooJIVkCjkoFbbcAnk5vZzsN52Pfml8yS+P5fzn2ecy3WrMB3XgAHQBk4lqcMHAKvAQU8APfAAOjLMwCGrqee1tk+a02mXwGnwDnwIePPdYE20HM9Nc4tgMB3LOAEaKzAdBqMJnDjeupPLgCI8TPgq4T0JjQCasB1VhBWRvPvgG/AG7ajO6DqeurHRgEEvvNSQvEz+VALaLie+r12AIHvvAV+Ag75kgKOXE/dLvIle0HzH2V6ypt5pE0DaePqAQS+8wnokH91pK2rAxD4zhfgkuLoUtqcfQwQmkUyH1Xd9dTV0gCkP3Uotiqup74vDEBG+wF6qJw0O1gp8/xjTkf7ZafIvbh1QtIg2NTI/GSKbM4VAbK87aOnjqeXzVbMxubXFtf2m9g7lKIbqOkucKaxecTbWWwEyL8fbHBLuy2NAHcSBdEIODHAPOLxJK4LNDBHjWddQM7wHjBL+66nxpMIOMU8nUa7wLmBAM4BLDm3V5gpxyZMWpiqA5swS2OqyjZhqspUHe8A7LpAmKU1VYc2sG8wgH0bw2UbuAeI6sEmrMwwVfc2+hx9L6OBjb4HoPOovwOw6wIwNBjA0JY6vK6B5ruup54mC6G2gQDak4UQQM9AAL1/AKQCs2tY+I+jEQAJ2VNN1YzuBSa6IUwb6a6ReH0OQHJlNQMA1NKyw9eEKWRddSceiQUgZKoaA6hOF1f/dyAiFRQtDc234oqqd0VScZ+WDx5p9O8fJVWSJ54JSl1dRQPzlbQK8tRDUamwrBfYfD2tSnQmAIFwBVwU0PzFrDrhxEEwYWAsUtF0fR7zCwEQCEUonq7MCvulAQgEc6/MRGaHvZwtlloyz98u+kXjr81lyg3Ki0vA+w1vpUfyzlIW85kjYCoazLw6mwDDvMvTKTByfX3+L4KL/xorzXR/AAAAAElFTkSuQmCC');
	var yellowDotOverlay = yellowDot.createOverlay(
	{'scale':
		{'magnitude': 0.15, 'reference': gapi.hangout.av.effects.ScaleReference.WIDTH}});

var buttonDisabled = false;	//true when button is clicked to prevent joining the queue several times

gapi.hangout.onApiReady.add(function(eventObj){
	if (eventObj.isApiReady) {
		buttonDisabled = false;
		participants = gapi.hangout.getEnabledParticipants();
		onDataChange();

		gapi.hangout.onParticipantsChanged.add(onParticipantsChange);
		gapi.hangout.data.onStateChanged.add(onDataChange);

		setInterval(updateTimeOutText, 200);
	}
});

function onParticipantsChange(eventObj) {
	participants = eventObj.participants;

	onServerUpdate();
};

function onDataChange (eventObj){
	var state = gapi.hangout.data.getState();
	queue = JSON.parse(state.queue);
	try{
		timeOut = JSON.parse(state.timeOut) - (JSON.parse(state.serverTime) - new Date().getTime());
	}
	catch (e){
		console.log(e);
		timeOut = -1;
	}

	onServerUpdate();
}

function onServerUpdate(){
	console.log("onServerUpdate called");
	buttonDisabled = false;
	printParticipants();
	clearOverlay();
	var queuePosition = queue.indexOf(gapi.hangout.getParticipantId());
	if (queuePosition == 0){
		placeGreenDot();
		document.getElementById('queueButton').innerHTML = "I Am Done";
	}
	else if (queuePosition > 0){
		placeYellowDot();
		document.getElementById('queueButton').innerHTML = "Put Hand Down";
	}
	else{
		document.getElementById('queueButton').innerHTML = "Raise My Hand";
	}

	//lowest order participant manages time updates
	//and checks for disconnected queue memembers
	if (isManager()){
		console.log("managing update...");
		var validSpeaker = false;
		var updateRequired = false;
		while (queue.length > 0 && !validSpeaker){

			var speakerInHangout = false;
			for (var i = 0; i < participants.length; i++){
				if (participants[i].id == queue[0]){
					if (!participants[i].hasAppEnabled){
						console.log("speaker doesn't have app on");
						queue.shift();
						updateRequired = true;
						speakerInHangout = true;
					}
					else{
						console.log("speaker running app");
						validSpeaker = true;
						speakerInHangout = true;
					}
				}
			}
			if (!speakerInHangout){
				console.log("speaker not in hangout");
				queue.shift();
				updateRequire = true;
			}
		}
		if (timeOut == -1 || new Date().getTime() > timeOut){
			console.log('new timeout value required');
			updateRequired = true;
		}
		if (updateRequired){
			console.log("sending new update");
			sendNewSpeaker();
		}
	}
}

//sends queue and updated timestamps
function sendNewSpeaker(){
	timeOut = new Date().getTime() + 1000*60;
	gapi.hangout.data.submitDelta({	'queue':JSON.stringify(queue),
									'timeOut': JSON.stringify(timeOut),
									'serverTime': JSON.stringify(new Date().getTime())});
}

function printParticipants(){
	if (queue.length > 0){
		document.getElementById('speakerName').innerHTML = getParticipantNameById(queue[0]);
		document.getElementById('speakerDisplay').style.display = 'inline';
	}
	else {
		document.getElementById('speakerDisplay').style.display = 'none';
	}

	var queueHTML = ""
	if (queue.length > 1){
		for (var i = 1; i < queue.length; i++){
			queueHTML = queueHTML + getParticipantNameById(queue[i]) + '
';
		}
		document.getElementById('queueMembers').innerHTML = queueHTML;
		document.getElementById('queueMembers').style.display = 'inline';
	}
	else {
		document.getElementById('queueMembers').style.display = 'none';
	}

	var memberHTML = "";
	for (var i = 0; i < participants.length; i++){
		if (queue.indexOf(participants[i].id) == -1){
			memberHTML = memberHTML + participants[i].person.displayName + '
';
		}
	}
	document.getElementById('otherParticipants').innerHTML = memberHTML;
}

function getParticipantNameById(id){
	for (var i = 0; i < participants.length; i++){
		if (participants[i].id == id){
			return participants[i].person.displayName;
		}
	}
	return '';
}

function buttonClick(){
	if (!buttonDisabled){
		buttonDisabled = true;
		var queuePosition = queue.indexOf(gapi.hangout.getParticipantId());
		if (queuePosition == 0){
			queue.shift();
			gapi.hangout.data.submitDelta({'queue':JSON.stringify(queue)});
		}
		else if (queuePosition == -1){
			queue.push(gapi.hangout.getParticipantId());
			gapi.hangout.data.submitDelta({'queue':JSON.stringify(queue)});
		}
	}
}

function placeGreenDot(){
	greenDotOverlay.setPosition(.4, -.35);
	greenDotOverlay.setVisible(true);
}

function placeYellowDot(){
	yellowDotOverlay.setPosition(.4, -.35);
	yellowDotOverlay.setVisible(true);
}

function clearOverlay(){
	greenDotOverlay.setVisible(false);
	yellowDotOverlay.setVisible(false);
}

//only one participant should be manager; this code might need updating
//might not be necessary at all - ok to send multiple identical updates
function isManager(){
	return true;
	return (participants[0].id == gapi.hangout.getParticipantId());
}

//what happens if manager is alt tabbed?
function updateTimeOutText(){
	if (timeOut != -1 && queue.length > 0){
		var timeDif = timeOut - new Date().getTime();
		document.getElementById('timeLeft').innerHTML = '  - ' + Math.max(0, Math.round(timeDif/1000)) + ' secounds';
		if (isManager() && timeDif < 0 || timeDif < 2000){
			console.log("Time up!");
			//force update iff queue is empty
			if (queue.length > 1){
				queue.shift();
			}
			sendNewSpeaker();
		}
	}
	else{
		document.getElementById('timeLeft').innerHTML = '';
	}
}<|endoftext|>


<commit_before>// Entity Order

App.Entity.Order = (function() {

	var orderId = 1;

	function Order( data ) {
		this.orderId = orderId++;

		this.customerId = data.customerId;
		this.shipmentDestination = data.shipmentDestination;
		this.paymentInformation = data.paymentInformation;
		this.customerContactInfo = data.customerContactInfo;
		this.shipmentMechanism = data.shipmentMechanism;
	}

	Order.validate = function( order ) {
		// some existence checking here
		// you should do real validation!
		return order.customerId
			&& order.shipmentDestination
			&& order.paymentInformation
			&& order.customerContactInfo
			&& order.shipmentMechanism;
	};

	return Order;

})();
<commit_msg>added check if order was passed<commit_after>// Entity Order

App.Entity.Order = (function() {

	var orderId = 1;

	function Order( data ) {
		this.orderId = orderId++;

		this.customerId = data.customerId;
		this.shipmentDestination = data.shipmentDestination;
		this.paymentInformation = data.paymentInformation;
		this.customerContactInfo = data.customerContactInfo;
		this.shipmentMechanism = data.shipmentMechanism;
	}

	Order.validate = function( order ) {
		if (order === undefined) {
			throw new Error('no order passed');
		}

		// some existence checking here
		// you should do real validation!
		return order.customerId
			&& order.shipmentDestination
			&& order.paymentInformation
			&& order.customerContactInfo
			&& order.shipmentMechanism;
	};

	return Order;

})();
<|endoftext|>


<commit_before>/*
 * eHistory Chrome Extension
 * https://chrome.google.com/webstore/detail/hiiknjobjfknoghbeelhfilaaikffopb
 *
 * Copyright 2011, Amjad Masad
 * Licensed under the MIT license
 * https://github.com/amasad/eHistory/blob/master/LICENSE.txt
 *
 */
(function(){
  /* global parseQuery, historyModel, historyView, EHistory, confirm */
  'use strict';
  /* parseForm: Parses the html form into text format
   *    @arg (jQueryObject) $form: jQuery object containing the form element
   *    @returns (String) text query equivalent to the form
   * intitle:title inurl:url site:site startTime:startime endTime:endtime searchquery
   */
  function parseForm ($form) {
    var query = '',
        text = '';
    // loop over all input elements
    $form.find('input').each(function (i, elem) {
      elem = $(elem);
      if (elem.attr('id') === 'pure-text') {
        // just text
        text += elem.val();
      } else {
        // filter/setting
        query += elem.val() ?  ' ' + elem.data('settings-item') + ':' + elem.val() : '';
      }
    });
    // return filter/setting text format key:value followed by regular text
    return $.trim(query + ' ' + text);
  }

  // Check version number
  $(function() {
    $.getJSON('manifest.json', function (manifest) {
      var version = manifest.version;
      if (localStorage['version'] !== version) {
        localStorage.clear();
        localStorage['version'] = version;
        /* global console */
        console.log('Version Updated!');
      }
    });
  });

  /*************** Controller  ***************/
  /*  Collection of functions and event handlers
   *    Interacts with UI, historyModel and historyView
   */
  $(function(){
    // DOM ready
    // search box
    var $query = $('#query');
    // advanced search form
    var $pnlAdvanced = $('.advanced-search');
    // history items table
    var $resultsTable = $('#tbl-main');

    $('.open-advanced').click(function () {
      var config = parseQuery($query.val());
      var operators = $.extend(config.settings, config.filters);
      $pnlAdvanced.find('input').each(function (i, elem) {
        elem = $(elem);
        if (elem.attr('id') === 'pure-text'){
          elem.val(config[2]);
        } else {
          // in the elements data contains type of settings/filter
          elem.val(operators[elem.data('settings-item')] || '');
        }
      });
    });

    // results day headers check-boxs handler
    $resultsTable.delegate('.chk-day', 'change', function () {
      // check all results until the next day header
      $(this).parents('tr').nextUntil('.hdr-day')
          .children(':nth-child(1)').children()
              .attr('checked',  $(this).attr('checked')).trigger('change');
    });

    // result item checkbox handler
    $resultsTable.delegate('.chk-entry', 'change', function () {
      var val =  $(this).attr('checked'),
          $row = $(this).parents('tr'),
          // decides what function to call, select/unselect
          fn = val ? $.proxy(historyModel.select, historyModel) : $.proxy(historyModel.unselect, historyModel);
      fn($row.data('id'),$row.data('day'));
    });

    // Update the main search box whenever advanced settings are changed.
    var updateMainSearchBox = function () {
      // Delay until the keypress is handled by the browser.
      setTimeout(function() {
        $query.val(parseForm($pnlAdvanced) || '');
      }, 0);
    };

    $('input', $pnlAdvanced).change(updateMainSearchBox)
                            .keypress(updateMainSearchBox)
                            .keydown(updateMainSearchBox);

    $('body').bind('click', function (e) {
      if ($(e.srcElement).parents('.advanced-search').length) return;
      $('.open-advanced').show();
      $('.advanced-search').hide();
    });
    $('.open-advanced').click(function () {
      $('.advanced-search').show();
      $('.open-advanced').hide();
      return false;
    });

    // called to initiate the search
    function search(config) {
      var settings = config.settings,
          filters = config.filters;

      historyView.displayThrobber();

        EHistory.search({
          text: settings.text || '',
          startTime: new Date(settings.startTime || 0).getTime() ,
          endTime: new Date(settings.endTime || Date.now()).getTime(),
          maxResults: historyModel.pageSize
        }, filters, function(results){
          historyModel.append(results);
        });
    }

    // form submit handler
    $('#frm-search').submit(function (e) {
      var text;
      e.preventDefault();
      //clear everything
      historyModel.clear();
      historyView.clear();
      historyView.disableControls();

      if ($pnlAdvanced.is(':visible')){
        text = parseForm($pnlAdvanced);
        search(parseQuery(text));
      } else {
        search(parseQuery($query.val()));
      }
      //return false;
    });

    $('#btn-clear-history').click(function () {
      if (confirm('Delete all items from history?')) {
        historyModel.clearHistory();
      }
    });

    $('.delete.selected').click(function () {
      if (confirm('Delete selected items?')) {
        historyModel.removeSelected();
      }
    });

    $('.delete.results').click(function () {
      if (confirm('Delete all search results?')) {
        historyModel.clearResults();
      }
    });

    $('.query').focus(function () {
      $('.query-wrapper').addClass('active');
    });
    $('.query').blur(function () {
      $('.query-wrapper').removeClass('active');
    });

    $(document).delegate('.hdr-day, .entry', 'click', function (e) {
      if ($(e.target).is('input')) return;
      $(this).find('input[type=checkbox]').click();
    });

    // Focus query box by default.
    $query.focus();
  });

  $(function () {

    $(window).resize(function () {
      $('#div-main').css('height', $(window).height() - 85 - 45);
    }).resize();
    $('#frm-search').submit();
  });

})();
<commit_msg>Implement shift+click selection. Fixes #9<commit_after>/*
 * eHistory Chrome Extension
 * https://chrome.google.com/webstore/detail/hiiknjobjfknoghbeelhfilaaikffopb
 *
 * Copyright 2011, Amjad Masad
 * Licensed under the MIT license
 * https://github.com/amasad/eHistory/blob/master/LICENSE.txt
 *
 */
(function(){
  /* global parseQuery, historyModel, historyView, EHistory, confirm */
  'use strict';
  /* parseForm: Parses the html form into text format
   *    @arg (jQueryObject) $form: jQuery object containing the form element
   *    @returns (String) text query equivalent to the form
   * intitle:title inurl:url site:site startTime:startime endTime:endtime searchquery
   */
  function parseForm ($form) {
    var query = '',
        text = '';
    // loop over all input elements
    $form.find('input').each(function (i, elem) {
      elem = $(elem);
      if (elem.attr('id') === 'pure-text') {
        // just text
        text += elem.val();
      } else {
        // filter/setting
        query += elem.val() ?  ' ' + elem.data('settings-item') + ':' + elem.val() : '';
      }
    });
    // return filter/setting text format key:value followed by regular text
    return $.trim(query + ' ' + text);
  }

  // Check version number
  $(function() {
    $.getJSON('manifest.json', function (manifest) {
      var version = manifest.version;
      if (localStorage['version'] !== version) {
        localStorage.clear();
        localStorage['version'] = version;
        /* global console */
        console.log('Version Updated!');
      }
    });
  });

  /*************** Controller  ***************/
  /*  Collection of functions and event handlers
   *    Interacts with UI, historyModel and historyView
   */
  $(function(){
    // DOM ready
    // search box
    var $query = $('#query');
    // advanced search form
    var $pnlAdvanced = $('.advanced-search');
    // history items table
    var $resultsTable = $('#tbl-main');

    $('.open-advanced').click(function () {
      var config = parseQuery($query.val());
      var operators = $.extend(config.settings, config.filters);
      $pnlAdvanced.find('input').each(function (i, elem) {
        elem = $(elem);
        if (elem.attr('id') === 'pure-text'){
          elem.val(config[2]);
        } else {
          // in the elements data contains type of settings/filter
          elem.val(operators[elem.data('settings-item')] || '');
        }
      });
    });

    // results day headers check-boxs handler
    $resultsTable.delegate('.chk-day', 'change', function () {
      // check all results until the next day header
      $(this).parents('tr').nextUntil('.hdr-day')
          .children(':nth-child(1)').children()
              .attr('checked',  $(this).attr('checked')).trigger('change');
    });

    var shiftDown = false;
    $(document).bind('keydown keyup', function (e) {
      shiftDown = e.shiftKey;
    });

    $resultsTable.delegate('.chk-entry', 'click', function () {
      if (shiftDown) {
        shiftClick($(this));
      }
    });

    var shiftClick = (function () {

      function getPath($firstChecked, dir, isChecked) {
        var $path = $();
        while ($firstChecked.length) {
          $firstChecked = $firstChecked[dir]();
          if (!$firstChecked.is('.entry')) {
            continue;
          } else if ((isChecked && $firstChecked.find(':checked').length) ||
              (!isChecked && !$firstChecked.find(':checked').length)) {
            break;
          } else {
            $path = $path.add($firstChecked);
          }
        }

        // If we reached the end and we're looking for checked boxes then
        // we haven't found any, however, if you we reached the end while
        // looking for checked boxes, we may have something.
        if ($firstChecked.length || !isChecked) {
          return $path;
        } else {
          return $();
        }
      }

      return function ($input) {
        var $row = $input.parents('tr');
        var isChecked = $input.is(':checked');
        // Go up until we find the first checked input
        var $path = getPath($row, 'prev', isChecked);
        // If we couldn't find anything going up then go down.
        if (!$path.length) {
          $path = getPath($row, 'next', isChecked);
        }
        $path.each(function (i, row) {
          if ($(row).is('.entry')) {
            $(row).find('input[type=checkbox]').attr('checked', isChecked);
          }
        });
      };

    })();

    // result item checkbox handler
    $resultsTable.delegate('.chk-entry', 'change', function () {
      var val =  $(this).attr('checked'),
          $row = $(this).parents('tr'),
          // decides what function to call, select/unselect
          fn = val ? $.proxy(historyModel.select, historyModel) : $.proxy(historyModel.unselect, historyModel);
      fn($row.data('id'),$row.data('day'));
    });

    $(document).delegate('.hdr-day, .entry', 'click', function (e) {
      if ($(e.target).is('input')) return;
      var $input = $(this).find('input[type=checkbox]');
      $input
        .attr('checked', !$input.is(':checked'))
        .trigger('change');
      if (shiftDown) {
        shiftClick($input);
      }
    });

    // Update the main search box whenever advanced settings are changed.
    var updateMainSearchBox = function () {
      // Delay until the keypress is handled by the browser.
      setTimeout(function() {
        $query.val(parseForm($pnlAdvanced) || '');
      }, 0);
    };

    $('input', $pnlAdvanced).change(updateMainSearchBox)
                            .keypress(updateMainSearchBox)
                            .keydown(updateMainSearchBox);

    $('body').bind('click', function (e) {
      if ($(e.srcElement).parents('.advanced-search').length) return;
      $('.open-advanced').show();
      $('.advanced-search').hide();
    });
    $('.open-advanced').click(function () {
      $('.advanced-search').show();
      $('.open-advanced').hide();
      return false;
    });

    // called to initiate the search
    function search(config) {
      var settings = config.settings,
          filters = config.filters;

      historyView.displayThrobber();

        EHistory.search({
          text: settings.text || '',
          startTime: new Date(settings.startTime || 0).getTime() ,
          endTime: new Date(settings.endTime || Date.now()).getTime(),
          maxResults: historyModel.pageSize
        }, filters, function(results){
          historyModel.append(results);
        });
    }

    // form submit handler
    $('#frm-search').submit(function (e) {
      var text;
      e.preventDefault();
      //clear everything
      historyModel.clear();
      historyView.clear();
      historyView.disableControls();

      if ($pnlAdvanced.is(':visible')){
        text = parseForm($pnlAdvanced);
        search(parseQuery(text));
      } else {
        search(parseQuery($query.val()));
      }
      //return false;
    });

    $('#btn-clear-history').click(function () {
      if (confirm('Delete all items from history?')) {
        historyModel.clearHistory();
      }
    });

    $('.delete.selected').click(function () {
      if (confirm('Delete selected items?')) {
        historyModel.removeSelected();
      }
    });

    $('.delete.results').click(function () {
      if (confirm('Delete all search results?')) {
        historyModel.clearResults();
      }
    });

    $('.query').focus(function () {
      $('.query-wrapper').addClass('active');
    });
    $('.query').blur(function () {
      $('.query-wrapper').removeClass('active');
    });

    // Focus query box by default.
    $query.focus();
  });

  $(function () {

    $(window).resize(function () {
      $('#div-main').css('height', $(window).height() - 85 - 45);
    }).resize();
    $('#frm-search').submit();
  });

})();
<|endoftext|>


<commit_before>var opbeatCore = require('opbeat-js-core')
var ServiceFactory = opbeatCore.ServiceFactory
var angularInitializer = require('./angularInitializer')
require('zone.js')
function init () {
  var serviceFactory = new ServiceFactory()
  angularInitializer(serviceFactory)
}

init()
<commit_msg>feat(opbeat-angular): check for global serviceFactory<commit_after>var opbeatCore = require('opbeat-js-core')
var ServiceFactory = opbeatCore.ServiceFactory
var angularInitializer = require('./angularInitializer')

require('zone.js')
function init () {
  var serviceFactory = new ServiceFactory()
  if (window.opbeatApi && window.opbeatApi.serviceFactory) {
    serviceFactory = window.opbeatApi.serviceFactory
  }
  angularInitializer(serviceFactory)
}

init()
<|endoftext|>


<commit_before>//= require folio/webp
//= require photoswipe.esm.folio
//= require photoswipe-dynamic-caption-plugin.esm

window.Folio = window.Folio || {}
window.Folio.Lightbox = {}

window.Folio.Lightbox.calls = []

window.Folio.Lightbox.instances = []

window.Folio.Lightbox.authorLabel = document.lang === 'cs' ? 'Foto' : 'Photo'

window.Folio.Lightbox.bind = (selector, options) => {
  options = options || {}

  window.Folio.Lightbox.calls.push([selector, options])

  const init = () => {
    const $items = $(selector)

    if ($items.length === 0) return

    if (options.individual) {
      $items.each(function () {
        let individualSelector = `.${this.className.replace(/\s+/g, '.')}`

        if (options.itemSelector) {
          individualSelector = `${individualSelector} ${options.itemSelector}`
        }

        window.Folio.Lightbox.instances.push(new window.Folio.Lightbox.Lightbox({ selector: individualSelector, options }))
      })
    } else if (options.fromData) {
      $items.each((i, el) => {
        window.Folio.Lightbox.instances.push(new window.Folio.Lightbox.Lightbox({ selector, options, data: $(el).data('lightbox-image-data') }))
      })
    } else {
      window.Folio.Lightbox.instances.push(new window.Folio.Lightbox.Lightbox({ selector, options }))
    }
  }

  if (typeof Turbolinks !== 'undefined' && Turbolinks !== null) {
    $(document)
      .on('turbolinks:load', init)
      .on('turbolinks:before-render', function () {
        if (window.Folio.Lightbox.instances.length === 0) return

        window.Folio.Lightbox.instances.forEach((instance) => {
          instance.destroy()
        })

        window.Folio.Lightbox.instances = []
      })
  } else {
    $(() => setTimeout(init, 0))
  }
}

window.Folio.Lightbox.updateAll = () => {
  window.Folio.Lightbox.instances.forEach((instance) => instance.destroy())

  window.Folio.Lightbox.instances = []

  window.Folio.Lightbox.calls.forEach((call) => window.Folio.Lightbox.bind(call[0], call[1]))
}

window.Folio.Lightbox.Lightbox = class FolioLightbox {
  constructor (attrs) {
    this.selector = attrs.selector
    this.options = attrs.options

    if (attrs.additionalSelector) {
      this.fullSelector = `${this.selector}, ${attrs.additionalSelector}`
    } else {
      this.fullSelector = this.selector
    }

    this.eventIdentifier = 'folioLightbox'
    this.$html = $(document.documentElement)

    this.bind(attrs.data)
  }

  bind (data) {
    this.unbind()
    const that = this

    $(document).on(`click.${this.eventIdentifier}`, this.fullSelector, function (e) {
      e.preventDefault()

      let items = data || that.items()

      if (that.options.itemsCallback) {
        items = that.options.itemsCallback(items)
      }

      let index = 0

      if (typeof that.options.forceIndex !== 'undefined') {
        index = that.options.forceIndex
      } else {
        items.forEach((item, i) => {
          if (item.el === this) {
            index = i
          }
        })
      }

      that.photoSwipe = new window.Folio.PhotoSwipe({
        dataSource: items,
        index: index
      })

      if (that.options.changeCallback) {
        that.photoSwipe.on('change', () => that.options.changeCallback(that))
      }

      that.photoSwipeCaptionPlugin = new window.Folio.PhotoSwipeDynamicCaption({
        on: () => {},
        pswp: that.photoSwipe
      }, {
        captionContent: (slide) => {
          if (slide.data.caption || slide.data.author) {
            let content = '
'

            if (slide.data.caption) {
              content += `<div class="f-pswp__caption-caption">${slide.data.caption}</div>`
            }

            if (slide.data.author) {
              content += `<div class="f-pswp__caption-author">${window.Folio.Lightbox.authorLabel}: ${slide.data.author}</div>`
            }

            content += '
'

            return content
          }
        }
      })

      that.photoSwipeCaptionPlugin.initPlugin()

      that.photoSwipe.init()
    })
  }

  items () {
    const items = []

    $(this.selector).each((i, el) => {
      const item = this.item(i, el)

      if (item) items.push(item)
    })

    return items
  }

  item (index, el) {
    let $el = $(el)

    if ($el.hasClass('f-image--sensitive-content')) {
      if (!this.$html.hasClass('f-html--show-sensitive-content')) return
    }

    if (!$el.data('lightbox-src')) {
      $el = $(el).find('[data-lightbox-src]')
    }

    if (!$el.length) return null

    return {
      w: parseInt($el.data('lightbox-width')),
      h: parseInt($el.data('lightbox-height')),
      caption: $el.data('lightbox-caption') || $el.next('figcaption').text(),
      author: $el.data('lightbox-author'),
      src: window.Folio.Webp.supported ? $el.data('lightbox-webp-src') : $el.data('lightbox-src'),
      el: el
    }
  }

  unbind () {
    $(document).off(`click.${this.eventIdentifier}`, this.fullSelector)
  }

  destroy () {
    try {
      this.photoSwipe.close()
      this.photoSwipe.destroy()
    } catch (_error) {
    }

    this.photoSwipe = null
    this.unbind()
  }
}
<commit_msg>fix lightbox js lang detection<commit_after>//= require folio/webp
//= require photoswipe.esm.folio
//= require photoswipe-dynamic-caption-plugin.esm

window.Folio = window.Folio || {}
window.Folio.Lightbox = {}

window.Folio.Lightbox.calls = []

window.Folio.Lightbox.instances = []

window.Folio.Lightbox.authorLabel = document.documentElement.lang === 'cs' ? 'Foto' : 'Photo'

window.Folio.Lightbox.bind = (selector, options) => {
  options = options || {}

  window.Folio.Lightbox.calls.push([selector, options])

  const init = () => {
    const $items = $(selector)

    if ($items.length === 0) return

    if (options.individual) {
      $items.each(function () {
        let individualSelector = `.${this.className.replace(/\s+/g, '.')}`

        if (options.itemSelector) {
          individualSelector = `${individualSelector} ${options.itemSelector}`
        }

        window.Folio.Lightbox.instances.push(new window.Folio.Lightbox.Lightbox({ selector: individualSelector, options }))
      })
    } else if (options.fromData) {
      $items.each((i, el) => {
        window.Folio.Lightbox.instances.push(new window.Folio.Lightbox.Lightbox({ selector, options, data: $(el).data('lightbox-image-data') }))
      })
    } else {
      window.Folio.Lightbox.instances.push(new window.Folio.Lightbox.Lightbox({ selector, options }))
    }
  }

  if (typeof Turbolinks !== 'undefined' && Turbolinks !== null) {
    $(document)
      .on('turbolinks:load', init)
      .on('turbolinks:before-render', function () {
        if (window.Folio.Lightbox.instances.length === 0) return

        window.Folio.Lightbox.instances.forEach((instance) => {
          instance.destroy()
        })

        window.Folio.Lightbox.instances = []
      })
  } else {
    $(() => setTimeout(init, 0))
  }
}

window.Folio.Lightbox.updateAll = () => {
  window.Folio.Lightbox.instances.forEach((instance) => instance.destroy())

  window.Folio.Lightbox.instances = []

  window.Folio.Lightbox.calls.forEach((call) => window.Folio.Lightbox.bind(call[0], call[1]))
}

window.Folio.Lightbox.Lightbox = class FolioLightbox {
  constructor (attrs) {
    this.selector = attrs.selector
    this.options = attrs.options

    if (attrs.additionalSelector) {
      this.fullSelector = `${this.selector}, ${attrs.additionalSelector}`
    } else {
      this.fullSelector = this.selector
    }

    this.eventIdentifier = 'folioLightbox'
    this.$html = $(document.documentElement)

    this.bind(attrs.data)
  }

  bind (data) {
    this.unbind()
    const that = this

    $(document).on(`click.${this.eventIdentifier}`, this.fullSelector, function (e) {
      e.preventDefault()

      let items = data || that.items()

      if (that.options.itemsCallback) {
        items = that.options.itemsCallback(items)
      }

      let index = 0

      if (typeof that.options.forceIndex !== 'undefined') {
        index = that.options.forceIndex
      } else {
        items.forEach((item, i) => {
          if (item.el === this) {
            index = i
          }
        })
      }

      that.photoSwipe = new window.Folio.PhotoSwipe({
        dataSource: items,
        index: index
      })

      if (that.options.changeCallback) {
        that.photoSwipe.on('change', () => that.options.changeCallback(that))
      }

      that.photoSwipeCaptionPlugin = new window.Folio.PhotoSwipeDynamicCaption({
        on: () => {},
        pswp: that.photoSwipe
      }, {
        captionContent: (slide) => {
          if (slide.data.caption || slide.data.author) {
            let content = '
'

            if (slide.data.caption) {
              content += `<div class="f-pswp__caption-caption">${slide.data.caption}</div>`
            }

            if (slide.data.author) {
              content += `<div class="f-pswp__caption-author">${window.Folio.Lightbox.authorLabel}: ${slide.data.author}</div>`
            }

            content += '
'

            return content
          }
        }
      })

      that.photoSwipeCaptionPlugin.initPlugin()

      that.photoSwipe.init()
    })
  }

  items () {
    const items = []

    $(this.selector).each((i, el) => {
      const item = this.item(i, el)

      if (item) items.push(item)
    })

    return items
  }

  item (index, el) {
    let $el = $(el)

    if ($el.hasClass('f-image--sensitive-content')) {
      if (!this.$html.hasClass('f-html--show-sensitive-content')) return
    }

    if (!$el.data('lightbox-src')) {
      $el = $(el).find('[data-lightbox-src]')
    }

    if (!$el.length) return null

    return {
      w: parseInt($el.data('lightbox-width')),
      h: parseInt($el.data('lightbox-height')),
      caption: $el.data('lightbox-caption') || $el.next('figcaption').text(),
      author: $el.data('lightbox-author'),
      src: window.Folio.Webp.supported ? $el.data('lightbox-webp-src') : $el.data('lightbox-src'),
      el: el
    }
  }

  unbind () {
    $(document).off(`click.${this.eventIdentifier}`, this.fullSelector)
  }

  destroy () {
    try {
      this.photoSwipe.close()
      this.photoSwipe.destroy()
    } catch (_error) {
    }

    this.photoSwipe = null
    this.unbind()
  }
}
<|endoftext|>


<commit_before>import { createDuck } from 'redux-duck';
import { fromJS, Map, List, Set } from 'immutable';
import { waitForAuth } from './auth';
import gql from '../util/gql';
import NProgress from 'nprogress';

const { defineType, createAction, createReducer } = createDuck('articleDetail');

// Action Types
//

const LOAD = defineType('LOAD');
const LOAD_AUTH = defineType('LOAD_AUTH');
const SET_STATE = defineType('SET_STATE');
const RESET = defineType('RESET');
const LOAD_SEARCH_OF_ARTICLES = defineType('LOAD_SEARCH_OF_ARTICLES');
const LOAD_SEARCH_OF_REPLIES = defineType('LOAD_SEARCH_OF_REPLIES');

// Action creators
//
const fragments = {
  articleFields: `
    fragment articleFields on Article {
      id
      text
      replyRequestCount
      replyCount
      createdAt
      references {
        type
      }
    }
  `,
  articleReplyAndUserFields: `
    fragment userFields on User {
      id
      name
      avatarUrl
    }
    fragment articleReplyFields on ArticleReply {
      canUpdateStatus
      status
      articleId
      replyId
      reply {
        id
        versions(limit: 1) {
          user {
            ...userFields
          }
          type
          text
          reference
        }
      }
      feedbacks {
        user{
          id
        }
        score
      }
      user { ...userFields }
      createdAt
    }
  `,
};

const loadData = createAction(LOAD);
const setState = createAction(SET_STATE);

export const load = id => dispatch => {
  dispatch(setState({ key: 'isLoading', value: true }));
  return gql`
    query($id: String!) {
      GetArticle(id: $id) {
        ...articleFields
        user { ...userFields }
        replyConnections: articleReplies { ...articleReplyFields }
        relatedArticles(filter: {replyCount: {GT: 0}}) {
          edges {
            node {
              ...articleFields
              user { ...userFields }
              replyCount
              replyConnections: articleReplies { ...articleReplyFields }
            }
            score
          }
        }
      }
    }
    ${fragments.articleFields}
    ${fragments.articleReplyAndUserFields}
  `({ id }).then(resp => {
    dispatch(loadData(resp.getIn(['data', 'GetArticle'])));
    dispatch(setState({ key: 'isLoading', value: false }));
  });
};

export const loadAuth = id => dispatch => {
  dispatch(setState({ key: 'isAuthLoading', value: true }));
  return waitForAuth
    .then(() =>
      gql`
        query($id: String!) {
          GetArticle(id: $id) {
            replyConnections: articleReplies {
              articleId
              replyId
              canUpdateStatus
            }
          }
        }
      `({ id })
    )
    .then(resp => {
      dispatch(createAction(LOAD_AUTH)(resp.getIn(['data', 'GetArticle'])));
      dispatch(setState({ key: 'isAuthLoading', value: false }));
    });
};

export const reset = createAction(RESET);

const reloadReply = articleId => dispatch =>
  gql`
    query($id: String!) {
      GetArticle(id: $id) {
        replyConnections: articleReplies {
          ...articleReplyFields
        }
      }
    }
    ${fragments.articleReplyAndUserFields}
  `({ id: articleId }).then(resp => {
    dispatch(loadData(resp.getIn(['data', 'GetArticle'])));
    dispatch(setState({ key: 'isReplyLoading', value: false }));
  });

export const connectReply = (articleId, replyId) => dispatch => {
  dispatch(setState({ key: 'isReplyLoading', value: true }));
  NProgress.start();
  return gql`
    mutation($articleId: String!, $replyId: String!) {
      CreateArticleReply(articleId: $articleId, replyId: $replyId) {
        id
      }
    }
  `({ articleId, replyId }).then(() => {
    dispatch(reloadReply(articleId));
    NProgress.done();
  });
};

export const updateArticleReplyStatus = (
  articleId,
  replyId,
  status
) => dispatch => {
  dispatch(setState({ key: 'isReplyLoading', value: true }));
  NProgress.start();
  return gql`
    mutation(
      $articleId: String!
      $replyId: String!
      $status: ArticleReplyStatusEnum!
    ) {
      UpdateArticleReplyStatus(
        articleId: $articleId
        replyId: $replyId
        status: $status
      ) {
        status
      }
    }
  `({ articleId, replyId, status }).then(() => {
    dispatch(reloadReply(articleId));
    NProgress.done();
  });
};

export const submitReply = params => dispatch => {
  dispatch(setState({ key: 'isReplyLoading', value: true }));
  NProgress.start();
  return gql`
    mutation(
      $articleId: String!
      $text: String!
      $type: ReplyTypeEnum!
      $reference: String
    ) {
      CreateReply(
        articleId: $articleId
        text: $text
        type: $type
        reference: $reference
      ) {
        id
      }
    }
  `(params).then(() => {
    dispatch(reloadReply(params.articleId));
    NProgress.done();
  });
};

export const voteReply = (articleId, replyId, vote) => dispatch => {
  dispatch(setState({ key: 'isReplyLoading', value: true }));
  NProgress.start();
  return gql`
    mutation($articleId: String!, $replyId: String!, $vote: FeedbackVote!) {
      CreateOrUpdateArticleReplyFeedback(
        articleId: $articleId
        replyId: $replyId
        vote: $vote
      ) {
        feedbackCount
      }
    }
  `({ articleId, replyId, vote }).then(() => {
    dispatch(reloadReply(articleId));
    NProgress.done();
  });
};

export const searchReplies = ({ q }) => dispatch => {
  return gql`
    query(
      $filter: ListReplyFilter
      $orderBy: [ListReplyOrderBy]
      $before: String
    ) {
      ListReplies(
        filter: $filter
        orderBy: $orderBy
        before: $before
        first: 25
      ) {
        edges {
          cursor
          node {
            id
            versions {
              text
              type
              createdAt
            }
            replyConnections: articleReplies {
              article {
                id
                text
              }
            }
          }
        }
      }
    }
  `({
    filter: {
      moreLikeThis: {
        like: q,
        minimumShouldMatch: '0',
      },
    },
    orderBy: {
      _score: 'DESC',
    },
    first: 25,
  }).then(resp => {
    dispatch(
      createAction(LOAD_SEARCH_OF_REPLIES)(
        resp.getIn(['data', 'ListReplies', 'edges'], List())
      )
    );
  });
};

export const searchRepiedArticle = ({ q }) => dispatch => {
  return gql`
    query(
      $filter: ListArticleFilter
      $orderBy: [ListArticleOrderBy]
      $before: String
    ) {
      ListArticles(
        filter: $filter
        orderBy: $orderBy
        before: $before
        first: 25
      ) {
        edges {
          node {
            id
            text
            replyCount
            createdAt
            replyConnections: articleReplies {
              reply {
                id
                versions {
                  text
                  createdAt
                  type
                }
              }
            }
          }
        }
      }
    }
  `({
    filter: {
      moreLikeThis: {
        like: q,
        minimumShouldMatch: '0',
      },
      replyCount: { GT: '1' },
    },
    orderBy: {
      _score: 'DESC',
    },
    first: 25,
  }).then(resp => {
    dispatch(
      createAction(LOAD_SEARCH_OF_ARTICLES)(
        resp.getIn(['data', 'ListArticles', 'edges'], List())
      )
    );
  });
};

// Reducer
//

export const initialState = fromJS({
  state: { isLoading: false, isAuthLoading: false, isReplyLoading: false },
  data: {
    // data from server
    article: null,
    replyConnections: [], // reply & its connection to this article
    relatedArticles: [],
    relatedReplies: [], // related articles' replies
    searchArticles: [],
    searchReplies: [],
  },
});

export default createReducer(
  {
    [SET_STATE]: (state, { payload: { key, value } }) =>
      state.setIn(['state', key], value),

    [LOAD]: (state, { payload }) => {
      const relatedArticleEdges =
        payload.getIn(['relatedArticles', 'edges']) || List();

      const replyIds = Set(
        (payload.get('replyConnections') || List()).map(conn =>
          conn.getIn(['reply', 'id'])
        )
      );

      return state.withMutations(s =>
        s
          .updateIn(['data', 'article'], article =>
            (article || Map()).merge(
              payload.remove('replyConnections').remove('relatedArticles')
            )
          )
          .setIn(
            ['data', 'replyConnections'],
            payload
              .get('replyConnections')
              .sort(
                (a, b) =>
                  new Date(b.get('createdAt')) - new Date(a.get('createdAt'))
              )
          )
          .updateIn(
            ['data', 'relatedArticles'],
            articles =>
              !relatedArticleEdges.size
                ? articles
                : relatedArticleEdges.map(edge =>
                    edge.get('node').remove('replyConnections')
                  )
          )
          .updateIn(
            ['data', 'relatedReplies'],
            replies =>
              !relatedArticleEdges.size
                ? replies
                : relatedArticleEdges
                    .flatMap(edge =>
                      edge
                        .getIn(['node', 'replyConnections'])
                        .map(conn =>
                          conn
                            .get('reply')
                            .set(
                              'article',
                              edge.get('node').remove('replyConnections')
                            )
                        )
                        .filter(reply => {
                          // Filter-out replies that is already re-used.
                          return reply && !replyIds.contains(reply.get('id'));
                        })
                    )
                    // De-duping replies using replyId, taking the reply with more relavant article
                    // (which should come first)
                    //
                    .groupBy(reply => reply.get('id'))
                    .map(replyGroup => replyGroup.first())
                    .toList()
          )
      );
    },

    [LOAD_AUTH]: (state, { payload }) => {
      const idAuthMap = payload.get('replyConnections').reduce((agg, conn) => {
        agg[conn.get('replyId')] = conn;
        return agg;
      }, {});
      return state.updateIn(['data', 'replyConnections'], replyConnections =>
        replyConnections.map(conn => conn.merge(idAuthMap[conn.get('replyId')]))
      );
    },

    [LOAD_SEARCH_OF_REPLIES]: (state, { payload }) => {
      const reconstructSearchRepliesList = payload.map(reply => {
        return Map({
          id: reply.getIn(['node', 'id']),
          article: reply.getIn(['node', 'replyConnections', 0, 'article']),
          versions: reply.getIn(['node', 'versions']),
        });
      });
      return state.setIn(
        ['data', 'searchReplies'],
        reconstructSearchRepliesList
      );
    },

    [LOAD_SEARCH_OF_ARTICLES]: (state, { payload }) => {
      const reconstructSearchArticlesList = payload.map(article => {
        return article.getIn(['node']);
      });

      return state.setIn(
        ['data', 'searchArticles'],
        reconstructSearchArticlesList
      );
    },

    [RESET]: state => state.set('data', initialState.get('data')),
  },
  initialState
);
<commit_msg>CreateArticleReply should not query obsolete id field<commit_after>import { createDuck } from 'redux-duck';
import { fromJS, Map, List, Set } from 'immutable';
import { waitForAuth } from './auth';
import gql from '../util/gql';
import NProgress from 'nprogress';

const { defineType, createAction, createReducer } = createDuck('articleDetail');

// Action Types
//

const LOAD = defineType('LOAD');
const LOAD_AUTH = defineType('LOAD_AUTH');
const SET_STATE = defineType('SET_STATE');
const RESET = defineType('RESET');
const LOAD_SEARCH_OF_ARTICLES = defineType('LOAD_SEARCH_OF_ARTICLES');
const LOAD_SEARCH_OF_REPLIES = defineType('LOAD_SEARCH_OF_REPLIES');

// Action creators
//
const fragments = {
  articleFields: `
    fragment articleFields on Article {
      id
      text
      replyRequestCount
      replyCount
      createdAt
      references {
        type
      }
    }
  `,
  articleReplyAndUserFields: `
    fragment userFields on User {
      id
      name
      avatarUrl
    }
    fragment articleReplyFields on ArticleReply {
      canUpdateStatus
      status
      articleId
      replyId
      reply {
        id
        versions(limit: 1) {
          user {
            ...userFields
          }
          type
          text
          reference
        }
      }
      feedbacks {
        user{
          id
        }
        score
      }
      user { ...userFields }
      createdAt
    }
  `,
};

const loadData = createAction(LOAD);
const setState = createAction(SET_STATE);

export const load = id => dispatch => {
  dispatch(setState({ key: 'isLoading', value: true }));
  return gql`
    query($id: String!) {
      GetArticle(id: $id) {
        ...articleFields
        user { ...userFields }
        replyConnections: articleReplies { ...articleReplyFields }
        relatedArticles(filter: {replyCount: {GT: 0}}) {
          edges {
            node {
              ...articleFields
              user { ...userFields }
              replyCount
              replyConnections: articleReplies { ...articleReplyFields }
            }
            score
          }
        }
      }
    }
    ${fragments.articleFields}
    ${fragments.articleReplyAndUserFields}
  `({ id }).then(resp => {
    dispatch(loadData(resp.getIn(['data', 'GetArticle'])));
    dispatch(setState({ key: 'isLoading', value: false }));
  });
};

export const loadAuth = id => dispatch => {
  dispatch(setState({ key: 'isAuthLoading', value: true }));
  return waitForAuth
    .then(() =>
      gql`
        query($id: String!) {
          GetArticle(id: $id) {
            replyConnections: articleReplies {
              articleId
              replyId
              canUpdateStatus
            }
          }
        }
      `({ id })
    )
    .then(resp => {
      dispatch(createAction(LOAD_AUTH)(resp.getIn(['data', 'GetArticle'])));
      dispatch(setState({ key: 'isAuthLoading', value: false }));
    });
};

export const reset = createAction(RESET);

const reloadReply = articleId => dispatch =>
  gql`
    query($id: String!) {
      GetArticle(id: $id) {
        replyConnections: articleReplies {
          ...articleReplyFields
        }
      }
    }
    ${fragments.articleReplyAndUserFields}
  `({ id: articleId }).then(resp => {
    dispatch(loadData(resp.getIn(['data', 'GetArticle'])));
    dispatch(setState({ key: 'isReplyLoading', value: false }));
  });

export const connectReply = (articleId, replyId) => dispatch => {
  dispatch(setState({ key: 'isReplyLoading', value: true }));
  NProgress.start();
  return gql`
    mutation($articleId: String!, $replyId: String!) {
      CreateArticleReply(articleId: $articleId, replyId: $replyId) {
        articleId
      }
    }
  `({ articleId, replyId }).then(() => {
    dispatch(reloadReply(articleId));
    NProgress.done();
  });
};

export const updateArticleReplyStatus = (
  articleId,
  replyId,
  status
) => dispatch => {
  dispatch(setState({ key: 'isReplyLoading', value: true }));
  NProgress.start();
  return gql`
    mutation(
      $articleId: String!
      $replyId: String!
      $status: ArticleReplyStatusEnum!
    ) {
      UpdateArticleReplyStatus(
        articleId: $articleId
        replyId: $replyId
        status: $status
      ) {
        status
      }
    }
  `({ articleId, replyId, status }).then(() => {
    dispatch(reloadReply(articleId));
    NProgress.done();
  });
};

export const submitReply = params => dispatch => {
  dispatch(setState({ key: 'isReplyLoading', value: true }));
  NProgress.start();
  return gql`
    mutation(
      $articleId: String!
      $text: String!
      $type: ReplyTypeEnum!
      $reference: String
    ) {
      CreateReply(
        articleId: $articleId
        text: $text
        type: $type
        reference: $reference
      ) {
        id
      }
    }
  `(params).then(() => {
    dispatch(reloadReply(params.articleId));
    NProgress.done();
  });
};

export const voteReply = (articleId, replyId, vote) => dispatch => {
  dispatch(setState({ key: 'isReplyLoading', value: true }));
  NProgress.start();
  return gql`
    mutation($articleId: String!, $replyId: String!, $vote: FeedbackVote!) {
      CreateOrUpdateArticleReplyFeedback(
        articleId: $articleId
        replyId: $replyId
        vote: $vote
      ) {
        feedbackCount
      }
    }
  `({ articleId, replyId, vote }).then(() => {
    dispatch(reloadReply(articleId));
    NProgress.done();
  });
};

export const searchReplies = ({ q }) => dispatch => {
  return gql`
    query(
      $filter: ListReplyFilter
      $orderBy: [ListReplyOrderBy]
      $before: String
    ) {
      ListReplies(
        filter: $filter
        orderBy: $orderBy
        before: $before
        first: 25
      ) {
        edges {
          cursor
          node {
            id
            versions {
              text
              type
              createdAt
            }
            replyConnections: articleReplies {
              article {
                id
                text
              }
            }
          }
        }
      }
    }
  `({
    filter: {
      moreLikeThis: {
        like: q,
        minimumShouldMatch: '0',
      },
    },
    orderBy: {
      _score: 'DESC',
    },
    first: 25,
  }).then(resp => {
    dispatch(
      createAction(LOAD_SEARCH_OF_REPLIES)(
        resp.getIn(['data', 'ListReplies', 'edges'], List())
      )
    );
  });
};

export const searchRepiedArticle = ({ q }) => dispatch => {
  return gql`
    query(
      $filter: ListArticleFilter
      $orderBy: [ListArticleOrderBy]
      $before: String
    ) {
      ListArticles(
        filter: $filter
        orderBy: $orderBy
        before: $before
        first: 25
      ) {
        edges {
          node {
            id
            text
            replyCount
            createdAt
            replyConnections: articleReplies {
              reply {
                id
                versions {
                  text
                  createdAt
                  type
                }
              }
            }
          }
        }
      }
    }
  `({
    filter: {
      moreLikeThis: {
        like: q,
        minimumShouldMatch: '0',
      },
      replyCount: { GT: '1' },
    },
    orderBy: {
      _score: 'DESC',
    },
    first: 25,
  }).then(resp => {
    dispatch(
      createAction(LOAD_SEARCH_OF_ARTICLES)(
        resp.getIn(['data', 'ListArticles', 'edges'], List())
      )
    );
  });
};

// Reducer
//

export const initialState = fromJS({
  state: { isLoading: false, isAuthLoading: false, isReplyLoading: false },
  data: {
    // data from server
    article: null,
    replyConnections: [], // reply & its connection to this article
    relatedArticles: [],
    relatedReplies: [], // related articles' replies
    searchArticles: [],
    searchReplies: [],
  },
});

export default createReducer(
  {
    [SET_STATE]: (state, { payload: { key, value } }) =>
      state.setIn(['state', key], value),

    [LOAD]: (state, { payload }) => {
      const relatedArticleEdges =
        payload.getIn(['relatedArticles', 'edges']) || List();

      const replyIds = Set(
        (payload.get('replyConnections') || List()).map(conn =>
          conn.getIn(['reply', 'id'])
        )
      );

      return state.withMutations(s =>
        s
          .updateIn(['data', 'article'], article =>
            (article || Map()).merge(
              payload.remove('replyConnections').remove('relatedArticles')
            )
          )
          .setIn(
            ['data', 'replyConnections'],
            payload
              .get('replyConnections')
              .sort(
                (a, b) =>
                  new Date(b.get('createdAt')) - new Date(a.get('createdAt'))
              )
          )
          .updateIn(
            ['data', 'relatedArticles'],
            articles =>
              !relatedArticleEdges.size
                ? articles
                : relatedArticleEdges.map(edge =>
                    edge.get('node').remove('replyConnections')
                  )
          )
          .updateIn(
            ['data', 'relatedReplies'],
            replies =>
              !relatedArticleEdges.size
                ? replies
                : relatedArticleEdges
                    .flatMap(edge =>
                      edge
                        .getIn(['node', 'replyConnections'])
                        .map(conn =>
                          conn
                            .get('reply')
                            .set(
                              'article',
                              edge.get('node').remove('replyConnections')
                            )
                        )
                        .filter(reply => {
                          // Filter-out replies that is already re-used.
                          return reply && !replyIds.contains(reply.get('id'));
                        })
                    )
                    // De-duping replies using replyId, taking the reply with more relavant article
                    // (which should come first)
                    //
                    .groupBy(reply => reply.get('id'))
                    .map(replyGroup => replyGroup.first())
                    .toList()
          )
      );
    },

    [LOAD_AUTH]: (state, { payload }) => {
      const idAuthMap = payload.get('replyConnections').reduce((agg, conn) => {
        agg[conn.get('replyId')] = conn;
        return agg;
      }, {});
      return state.updateIn(['data', 'replyConnections'], replyConnections =>
        replyConnections.map(conn => conn.merge(idAuthMap[conn.get('replyId')]))
      );
    },

    [LOAD_SEARCH_OF_REPLIES]: (state, { payload }) => {
      const reconstructSearchRepliesList = payload.map(reply => {
        return Map({
          id: reply.getIn(['node', 'id']),
          article: reply.getIn(['node', 'replyConnections', 0, 'article']),
          versions: reply.getIn(['node', 'versions']),
        });
      });
      return state.setIn(
        ['data', 'searchReplies'],
        reconstructSearchRepliesList
      );
    },

    [LOAD_SEARCH_OF_ARTICLES]: (state, { payload }) => {
      const reconstructSearchArticlesList = payload.map(article => {
        return article.getIn(['node']);
      });

      return state.setIn(
        ['data', 'searchArticles'],
        reconstructSearchArticlesList
      );
    },

    [RESET]: state => state.set('data', initialState.get('data')),
  },
  initialState
);
<|endoftext|>


<commit_before>/**
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 *
 * @flow
 */

export {
  __SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED,
  act as unstable_act,
  Children,
  Component,
  Fragment,
  Profiler,
  PureComponent,
  StrictMode,
  Suspense,
  cloneElement,
  createContext,
  createElement,
  createFactory,
  createMutableSource as unstable_createMutableSource,
  createRef,
  forwardRef,
  isValidElement,
  lazy,
  memo,
  startTransition,
  useId,
  useCallback,
  useContext,
  useDebugValue,
  useDeferredValue,
  useEffect,
  useImperativeHandle,
  useInsertionEffect,
  useLayoutEffect,
  useMemo,
  useSyncExternalStore,
  useReducer,
  useRef,
  useState,
  useTransition,
  version,
} from './src/React';
<commit_msg>Remove createMutableSource from stable exports (#23352)<commit_after>/**
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 *
 * @flow
 */

export {
  __SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED,
  act as unstable_act,
  Children,
  Component,
  Fragment,
  Profiler,
  PureComponent,
  StrictMode,
  Suspense,
  cloneElement,
  createContext,
  createElement,
  createFactory,
  createRef,
  forwardRef,
  isValidElement,
  lazy,
  memo,
  startTransition,
  useId,
  useCallback,
  useContext,
  useDebugValue,
  useDeferredValue,
  useEffect,
  useImperativeHandle,
  useInsertionEffect,
  useLayoutEffect,
  useMemo,
  useSyncExternalStore,
  useReducer,
  useRef,
  useState,
  useTransition,
  version,
} from './src/React';
<|endoftext|>