diff --git a/www/bars.mustache.html b/www/bars.mustache.html index ded4fd8b..522be1e4 100755 --- a/www/bars.mustache.html +++ b/www/bars.mustache.html @@ -2,16 +2,21 @@
-

InPhO Topic Explorer

+

InPhO Topic Explorer

{{#corpus_link}}{{corpus_name}}{{/corpus_link}} {{^corpus_link}}{{corpus_name}}{{/corpus_link}}
About this corpus
Click ? button at bottom left for help with the topic explorer interface.

+
+

+
+
-
+ +
@@ -20,7 +25,7 @@

-
+
@@ -32,8 +37,8 @@

-
- +
+
@@ -122,4 +127,7 @@

Fingerprint icon Topic Fingerp

+ + + diff --git a/www/bootstrap-tour.js b/www/bootstrap-tour.js new file mode 100644 index 00000000..10d43fdf --- /dev/null +++ b/www/bootstrap-tour.js @@ -0,0 +1,1737 @@ +/* ======================================================================== + * bootstrap-tour - v0.11.0 + * http://bootstraptour.com + * ======================================================================== + * Copyright 2012-2015 Ulrich Sossou + * + * ======================================================================== + * Licensed under the MIT License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/MIT + * + * 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. + * ======================================================================== + * + * Updated for CS by FFS 2018 - v0.5 + * Features added: + * + + 1. onNext/onPrevious - prevent auto-move to next step, allow .goTo + 2. *** Do not call Tour.init *** - fixed tours with hidden elements on page reload + 3. Dynamically determine step element by function + 4. Only continue tour when reflex element is clicked using reflexOnly + 5. Call onElementUnavailable if step element is missing + 6. Scroll flicker/continual step reload fixed + 7. Magic progress bar and progress text, plus options to customize per step + 8. Prevent user interaction with element using preventInteraction + 9. Wait for arbitrary DOM element to be visible before showing tour step/crapping out due to missing element, using delayOnElement + 10. Handle bootstrap modal dialogs better - autodetect modals or children of modals, and call onModalHidden when user dismisses modal without following tour steps + 11. Automagically fixes drawing issues with Bootstrap Selectpicker (https://github.com/snapappointments/bootstrap-select/) + + -------------- + 1. Control flow from onNext() / onPrevious() options: + Returning false from onNext/onPrevious handler will prevent Tour from automatically moving to the next/previous step. + Tour flow methods (Tour.goTo etc) now also work correctly in onNext/onPrevious. + Option is available per step or globally: + + var tourSteps = [ + { + element: "#inputBanana", + title: "Bananas!", + content: "Bananas are yellow, except when they're not", + onNext: function(tour){ + if($('#inputBanana').val() !== "banana") + { + // no banana? highlight the banana field + $('#inputBanana').css("background-color", "red"); + // do not jump to the next tour step! + return false; + } + } + } + ]; + + var Tour=new Tour({ + steps: tourSteps, + onNext: function(tour) + { + if(someVar = true) + { + // force the tour to jump to slide 3 + tour.goTo(3); + // Prevent default move to next step - important! + return false; + } + } + }); + + -------------- + 2. Do not call Tour.init + When setting up Tour, do not call Tour.init(). + Call Tour.start() to start/resume the Tour from previous step + Call Tour.restart() to always start Tour from first step + + Tour.init() was a redundant method that caused conflict with hidden Tour elements. + +--------------- + 3. Dynamically determine element by function + Step "element:" option allows element to be determined programmatically. Return a jquery object. + The following is possible: + + var tourSteps = [ + { + element: function() { return $(document).find("...something..."); }, + title: "Dynamic", + content: "Element found by function" + }, + { + element: "#static", + title: "Static", + content: "Element found by static ID" + } + ]; + +--------------- + 4. Only continue tour when reflex element is clicked + Use step option reflexOnly in conjunction with step option reflex to automagically hide the "next" button in the tour, and only continue when the user clicks the element: + var tourSteps = [ + { + element: "#myButton", + reflex: true, + reflexOnly: true, + title: "Click it", + content: "Click to continue, or you're stuck" + } + ]; + +---------------- + 5. Call function when element is missing + If the element specified in the step (static or dynamically determined as per feature #3), onElementUnavailable is called. + Function signature: function(tour, stepNumber) {} + Option is available at global and per step levels. + + function tourBroken(tour, stepNumber) + { + alert("Uhoh, tour element is done broke on step number " + stepNumber); + } + + var tourSteps = [ + { + element: "#btnMagic", + onElementUnavailable: tourBroken, + title: "Hold my beer", + content: "now watch this" + } + ]; + +--------------- + 6. Scroll flicker / continue reload fixed + Original Tour constantly reloaded the current tour step on scroll & similar events. This produced flickering, constant reloads and therefore constant calls to all the step function calls. + This is now fixed. Scrolling the browser window does not cause the tour step to reload. + + IMPORTANT: orphan steps are stuck to the center of the screen. However steps linked to elements ALWAYS stay stuck to their element, even if user scrolls the element & tour popover + off the screen. This is my personal preference, as original functionality of tour step moving with the scroll even when the element was off the viewport seemed strange. + +--------------- + 7. Progress bar & progress text: + Use the following options globally or per step to show tour progress: + showProgressBar - shows a bootstrap progress bar for tour progress at the top of the tour content + showProgressText - shows a textual progress (N/X, i.e.: 1/24 for slide 1 of 24) in the tour title + + var tourSteps = [ + { + element: "#inputBanana", + title: "Bananas!", + content: "Bananas are yellow, except when they're not", + }, + { + element: "#inputOranges", + title: "Oranges!", + content: "Oranges are not bananas", + showProgressBar: false, // don't show the progress bar on this step only + showProgressText: false, // don't show the progress text on this step only + } + ]; + var Tour=new Tour({ + steps: tourSteps, + showProgressBar: true, // default show progress bar + showProgressText: true, // default show progress text + }); + + 7b. Customize the progressbar/progress text: + In conjunction with 7a, provide the following functions globally or per step to draw your own progressbar/progress text: + + getProgressBarHTML(percent) + getProgressTextHTML(stepNumber, percent, stepCount) + + These will be called when each step is shown, with the appropriate percentage/step number etc passed to your function. Return an HTML string of a "drawn" progress bar/progress text + which will be directly inserted into the tour step. + + Example: + var tourSteps = [ + { + element: "#inputBanana", + title: "Bananas!", + content: "Bananas are yellow, except when they're not", + }, + { + element: "#inputOranges", + title: "Oranges!", + content: "Oranges are not bananas", + getProgressBarHTML: function(percent) + { + // override the global progress bar function for this step + return '
You're ' + percent + ' of the way through!
'; + } + } + ]; + var Tour=new Tour({ + steps: tourSteps, + showProgressBar: true, // default show progress bar + showProgressText: true, // default show progress text + getProgressBarHTML: function(percent) + { + // default progress bar for all steps. Return valid HTML to draw the progress bar you want + return '
'; + }, + getProgressTextHTML: function(stepNumber, percent, stepCount) + { + // default progress text for all steps + return 'Slide ' + stepNumber + "/" + stepCount; + }, + + }); + +---------------- + 8. Prevent interaction with element + Sometimes you want to highlight a DOM element (button, input field) for a tour step, but don't want the user to be able to interact with it. + Use preventInteraction to stop the user touching the element: + + var tourSteps = [ + { + element: "#btnMCHammer", + preventInteraction: true, + title: "Hammer Time", + content: "You can't touch this" + } + ]; + +---------------- + 9. Wait for an element to appear before continuing tour + Sometimes a tour step element might not be immediately ready because of transition effects etc. This is a specific issue with bootstrap select, which is relatively slow to show the selectpicker + dropdown after clicking. + Use delayOnElement to instruct Tour to wait for **ANY** element to appear before showing the step (or crapping out due to missing element). Yes this means the tour step element can be one DOM + element, but the delay will wait for a completely separate DOM element to appear. This is really useful for hidden divs etc. + Use in conjunction with onElementUnavailable for robust tour step handling. + + delayOnElement is an object with the following: + delayOnElement: { + delayElement: "#waitForMe", // the element to wait to become visible, or the string literal "element" to use the step element + maxDelay: 2000, // optional milliseconds to wait/timeout for the element, before crapping out. If maxDelay is not specified, this is 2000ms by default + } + + var tourSteps = [ + { + element: "#btnPrettyTransition", + delayOnElement: { + delayElement: "element" // use string literal "element" to wait for this step's element, i.e.: #btnPrettyTransition + }, + title: "Ages", + content: "This button takes ages to appear" + }, + { + element: "#inputUnrelated", + delayOnElement: { + delayElement: "#divStuff" // wait until DOM element "divStuff" is visible before showing this tour step against DOM element "inputUnrelated" + }, + title: "Waiting", + content: "This input is nice, but you only see this step when the other div appears" + }, + { + element: "#btnDontForgetThis", + delayOnElement: { + delayElement: "element", // use string literal "element" to wait for this step's element, i.e.: #btnDontForgetThis + maxDelay: 5000 // wait 5 seconds for it to appear before timing out + }, + title: "Cool", + content: "Remember the onElementUnavailable option!", + onElementUnavailable: function(tour, stepNumber) + { + // This will be called if btnDontForgetThis is not visible after 5 seconds + console.log("Well that went badly wrong"); + } + }, + ]; + +---------------- + 10. Trigger when modal closes + If tour element is a modal, or is a DOM element inside a modal, the element can disappear "at random" if the user dismisses the dialog. + In this case, onModalHidden global and per step function is called. Only functional when step is not an orphan. + This is useful if a tour includes a step that launches a modal, and the tour requires the user to take some actions inside the modal before OK'ing it and moving to the next + tour step. + + Return (int) step number to immediately move to that step + Return exactly false to not change tour state in any way - this is useful if you need to reshow the modal because some validation failed + Return anything else to move to the next step + + element === Bootstrap modal, or element parent === bootstrap modal is automatically detected. + + var Tour=new Tour({ + steps: tourSteps, + onModalHidden: function(tour, stepNumber) + { + console.log("Well damn, this step's element was a modal, or inside a modal, and the modal just done got dismissed y'all. Moving to step 3."); + + // move to step number 3 + return 3; + }, + }); + + + var Tour=new Tour({ + steps: tourSteps, + onModalHidden: function(tour, stepNumber) + { + if(validateSomeModalContent() == false) + { + // The validation failed, user dismissed modal without properly taking actions. + // Show the modal again + showModalAgain(); + + // Instruct tour to stay on same step + return false; + } + else + { + // Content was valid. Return null or do nothing to instruct tour to continue to next step + } + }, + }); + +---------------- + 11. Fix conflict with Bootstrap Selectpicker: https://github.com/snapappointments/bootstrap-select/ + Selectpicker draws a custom select. Tour now automagically finds and adjusts the selectpicker dropdown so that it appears correctly within the tour + + * + */ + +var bind = function (fn, me) { + return function () { + return fn.apply(me, arguments); + }; +}; + +(function (window, factory) { + if (typeof define === 'function' && define.amd) { + return define(['jquery'], function (jQuery) { + return window.Tour = factory(jQuery); + }); + } else if (typeof exports === 'object') { + return module.exports = factory(require('jquery')); + } else { + return window.Tour = factory(window.jQuery); + } +})(window, function ($) { + var Tour, + document; + document = window.document; + Tour = (function () { + function Tour(options) { + this._showPopoverAndOverlay = bind(this._showPopoverAndOverlay, this); + var storage; + try { + storage = window.localStorage; + } catch (error) { + storage = false; + } + this._options = $.extend({ + name: 'tour', + steps: [], + container: 'body', + autoscroll: true, + keyboard: true, + storage: storage, + debug: false, + backdrop: false, + backdropContainer: 'body', + backdropPadding: 0, + redirect: true, + orphan: false, + duration: false, + delay: false, + basePath: '', + template: '', + showProgressBar: true, + showProgressText: true, + getProgressBarHTML: null,//function(percent) {}, + getProgressTextHTML: null,//function(stepNumber, percent, stepCount) {}, + afterSetState: function (key, value) {}, + afterGetState: function (key, value) {}, + afterRemoveState: function (key) {}, + onStart: function (tour) {}, + onEnd: function (tour) {}, + onShow: function (tour) {}, + onShown: function (tour) {}, + onHide: function (tour) {}, + onHidden: function (tour) {}, + onNext: function (tour) {}, + onPrev: function (tour) {}, + onPause: function (tour, duration) {}, + onResume: function (tour, duration) {}, + onRedirectError: function (tour) {}, + onElementUnavailable: null, // function (tour, stepNumber) {}, + onModalHidden: null, // function(tour, stepNumber) {} + }, options); + this._force = false; + this._inited = false; + this._current = null; + this.backdrops = []; + this; + } + + Tour.prototype.addSteps = function (steps) { + var j, + len, + step; + for (j = 0, len = steps.length; j < len; j++) { + step = steps[j]; + this.addStep(step); + } + return this; + }; + + Tour.prototype.addStep = function (step) { + this._options.steps.push(step); + return this; + }; + + Tour.prototype.getStepCount = function() { + return this._options.steps.length; + }; + + Tour.prototype.getStep = function (i) { + if (this._options.steps[i] != null) { + + if(typeof(this._options.steps[i].element) == "function") + { + this._options.steps[i].element = this._options.steps[i].element(); + } + + return $.extend({ + id: "step-" + i, + path: '', + host: '', + placement: 'right', + title: '', + content: '

', + next: i === this._options.steps.length - 1 ? -1 : i + 1, + prev: i - 1, + animation: true, + container: this._options.container, + autoscroll: this._options.autoscroll, + backdrop: this._options.backdrop, + backdropContainer: this._options.backdropContainer, + backdropPadding: this._options.backdropPadding, + redirect: this._options.redirect, + reflexElement: this._options.steps[i].element, + preventInteraction: false, + backdropElement: this._options.steps[i].element, + orphan: this._options.orphan, + duration: this._options.duration, + delay: this._options.delay, + template: this._options.template, + showProgressBar: this._options.showProgressBar, + showProgressText: this._options.showProgressText, + getProgressBarHTML: this._options.getProgressBarHTML, + getProgressTextHTML: this._options.getProgressTextHTML, + onShow: this._options.onShow, + onShown: this._options.onShown, + onHide: this._options.onHide, + onHidden: this._options.onHidden, + onNext: this._options.onNext, + onPrev: this._options.onPrev, + onPause: this._options.onPause, + onResume: this._options.onResume, + onRedirectError: this._options.onRedirectError, + onElementUnavailable: this._options.onElementUnavailable, + onModalHidden: this._options.onModalHidden + }, this._options.steps[i]); + } + }; + + Tour.prototype.init = function (force) { + this._force = force; + if (this.ended()) { + this._debug('Tour ended, init prevented.'); + return this; + } + this.setCurrentStep(); + this._initMouseNavigation(); + this._initKeyboardNavigation(); + this._onResize((function (_this) { + return function () { + return _this.showStep(_this._current); + }; + })(this)); + this._onScroll((function (_this) { + return function () { + return _this._showPopoverAndOverlay(_this._current); + }; + })(this)); +/* + // Removed - .init is an unnecessary call, .start force calls .init if tour is not initialized. This code creates conflict + // where page has hidden elements, page is reloaded, tour is then forced to start on step N when hidden element is not shown yet + if (this._current !== null) { + this.showStep(this._current); + } + */ this._inited = true; + return this; + }; + + Tour.prototype.start = function (force) { + var promise; + if (force == null) { + force = false; + } + if (!this._inited) { + this.init(force); + } + + // removed if condition - tour should always start when .start is called. Original flow prevented tour from start if _current step index was set (tour already started) + //if (this._current === null) { + promise = this._makePromise(this._options.onStart != null ? this._options.onStart(this) : void 0); + this._callOnPromiseDone(promise, this.showStep, this._current); + // } + + return this; + }; + + Tour.prototype.next = function () { + var promise; + promise = this.hideStep(this._current, this._current + 1); + return this._callOnPromiseDone(promise, this._showNextStep); + }; + + Tour.prototype.prev = function () { + var promise; + promise = this.hideStep(this._current, this._current - 1); + return this._callOnPromiseDone(promise, this._showPrevStep); + }; + + Tour.prototype.goTo = function (i) { + var promise; + promise = this.hideStep(this._current, i); + return this._callOnPromiseDone(promise, this.showStep, i); + }; + + Tour.prototype.end = function () { + var endHelper, + promise; + endHelper = (function (_this) { + return function (e) { + $(document).off("click.tour-" + _this._options.name); + $(document).off("keyup.tour-" + _this._options.name); + $(window).off("resize.tour-" + _this._options.name); + $(window).off("scroll.tour-" + _this._options.name); + _this._setState('end', 'yes'); + _this._inited = false; + _this._force = false; + _this._clearTimer(); + if (_this._options.onEnd != null) { + return _this._options.onEnd(_this); + } + }; + })(this); + promise = this.hideStep(this._current); + return this._callOnPromiseDone(promise, endHelper); + }; + + Tour.prototype.ended = function () { + return !this._force && !!this._getState('end'); + }; + + Tour.prototype.restart = function () { + this._removeState('current_step'); + this._removeState('end'); + this._removeState('redirect_to'); + return this.start(); + }; + + Tour.prototype.pause = function () { + var step; + step = this.getStep(this._current); + if (!(step && step.duration)) { + return this; + } + this._paused = true; + this._duration -= new Date().getTime() - this._start; + window.clearTimeout(this._timer); + this._debug("Paused/Stopped step " + (this._current + 1) + " timer (" + this._duration + " remaining)."); + if (step.onPause != null) { + return step.onPause(this, this._duration); + } + }; + + Tour.prototype.resume = function () { + var step; + step = this.getStep(this._current); + if (!(step && step.duration)) { + return this; + } + this._paused = false; + this._start = new Date().getTime(); + this._duration = this._duration || step.duration; + this._timer = window.setTimeout((function (_this) { + return function () { + if (_this._isLast()) { + return _this.next(); + } else { + return _this.end(); + } + }; + })(this), this._duration); + this._debug("Started step " + (this._current + 1) + " timer with duration " + this._duration); + if ((step.onResume != null) && this._duration !== step.duration) { + return step.onResume(this, this._duration); + } + }; + + Tour.prototype.reshowCurrentStep = function() + { + var promise; + promise = this.hideStep(this._current, this._current); + return this._callOnPromiseDone(promise, this.showStep, this._current); + }; + + Tour.prototype.hideStep = function (i, iNext) { + var hideDelay, + hideStepHelper, + promise, + step; + step = this.getStep(i); + if (!step) { + return; + } + this._clearTimer(); + promise = this._makePromise(step.onHide != null ? step.onHide(this, i) : void 0); + + hideStepHelper = (function (_this) + { + return function (e) + { + var $element, + next_step; + $element = $(step.element); + if (!($element.data('bs.popover') || $element.data('popover'))) + { + $element = $('body'); + } + + $element.popover('destroy').removeClass("tour-" + _this._options.name + "-element tour-" + _this._options.name + "-" + i + "-element").removeData('bs.popover'); + + if (step.reflex) + { + $(step.reflexElement).removeClass('tour-step-element-reflex').off((_this._reflexEvent(step.reflex)) + ".tour-" + _this._options.name); + } + + if (step.backdrop) + { + next_step = (iNext != null) && _this.getStep(iNext); + if (!next_step || !next_step.backdrop || next_step.backdropElement !== step.backdropElement) { + _this._hideOverlayElement(step); + } + } + + if (step.onHidden != null) + { + return step.onHidden(_this); + } + }; + })(this); + + hideDelay = step.delay.hide || step.delay; + if ({} + .toString.call(hideDelay) === '[object Number]' && hideDelay > 0) { + this._debug("Wait " + hideDelay + " milliseconds to hide the step " + (this._current + 1)); + window.setTimeout((function (_this) { + return function () { + return _this._callOnPromiseDone(promise, hideStepHelper); + }; + })(this), hideDelay); + } else { + this._callOnPromiseDone(promise, hideStepHelper); + } + return promise; + }; + + Tour.prototype.showStep = function (i) { + var path, + promise, + showDelay, + showStepHelper, + skipToPrevious, + step; + if (this.ended()) { + this._debug('Tour ended, showStep prevented.'); + return this; + } + step = this.getStep(i); + if (!step) { + return; + } + + skipToPrevious = i < this._current; + promise = this._makePromise(step.onShow != null ? step.onShow(this, i) : void 0); + this.setCurrentStep(i); + + path = (function () { + switch ({} + .toString.call(step.path)) { + case '[object Function]': + return step.path(); + case '[object String]': + return this._options.basePath + step.path; + default: + return step.path; + } + }).call(this); + + + if (step.redirect && this._isRedirect(step.host, path, document.location)) { + this._redirect(step, i, path); + if (!this._isJustPathHashDifferent(step.host, path, document.location)) { + return; + } + } + + + showStepHelper = (function (_this) { + return function (e) { + if (_this._isOrphan(step)) { + if (step.orphan === false) + { + _this._debug("Skip the orphan step " + (_this._current + 1) + ".\nOrphan option is false and the element does not exist or is hidden."); + + if(typeof(step.onElementUnavailable) == "function") + { + step.onElementUnavailable(_this, _this._current + 1); + } + + if (skipToPrevious) { + _this._showPrevStep(); + } else { + _this._showNextStep(); + } + return; + } + _this._debug("Show the orphan step " + (_this._current + 1) + ". Orphans option is true."); + } + if (step.autoscroll) { + _this._scrollIntoView(i); + } else { + _this._showPopoverAndOverlay(i); + } + + if (step.duration) { + return _this.resume(); + } + }; + })(this); + + // delay in millisec specified in step options + showDelay = step.delay.show || step.delay; + if ({} + .toString.call(showDelay) === '[object Number]' && showDelay > 0) { + this._debug("Wait " + showDelay + " milliseconds to show the step " + (this._current + 1)); + window.setTimeout((function (_this) { + return function () { + return _this._callOnPromiseDone(promise, showStepHelper); + }; + })(this), showDelay); + } + else + { + if(step.delayOnElement) + { + // delay by element existence or max delay (default 2 sec) + var $delayElement = null; + var delayFunc = null; + var _this = this; + + if(typeof(step.delayOnElement.delayElement) == "function") + $delayElement = step.delayOnElement.delayElement(); + else if(step.delayOnElement.delayElement == "element") + $delayElement = $(step.element); + else + $delayElement = $(step.delayOnElement.delayElement); + + delayMax = (step.delayOnElement.maxDelay ? step.delayOnElement.maxDelay : 2000); + this._debug("Wait for element " + $delayElement[0].tagName + " visible or max " + delayMax + " milliseconds to show the step " + (this._current + 1)); + + delayFunc = window.setInterval( function() + { + _this._debug("Wait for element " + $delayElement[0].tagName + ": checking..."); + if($delayElement.is(':visible')) + { + _this._debug("Wait for element " + $delayElement[0].tagName + ": found, showing step"); + window.clearInterval(delayFunc); + delayFunc = null; + return _this._callOnPromiseDone(promise, showStepHelper); + } + }, 250); + + // set max delay to greater than default interval check for element appearance + if(delayMax < 250) + delayMax = 251; + + window.setTimeout( function () + { + if(delayFunc) + { + _this._debug("Wait for element " + $delayElement[0].tagName + ": max timeout reached without element found"); + window.clearInterval(delayFunc); + + // showStepHelper will handle broken/missing/invisible element + return _this._callOnPromiseDone(promise, showStepHelper); + } + }, delayMax); + } + else + { + // no delay by milliseconds or delay by time + this._callOnPromiseDone(promise, showStepHelper); + } + } + + return promise; + }; + + Tour.prototype.getCurrentStep = function () { + return this._current; + }; + + Tour.prototype.setCurrentStep = function (value) { + if (value != null) { + this._current = value; + this._setState('current_step', value); + } else { + this._current = this._getState('current_step'); + this._current = this._current === null ? 0 : parseInt(this._current, 10); + } + return this; + }; + + Tour.prototype.redraw = function () { + return this._showOverlayElement(this.getStep(this.getCurrentStep())); + }; + + Tour.prototype._setState = function (key, value) { + var e, + keyName; + if (this._options.storage) { + keyName = this._options.name + "_" + key; + try { + this._options.storage.setItem(keyName, value); + } catch (error) { + e = error; + if (e.code === DOMException.QUOTA_EXCEEDED_ERR) { + this._debug('LocalStorage quota exceeded. State storage failed.'); + } + } + return this._options.afterSetState(keyName, value); + } else { + if (this._state == null) { + this._state = {}; + } + return this._state[key] = value; + } + }; + + Tour.prototype._removeState = function (key) { + var keyName; + if (this._options.storage) { + keyName = this._options.name + "_" + key; + this._options.storage.removeItem(keyName); + return this._options.afterRemoveState(keyName); + } else { + if (this._state != null) { + return delete this._state[key]; + } + } + }; + + Tour.prototype._getState = function (key) { + var keyName, + value; + if (this._options.storage) { + keyName = this._options.name + "_" + key; + value = this._options.storage.getItem(keyName); + } else { + if (this._state != null) { + value = this._state[key]; + } + } + if (value === void 0 || value === 'null') { + value = null; + } + this._options.afterGetState(key, value); + return value; + }; + + Tour.prototype._showNextStep = function () { + var promise, + showNextStepHelper, + step; + step = this.getStep(this._current); + + showNextStepHelper = (function (_this) { + return function (e) { + return _this.showStep(step.next); + }; + })(this); + + promise = void 0; + + if (step.onNext != null) + { + rslt = step.onNext(this); + + if(rslt === false) + { + return this.showStep(this._current); + } + + promise = this._makePromise(rslt); + } + + return this._callOnPromiseDone(promise, showNextStepHelper); + }; + + Tour.prototype._showPrevStep = function () { + var promise, + showPrevStepHelper, + step; + step = this.getStep(this._current); + showPrevStepHelper = (function (_this) { + return function (e) { + return _this.showStep(step.prev); + }; + })(this); + + promise = void 0; + + if (step.onPrev != null) + { + rslt = step.onPrev(this); + + if(rslt === false) + { + return this.showStep(this._current); + } + + promise = this._makePromise(rslt); + } + + return this._callOnPromiseDone(promise, showPrevStepHelper); + }; + + Tour.prototype._debug = function (text) { + if (this._options.debug) { + return window.console.log("Bootstrap Tour '" + this._options.name + "' | " + text); + } + }; + + Tour.prototype._isRedirect = function (host, path, location) { + var currentPath; + if ((host != null) && host !== '' && (({} + .toString.call(host) === '[object RegExp]' && !host.test(location.origin)) || ({} + .toString.call(host) === '[object String]' && this._isHostDifferent(host, location)))) { + return true; + } + currentPath = [location.pathname, location.search, location.hash].join(''); + return (path != null) && path !== '' && (({} + .toString.call(path) === '[object RegExp]' && !path.test(currentPath)) || ({} + .toString.call(path) === '[object String]' && this._isPathDifferent(path, currentPath))); + }; + + Tour.prototype._isHostDifferent = function (host, location) { + switch ({} + .toString.call(host)) { + case '[object RegExp]': + return !host.test(location.origin); + case '[object String]': + return this._getProtocol(host) !== this._getProtocol(location.href) || this._getHost(host) !== this._getHost(location.href); + default: + return true; + } + }; + + Tour.prototype._isPathDifferent = function (path, currentPath) { + return this._getPath(path) !== this._getPath(currentPath) || !this._equal(this._getQuery(path), this._getQuery(currentPath)) || !this._equal(this._getHash(path), this._getHash(currentPath)); + }; + + Tour.prototype._isJustPathHashDifferent = function (host, path, location) { + var currentPath; + if ((host != null) && host !== '') { + if (this._isHostDifferent(host, location)) { + return false; + } + } + currentPath = [location.pathname, location.search, location.hash].join(''); + if ({} + .toString.call(path) === '[object String]') { + return this._getPath(path) === this._getPath(currentPath) && this._equal(this._getQuery(path), this._getQuery(currentPath)) && !this._equal(this._getHash(path), this._getHash(currentPath)); + } + return false; + }; + + Tour.prototype._redirect = function (step, i, path) { + var href; + if ($.isFunction(step.redirect)) { + return step.redirect.call(this, path); + } else { + href = {} + .toString.call(step.host) === '[object String]' ? "" + step.host + path : path; + this._debug("Redirect to " + href); + if (this._getState('redirect_to') === ("" + i)) { + this._debug("Error redirection loop to " + path); + this._removeState('redirect_to'); + if (step.onRedirectError != null) { + return step.onRedirectError(this); + } + } else { + this._setState('redirect_to', "" + i); + return document.location.href = href; + } + } + }; + + Tour.prototype._isOrphan = function (step) { + return (step.element == null) || !$(step.element).length || $(step.element).is(':hidden') && ($(step.element)[0].namespaceURI !== 'http://www.w3.org/2000/svg'); + }; + + Tour.prototype._isLast = function () { + return this._current < this._options.steps.length - 1; + }; + + Tour.prototype._showPopoverAndOverlay = function (i) { + var step; + if (this.getCurrentStep() !== i || this.ended()) { + return; + } + step = this.getStep(i); + if (step.backdrop) { + this._showOverlayElement(step); + } + this._showPopover(step, i); + + this._fixBootstrapSelectPickerZindex(step); + + if (step.onShown != null) { + step.onShown(this); + } + return this; + }; + + Tour.prototype._showPopover = function (step, i) { + var $element, + $tip, + isOrphan, + options, + shouldAddSmart, + title, + content, + percentProgress, + modalObject; + + // is this step already visible? + if($(document).find(".popover.tour-" + this._options.name + ".tour-" + this._options.name + "-" + i).length == 0) + { + $(".tour-" + this._options.name).remove(); + options = $.extend({}, this._options); + isOrphan = this._isOrphan(step); + step.template = this._template(step, i); + + if (isOrphan) + { + step.element = 'body'; + step.placement = 'top'; + } + + $element = $(step.element); + + $modalObject = null; + + // is element a modal? + if(!isOrphan && ($element.hasClass("modal") || $element.data('bs.modal'))) + { + $modalObject = $element; + + // fix the element, the actual visible offset comes from modal > modal-dialog > modal-content and $element is used to calc this offset & size + $element = $(step.element).find(".modal-content"); + } + else + { + $element = $(step.element); + } + + $element.addClass("tour-" + this._options.name + "-element tour-" + this._options.name + "-" + i + "-element"); + if (step.options) { + $.extend(options, step.options); + } + if (step.reflex && !isOrphan) + { + $(step.reflexElement).addClass('tour-step-element-reflex').off((this._reflexEvent(step.reflex)) + ".tour-" + this._options.name).on((this._reflexEvent(step.reflex)) + ".tour-" + this._options.name, (function (_this) { + return function () { + if (_this._isLast()) { + return _this.next(); + } else { + return _this.end(); + } + }; + })(this)); + } + + + // is element inside a modal? + if($element.parents().hasClass("modal:first").length) + { + $modalObject = $element.parents().hasClass("modal:first"); + } + + var funcModalHelper = function(_this, $_modalObject) + { + return function () + { + _this._debug("Modal close triggered"); + + if(typeof(step.onModalHidden) == "function") + { + // if step onModalHidden returns false, do nothing. int, move to the step specified. Otherwise continue regular next/end functionality + var rslt; + + rslt = step.onModalHidden(_this, i); + + if(rslt === false) + { + _this._debug("onModalHidden returned exactly false, tour step unchanged"); + return; + } + + if(Number.isInteger(rslt)) + { + _this._debug("onModalHidden returned int, tour moving to step " + rslt + 1); + + $_modalObject.off("hidden.bs.modal", funcModalHelper); + return _this.goTo(rslt); + } + } + + $_modalObject.off("hidden.bs.modal", funcModalHelper); + + if (_this._isLast()) + { + return _this.next(); + } + else + { + return _this.end(); + } + }; + }(this, $modalObject); + + if($modalObject) + { + $modalObject.off("hidden.bs.modal", funcModalHelper).on("hidden.bs.modal", funcModalHelper); + } + + + + shouldAddSmart = step.smartPlacement === true && step.placement.search(/auto/i) === -1; + + title = step.title; + content = step.content; + percentProgress = parseInt(((i + 1) / this.getStepCount()) * 100); + + if(step.showProgressBar) + { + if(typeof(step.getProgressBarHTML) == "function") + { + content = step.getProgressBarHTML(percentProgress) + content; + } + else + { + content = '
' + content; + } + } + + if(step.showProgressText) + { + if(typeof(step.getProgressTextHTML) == "function") + { + title += step.getProgressTextHTML(i, percentProgress, this.getStepCount()); + } + else + { + title += '' + (i + 1) + '/' + this.getStepCount() + ''; + } + } + + $element.popover({ + placement: shouldAddSmart ? "auto " + step.placement : step.placement, + trigger: 'manual', + title: title, + content: content, + html: true, + animation: step.animation, + container: step.container, + template: step.template, + selector: step.element + }).popover('show'); + + $tip = $element.data('bs.popover') ? $element.data('bs.popover').tip() : $element.data('popover').tip(); + $tip.attr('id', step.id); + if ($element.css('position') === 'fixed') { + $tip.css('position', 'fixed'); + } + + this._debug("Step " + (this._current + 1) + " of " + this._options.steps.length); + } + else + { + if (this._isOrphan(step)) + { + step.element = 'body'; + step.placement = 'top'; + } + + $element = $(step.element); + $tip = $element.data('bs.popover') ? $element.data('bs.popover').tip() : $element.data('popover').tip(); + } + + if (isOrphan) + { + return this._center($tip); + } + else + { + return this._reposition($tip, step); + } + }; + + Tour.prototype._template = function (step, i) { + var $navigation, + $next, + $prev, + $resume, + $template, + template; + template = step.template; + if (this._isOrphan(step) && {} + .toString.call(step.orphan) !== '[object Boolean]') { + template = step.orphan; + } + $template = $.isFunction(template) ? $(template(i, step)) : $(template); + $navigation = $template.find('.popover-navigation'); + $prev = $navigation.find('[data-role="prev"]'); + $next = $navigation.find('[data-role="next"]'); + $resume = $navigation.find('[data-role="pause-resume"]'); + if (this._isOrphan(step)) { + $template.addClass('orphan'); + } + $template.addClass("tour-" + this._options.name + " tour-" + this._options.name + "-" + i); + if (step.reflex) { + $template.addClass("tour-" + this._options.name + "-reflex"); + } + if (step.prev < 0) { + $prev.addClass('disabled').prop('disabled', true).prop('tabindex', -1); + } + if (step.next < 0) { + $next.addClass('disabled').prop('disabled', true).prop('tabindex', -1); + } + if (step.reflexOnly && step.reflex) { + $next.hide(); + } + if (!step.duration) { + $resume.remove(); + } + return $template.clone().wrap('
').parent().html(); + }; + + Tour.prototype._reflexEvent = function (reflex) { + if ({} + .toString.call(reflex) === '[object Boolean]') { + return 'click'; + } else { + return reflex; + } + }; + + Tour.prototype._reposition = function ($tip, step) { + var offsetBottom, + offsetHeight, + offsetRight, + offsetWidth, + originalLeft, + originalTop, + tipOffset; + offsetWidth = $tip[0].offsetWidth; + offsetHeight = $tip[0].offsetHeight; + + tipOffset = $tip.offset(); + originalLeft = tipOffset.left; + originalTop = tipOffset.top; + + offsetBottom = $(document).height() - tipOffset.top - $tip.outerHeight(); + if (offsetBottom < 0) { + tipOffset.top = tipOffset.top + offsetBottom; + } + + offsetRight = $('html').outerWidth() - tipOffset.left - $tip.outerWidth(); + if (offsetRight < 0) { + tipOffset.left = tipOffset.left + offsetRight; + } + if (tipOffset.top < 0) { + tipOffset.top = 0; + } + if (tipOffset.left < 0) { + tipOffset.left = 0; + } + + $tip.offset(tipOffset); + + if (step.placement === 'bottom' || step.placement === 'top') { + if (originalLeft !== tipOffset.left) { + return this._replaceArrow($tip, (tipOffset.left - originalLeft) * 2, offsetWidth, 'left'); + } + } else { + if (originalTop !== tipOffset.top) { + return this._replaceArrow($tip, (tipOffset.top - originalTop) * 2, offsetHeight, 'top'); + } + } + }; + + Tour.prototype._center = function ($tip) { + return $tip.css('top', $(window).outerHeight() / 2 - $tip.outerHeight() / 2); + }; + + Tour.prototype._replaceArrow = function ($tip, delta, dimension, position) { + return $tip.find('.arrow').css(position, delta ? 50 * (1 - delta / dimension) + '%' : ''); + }; + + Tour.prototype._scrollIntoView = function (i) { + var $element, + $window, + counter, + height, + offsetTop, + scrollTop, + step, + windowHeight; + step = this.getStep(i); + $element = $(step.element); + if (!$element.length) { + return this._showPopoverAndOverlay(i); + } + $window = $(window); + offsetTop = $element.offset().top; + height = $element.outerHeight(); + windowHeight = $window.height(); + scrollTop = 0; + switch (step.placement) { + case 'top': + scrollTop = Math.max(0, offsetTop - (windowHeight / 2)); + break; + case 'left': + case 'right': + scrollTop = Math.max(0, (offsetTop + height / 2) - (windowHeight / 2)); + break; + case 'bottom': + scrollTop = Math.max(0, (offsetTop + height) - (windowHeight / 2)); + } + this._debug("Scroll into view. ScrollTop: " + scrollTop + ". Element offset: " + offsetTop + ". Window height: " + windowHeight + "."); + counter = 0; + return $('body, html').stop(true, true).animate({ + scrollTop: Math.ceil(scrollTop) + }, (function (_this) { + return function () { + if (++counter === 2) { + _this._showPopoverAndOverlay(i); + return _this._debug("Scroll into view.\nAnimation end element offset: " + ($element.offset().top) + ".\nWindow height: " + ($window.height()) + "."); + } + }; + })(this)); + }; + + Tour.prototype._onResize = function (callback, timeout) { + return $(window).on("resize.tour-" + this._options.name, function () { + clearTimeout(timeout); + return timeout = setTimeout(callback, 100); + }); + }; + + Tour.prototype._onScroll = function (callback, timeout) { + return $(window).on("scroll.tour-" + this._options.name, function () { + clearTimeout(timeout); + return timeout = setTimeout(callback, 100); + }); + }; + + Tour.prototype._initMouseNavigation = function () { + var _this; + _this = this; + return $(document).off("click.tour-" + this._options.name, ".popover.tour-" + this._options.name + " *[data-role='prev']").off("click.tour-" + this._options.name, ".popover.tour-" + this._options.name + " *[data-role='next']").off("click.tour-" + this._options.name, ".popover.tour-" + this._options.name + " *[data-role='end']").off("click.tour-" + this._options.name, ".popover.tour-" + this._options.name + " *[data-role='pause-resume']").on("click.tour-" + this._options.name, ".popover.tour-" + this._options.name + " *[data-role='next']", (function (_this) { + return function (e) { + e.preventDefault(); + return _this.next(); + }; + })(this)).on("click.tour-" + this._options.name, ".popover.tour-" + this._options.name + " *[data-role='prev']", (function (_this) { + return function (e) { + e.preventDefault(); + if (_this._current > 0) { + return _this.prev(); + } + }; + })(this)).on("click.tour-" + this._options.name, ".popover.tour-" + this._options.name + " *[data-role='end']", (function (_this) { + return function (e) { + e.preventDefault(); + return _this.end(); + }; + })(this)).on("click.tour-" + this._options.name, ".popover.tour-" + this._options.name + " *[data-role='pause-resume']", function (e) { + var $this; + e.preventDefault(); + $this = $(this); + $this.text(_this._paused ? $this.data('pause-text') : $this.data('resume-text')); + if (_this._paused) { + return _this.resume(); + } else { + return _this.pause(); + } + }); + }; + + Tour.prototype._initKeyboardNavigation = function () { + if (!this._options.keyboard) { + return; + } + return $(document).on("keyup.tour-" + this._options.name, (function (_this) { + return function (e) { + if (!e.which) { + return; + } + switch (e.which) { + case 39: + e.preventDefault(); + if (_this._isLast()) { + return _this.next(); + } else { + return _this.end(); + } + break; + case 37: + e.preventDefault(); + if (_this._current > 0) { + return _this.prev(); + } + } + }; + })(this)); + }; + + Tour.prototype._makePromise = function (result) { + if (result && $.isFunction(result.then)) { + return result; + } else { + return null; + } + }; + + Tour.prototype._callOnPromiseDone = function (promise, cb, arg) { + if (promise) { + return promise.then( + (function (_this) + { + return function (e) + { + return cb.call(_this, arg); + }; + } + )(this) + ); + } else { + return cb.call(this, arg); + } + }; + + // Bootstrap Select custom draws the drop down, force the Z index between Tour overlay and popoper + Tour.prototype._fixBootstrapSelectPickerZindex = function(step) + { + if(!this._isOrphan(step)) + { + // is this element or child of this element a selectpicker + if($(step.element)[0].tagName.toLowerCase() == "select") + { + $selectpicker = $(step.element); + } + else + { + $selectpicker = $(step.element).find("select:first"); + } + + // is this selectpicker a bootstrap-select: https://github.com/snapappointments/bootstrap-select/ + if($selectpicker.parent().hasClass("bootstrap-select")) + { + // set zindex to open dropdown over background element + $selectpicker.parent().css("z-index", "1101"); + } + } + } + + + Tour.prototype._showBackground = function (step, data) { + var $backdrop, + base, + height, + j, + len, + pos, + ref, + results, + width; + + height = $(document).height(); + width = $(document).width(); + ref = ['top', 'bottom', 'left', 'right']; + results = []; + + + for (j = 0, len = ref.length; j < len; j++) { + pos = ref[j]; + $backdrop = (base = this.backdrops)[pos] != null ? base[pos] : base[pos] = $('
', { + "class": "tour-backdrop " + pos + }); + $(step.backdropContainer).append($backdrop); + switch (pos) { + case 'top': + results.push($backdrop.height(data.offset.top > 0 ? data.offset.top : 0).width(width).offset({ + top: 0, + left: 0 + })); + break; + case 'bottom': + results.push($backdrop.offset({ + top: data.offset.top + data.height, + left: 0 + }).height(height - (data.offset.top + data.height)).width(width)); + break; + case 'left': + results.push($backdrop.offset({ + top: data.offset.top, + left: 0 + }).height(data.height).width(data.offset.left > 0 ? data.offset.left : 0)); + break; + case 'right': + results.push($backdrop.offset({ + top: data.offset.top, + left: data.offset.left + data.width + }).height(data.height).width(width - (data.offset.left + data.width))); + break; + default: + results.push(void 0); + } + } + + return results; + }; + + Tour.prototype._showOverlayElement = function (step) { + var $backdropElement, + elementData; + + if(step.preventInteraction) + { + $(step.backdropContainer).append("
"); + $("#tourPrevent").width($(step.element).outerWidth()); + $("#tourPrevent").height($(step.element).outerHeight()); + $("#tourPrevent").offset($(step.element).offset()); + } + + $backdropElement = $(step.backdropElement); + if ($backdropElement.length === 0) + { + elementData = { + width: 0, + height: 0, + offset: { + top: 0, + left: 0 + } + }; + } + else + { + elementData = { + width: $backdropElement.innerWidth(), + height: $backdropElement.innerHeight(), + offset: $backdropElement.offset() + }; + $backdropElement.addClass('tour-step-backdrop'); + if (step.backdropPadding) { + elementData = this._applyBackdropPadding(step.backdropPadding, elementData); + } + } + + return this._showBackground(step, elementData); + }; + + Tour.prototype._hideOverlayElement = function (step) { + var $backdrop, + pos, + ref; + + // remove any previous interaction overlay + if($("#tourPrevent").length) + $("#tourPrevent").remove(); + + $(step.backdropElement).removeClass('tour-step-backdrop'); + ref = this.backdrops; + for (pos in ref) { + $backdrop = ref[pos]; + if ($backdrop && $backdrop.remove !== void 0) { + $backdrop.remove(); + } + } + return this.backdrops = []; + }; + + Tour.prototype._applyBackdropPadding = function (padding, data) { + if (typeof padding === 'object') { + if (padding.top == null) { + padding.top = 0; + } + if (padding.right == null) { + padding.right = 0; + } + if (padding.bottom == null) { + padding.bottom = 0; + } + if (padding.left == null) { + padding.left = 0; + } + data.offset.top = data.offset.top - padding.top; + data.offset.left = data.offset.left - padding.left; + data.width = data.width + padding.left + padding.right; + data.height = data.height + padding.top + padding.bottom; + } else { + data.offset.top = data.offset.top - padding; + data.offset.left = data.offset.left - padding; + data.width = data.width + (padding * 2); + data.height = data.height + (padding * 2); + } + return data; + }; + + Tour.prototype._clearTimer = function () { + window.clearTimeout(this._timer); + this._timer = null; + return this._duration = null; + }; + + Tour.prototype._getProtocol = function (url) { + url = url.split('://'); + if (url.length > 1) { + return url[0]; + } else { + return 'http'; + } + }; + + Tour.prototype._getHost = function (url) { + url = url.split('//'); + url = url.length > 1 ? url[1] : url[0]; + return url.split('/')[0]; + }; + + Tour.prototype._getPath = function (path) { + return path.replace(/\/?$/, '').split('?')[0].split('#')[0]; + }; + + Tour.prototype._getQuery = function (path) { + return this._getParams(path, '?'); + }; + + Tour.prototype._getHash = function (path) { + return this._getParams(path, '#'); + }; + + Tour.prototype._getParams = function (path, start) { + var j, + len, + param, + params, + paramsObject; + params = path.split(start); + if (params.length === 1) { + return {}; + } + params = params[1].split('&'); + paramsObject = {}; + for (j = 0, len = params.length; j < len; j++) { + param = params[j]; + param = param.split('='); + paramsObject[param[0]] = param[1] || ''; + } + return paramsObject; + }; + + Tour.prototype._equal = function (obj1, obj2) { + var j, + k, + len, + obj1Keys, + obj2Keys, + v; + if ({} + .toString.call(obj1) === '[object Object]' && {} + .toString.call(obj2) === '[object Object]') { + obj1Keys = Object.keys(obj1); + obj2Keys = Object.keys(obj2); + if (obj1Keys.length !== obj2Keys.length) { + return false; + } + for (k in obj1) { + v = obj1[k]; + if (!this._equal(obj2[k], v)) { + return false; + } + } + return true; + } else if ({} + .toString.call(obj1) === '[object Array]' && {} + .toString.call(obj2) === '[object Array]') { + if (obj1.length !== obj2.length) { + return false; + } + for (k = j = 0, len = obj1.length; j < len; k = ++j) { + v = obj1[k]; + if (!this._equal(v, obj2[k])) { + return false; + } + } + return true; + } else { + return obj1 === obj2; + } + }; + + return Tour; + + })(); + return Tour; +}); diff --git a/www/cluster.mustache.html b/www/cluster.mustache.html old mode 100644 new mode 100755 index 7fd0c966..48ea9174 --- a/www/cluster.mustache.html +++ b/www/cluster.mustache.html @@ -1,4 +1,5 @@ + - + diff --git a/www/nodes.js b/www/nodes.js old mode 100644 new mode 100755 index 486e2ddb..e587cc74 --- a/www/nodes.js +++ b/www/nodes.js @@ -4,39 +4,69 @@ if (q) { q = q.split('+').join(' '); $('#words').val(q); $('#words').css('font-weight', 'bold'); - $('#words').change(function() { + $('#words').change(function () { $('#words').css('font-weight', 'normal'); }); }; if (window.location.pathname.endsWith('topics') || window.location.pathname.endsWith('topics.local.html')) { var i = window.location.pathname.lastIndexOf('topics'); - var base_url = window.location.origin + window.location.pathname.substr(0,i); + var base_url = window.location.origin + window.location.pathname.substr(0, i); } else { var base_url = window.location.origin + window.location.pathname; } -var combineWords = function(words) { - return d3.keys(words).sort(function(a,b) { +var combineWords = function (words) { + return d3.keys(words).sort(function (a, b) { if (words[a] > words[b]) return -1; else if (words[a] < words[b]) return 1; else return 0; - }).join(", ") + ", ..."; + }).join(", ") + ", ..."; } -var margin = {top: 20, right: 80, bottom: 80, left: 40}, +var nodeTour = new Tour({ + name: "nodeTour", + smartPlacement: false, + debug: true, + steps: [ + { + element: "#chart", + title: "Topic Map", + content: "The collection of circles below is the Topic Map. It represents the topics from the all the trained models on a two-dimensional map that attempts to place similar topics close to each other.", + placement: "top" + }, { + element: "#form-group-tour", + title: "Document Search", + content: "To limit your topic search, type in some key words for your desired topic and then click 'View Topic Clusters.' This will adjust the color saturation such that the more saturated the node is, the more relevant it is to the entered key word(s).", + placement: "top" + } + ] +}); +nodeTour.init(); + +// start tour +jQuery(document).ready(function ($){ + $('#tour-button').click(function () { + nodeTour.restart(); + }); +}); + + + + +var margin = { top: 20, right: 80, bottom: 80, left: 40 }, width = $('#chart').parent().width() - margin.left - margin.right, height = $(document).height() - Math.min($('#main').height(), 400) - margin.top - margin.bottom, padding = 1, // separation between nodes radius = 30; var x = d3.scale.linear() - .range([0, width ]); + .range([0, width]); var y = d3.scale.linear() .range([height, 0]); @@ -57,35 +87,41 @@ var yAxis = d3.svg.axis() var controls = d3.select("#chart").append("label") .attr("id", "controls") - .attr("class", "hide"); + .attr("class", "hide") var checkbox = controls.append("input") .attr("id", "collisiondetection") .attr("type", "checkbox"); controls.append("span") .text("Collision detection"); +nodeTour.addStep({ + element: "#controls", + title: "Collision Detection", + content: "Checking the 'Collision detection' checkbox will minimize overlap among the nodes but distort the underlying similarity relationships.", + placement: "top" +}); var svg = d3.select("#chart").append("svg") .attr("width", width + margin.left + margin.right) .attr("height", height + margin.top + margin.bottom) .append("g") .attr("transform", "translate(" + margin.left + "," + margin.top + ")"); +// .attr("data-step", 4); -$(document).ready(function() { +$(document).ready(function () { $.ajaxSetup({ cache: true }); }); var ext_data; -d3.csv(base_url + "cluster.csv", function(error, data) { +d3.csv(base_url + "cluster.csv", function (error, data) { var topics = {}; var colors = {}; var node; - var ks = data.map(function(d) { return parseInt(d.k); }).filter(function(item, i, ar){ return ar.indexOf(item) === i; });; - + var ks = data.map(function (d) { return parseInt(d.k); }).filter(function (item, i, ar) { return ar.indexOf(item) === i; });; // sidebar items var cluster_view = $('#sidebar-topics li:first-child').html(); $('#sidebar-topics').html(''); if (!window.location.pathname.endsWith('topics.local.html')) $('#sidebar-topics').append('
  • ' + cluster_view + "
  • "); - ks.map(function(k) { $('#sidebar-topics').append('
  • ' + k + '
  • ') }); + ks.map(function (k) { $('#sidebar-topics').append('
  • ' + k + '
  • ') }); $('#sidebar-topics li a').tooltip(); var sizes = d3.scale.linear() @@ -94,9 +130,9 @@ d3.csv(base_url + "cluster.csv", function(error, data) { ext_data = data; var xVar = "orig_x", - yVar = "orig_y"; + yVar = "orig_y"; - data.forEach(function(d) { + data.forEach(function (d) { d[xVar] = parseFloat(d[xVar]); d[yVar] = parseFloat(d[yVar]); }); @@ -107,21 +143,24 @@ d3.csv(base_url + "cluster.csv", function(error, data) { .on("tick", tick) .charge(-1) .gravity(0); - //.chargeDistance(20); + //.chargeDistance(20); - x.domain(d3.extent(data, function(d) { return d[xVar]; })).nice(); - y.domain(d3.extent(data, function(d) { return d[yVar]; })).nice(); + x.domain(d3.extent(data, function (d) { return d[xVar]; })).nice(); + y.domain(d3.extent(data, function (d) { return d[yVar]; })).nice(); var prev = data[0].k; var currentTop = 0; // Set initial positions - data.forEach(function(d) { + data.forEach(function (d) { d.x = x(d[xVar]); d.y = y(d[yVar]); d.color = color(d.cluster); d.radius = sizes(d.k); if (d.k != prev) { prev = d.k; currentTop = 0; } d.topic = currentTop++; + if (currentTop == 0) { + d.attr() + } }); $('#chart #loading').remove(); @@ -140,11 +179,11 @@ d3.csv(base_url + "cluster.csv", function(error, data) { .attr("y", -6) .style("text-anchor", "end") .text("Sepal Width (cm)");*/ -/* - svg.append("g") - .attr("class", "y axis") - .call(yAxis); - */ + /* + svg.append("g") + .attr("class", "y axis") + .call(yAxis); + */ /* .append("text") .attr("class", "label") @@ -153,97 +192,131 @@ d3.csv(base_url + "cluster.csv", function(error, data) { .attr("dy", ".71em") .style("text-anchor", "end") .text("Sepal Length (cm)");*/ - - var k_urls = ks.map(function(k) { return base_url + k + "/topics.json" }); + + var k_urls = ks.map(function (k) { return base_url + k + "/topics.json" }); console.log(ks, k_urls); Promise.all(k_urls.map($.getJSON)).then(function (data) { - data.forEach(function(d,i) { - colors[ks[i]] = {}; - $.each(d, function(key, val) { colors[ks[i]][key] = val.color }); - }); - data.forEach(function(d,i) { - topics[ks[i]] = {}; - $.each(d, function(key, val) { topics[ks[i]][key] = combineWords(val.words) }); - }); - }).then(function() { - var words = inpho.util.getValueForURLParam('q'); - return (words) ? $.getJSON('topics.json?q=' + words) : null }) - .catch(function(error) { + data.forEach(function (d, i) { + colors[ks[i]] = {}; + $.each(d, function (key, val) { colors[ks[i]][key] = val.color }); + }); + data.forEach(function (d, i) { + topics[ks[i]] = {}; + $.each(d, function (key, val) { topics[ks[i]][key] = combineWords(val.words) }); + }); + }).then(function () { + var words = inpho.util.getValueForURLParam('q'); + return (words) ? $.getJSON('topics.json?q=' + words) : null + }) + .catch(function (error) { var words = inpho.util.getValueForURLParam('q'); if (error.status == 404) { $('#words').parents('.form-group').addClass('has-error'); $('#words').attr('placeholder', 'Terms not in corpus: "' + words + '". Try another query...'); $('#words').val(''); - } else if (error.status == 410) { + } else if (error.status == 410) { $('#words').parents('.form-group').addClass('has-warning'); $('#words').attr('placeholder', 'Terms removed by stoplisting: "' + words + '". Try another query...'); $('#words').val(''); } - }).then(function(distData) { + }).then(function (distData) { if (distData != null) { - $('#words').parents('.form-group').removeClass('has-error'); - $('#words').parents('.form-group').removeClass('has-warning'); - $('#words').parents('.form-group').addClass('has-success'); - opacity.domain(d3.extent(distData, function(d) { return d['distance']; })).nice(); - for (var obj in distData) { - obj = distData[obj]; - data.map(function(d) { if (d.k == obj.k.toString() && d.topic == obj.t) d.opacity = obj.distance; }); - } + $('#words').parents('.form-group').removeClass('has-error'); + $('#words').parents('.form-group').removeClass('has-warning'); + $('#words').parents('.form-group').addClass('has-success'); + opacity.domain(d3.extent(distData, function (d) { return d['distance']; })).nice(); + for (var obj in distData) { + obj = distData[obj]; + data.map(function (d) { if (d.k == obj.k.toString() && d.topic == obj.t) d.opacity = obj.distance; }); + } } - }).then(function() { + }).then(function () { console.log(data); node = svg.selectAll(".dot") - .data(data, function(d) { return d.k + '-' + d.topic; }) + .data(data, function (d) { return d.k + '-' + d.topic; }) .enter().append("circle") - .attr("class", "dot") - .attr("id", function(d) { return d.k + '_' + d.topic; }) - .attr("r", function(d) { return d.radius; }) - .attr("cx", function(d) { return x(d[xVar]); }) - .attr("cy", function(d) { return y(d[yVar]); }) - .style("fill", function(d) { return d.color; }) - .style("fill-opacity", function(d) { return opacity(d.opacity) || 0.7; }) - .on("click", function(d) { window.location.href = base_url + d.k + "/?topic=" + d.topic }) - .attr("title", function(d) { - return "Topic " + d.topic + " (k=" + d.k + ")" - + "
    " + topics[d.k][d.topic]; - }) - .on("mouseover", function (d) { $(this).tooltip('show')}) - .on("mouseout", function (d) { $(this).tooltip('hide')}); - - $(".dot").tooltip({container:'body', trigger: 'manual', animation: false, html: true}); - - }); -/* - var legend = svg.selectAll(".legend") - .data(color.domain()) - .enter().append("g") - .attr("class", "legend") - .attr("transform", function(d, i) { return "translate(0," + i * 20 + ")"; }); - - legend.append("rect") - .attr("x", width - 18) - .attr("width", 18) - .attr("height", 18) - .style("fill", color); - - legend.append("text") - .attr("x", width - 24) - .attr("y", 9) - .attr("dy", ".35em") - .style("text-anchor", "end") - .text(function(d) { return d; }); -*/ - d3.select("#collisiondetection").on("change", function() { + .attr("class", "dot") + .attr("id", function (d) { return d.k + '_' + d.topic; }) + .attr("r", function (d) { return d.radius; }) + .attr("cx", function (d) { return x(d[xVar]); }) + .attr("cy", function (d) { return y(d[yVar]); }) + + .attr("data-step", function (d) { + if ((d.k + "_" + d.topic) === "20_0") { + nodeTour.addStep({ + element: "#20_0", + title: "Topic Circles", + smartPlacement: false, + debug: true, + content: "Clicking on any topic circle will take you to the Hypershelf with the top documents for that topic already selected.", + placement: "top", + autoscroll: "false" + }); + nodeTour.addStep({ + element: "#home-page", + title: "Home Page", + content: "Click here to return to the home page.", + placement: "bottom", + autoscroll: "false" + }); + } else { + return -1; + } + }) + .attr("data-intro", function (d) { + if ((d.k + "_" + d.topic) === "20_0") { + //return "Clicking on any topic circle will take you to the Hypershelf with the top documents for that topic already selected."; + return "clicking"; + } else { + return -1; + } + }) + + .style("fill", function (d) { return d.color; }) + .style("fill-opacity", function (d) { return opacity(d.opacity) || 0.7; }) + .on("click", function (d) { window.location.href = base_url + d.k + "/?topic=" + d.topic }) + .attr("title", function (d) { + return "Topic " + d.topic + " (k=" + d.k + ")" + + "
    " + topics[d.k][d.topic]; + }) + .on("mouseover", function (d) { $(this).tooltip('show') }) + .on("mouseout", function (d) { $(this).tooltip('hide') }); + + $(".dot").tooltip({ container: 'body', trigger: 'manual', animation: false, html: true }); + + }); + + /* + var legend = svg.selectAll(".legend") + .data(color.domain()) + .enter().append("g") + .attr("class", "legend") + .attr("transform", function(d, i) { return "translate(0," + i * 20 + ")"; }); + + legend.append("rect") + .attr("x", width - 18) + .attr("width", 18) + .attr("height", 18) + .style("fill", color); + + legend.append("text") + .attr("x", width - 24) + .attr("y", 9) + .attr("dy", ".35em") + .style("text-anchor", "end") + .text(function(d) { return d; }); + */ + d3.select("#collisiondetection").on("change", function () { if (!checkbox.node().checked) - force + force .charge(0) .gravity(0) //.chargeDistance(0) .start(); else - force + force .charge(-1) .gravity(0) //.chargeDistance(20) @@ -256,12 +329,12 @@ d3.csv(base_url + "cluster.csv", function(error, data) { if (checkbox.node().checked) node.each(collide(e.alpha)); - node.attr("cx", function(d) { return d.x; }) - .attr("cy", function(d) { return d.y; }); + node.attr("cx", function (d) { return d.x; }) + .attr("cy", function (d) { return d.y; }); } function moveTowardDataPosition(alpha) { - return function(d) { + return function (d) { d.x += (x(d[xVar]) - d.x) * 0.1 * alpha; d.y += (y(d[yVar]) - d.y) * 0.1 * alpha; }; @@ -270,18 +343,18 @@ d3.csv(base_url + "cluster.csv", function(error, data) { // Resolve collisions between nodes. function collide(alpha) { var quadtree = d3.geom.quadtree(data); - return function(d) { + return function (d) { var r = d.radius + radius + padding, - nx1 = d.x - r, - nx2 = d.x + r, - ny1 = d.y - r, - ny2 = d.y + r; - quadtree.visit(function(quad, x1, y1, x2, y2) { + nx1 = d.x - r, + nx2 = d.x + r, + ny1 = d.y - r, + ny2 = d.y + r; + quadtree.visit(function (quad, x1, y1, x2, y2) { if (quad.point && (quad.point !== d)) { var x = d.x - quad.point.x, - y = d.y - quad.point.y, - l = Math.sqrt(x * x + y * y), - r = d.radius + quad.point.radius + (d.color !== quad.point.color) * padding; + y = d.y - quad.point.y, + l = Math.sqrt(x * x + y * y), + r = d.radius + quad.point.radius + (d.color !== quad.point.color) * padding; if (l < r) { l = (l - r) / l * alpha; d.x -= x *= l; @@ -296,20 +369,20 @@ d3.csv(base_url + "cluster.csv", function(error, data) { } }); -var toggleDisplay = function(k) { - var selection = d3.selectAll(".dot").filter(function(d) { return d.k.toString() == k.toString() }); +var toggleDisplay = function (k) { + var selection = d3.selectAll(".dot").filter(function (d) { return d.k.toString() == k.toString() }); console.log(selection); if (selection.style('display') == 'none') { selection.style('display', 'inline'); $('.sidebar-nav li a') - .filter(function() { return $(this).text() == k.toString() }) - .addClass('bg-info'); + .filter(function () { return $(this).text() == k.toString() }) + .addClass('bg-info'); } else { selection.style('display', 'none'); $('.sidebar-nav li a') - .filter(function() { return $(this).text() == k.toString() }) - .removeClass('bg-info'); + .filter(function () { return $(this).text() == k.toString() }) + .removeClass('bg-info'); } } diff --git a/www/splash.mustache.html b/www/splash.mustache.html old mode 100644 new mode 100755 index e165d62c..63aef98b --- a/www/splash.mustache.html +++ b/www/splash.mustache.html @@ -1,50 +1,60 @@ - + -
    -
    -
    -

    InPhO Topic Explorer

    -

    - {{#corpus_link}}{{corpus_name}}{{/corpus_link}} - {{^corpus_link}}{{corpus_name}}{{/corpus_link}}
    - About this corpus

    -

    Topic models are about contexts, rather than individual words.
    - We search a topic model using full documents, rather than keywords.
    Enter the model by one of the following methods:

    -
    -
    - -
    Document -
    -
    -
    -
    Topic
    -
    -
    - -
    -
    -
    -
    -

    About this corpus
    - Back to top

    -
    - -
    -

    Back to top

    + +
    +
    +
    +

    InPhO + Topic Explorer

    +

    + {{#corpus_link}}{{corpus_name}}{{/corpus_link}} + {{^corpus_link}}{{corpus_name}}{{/corpus_link}}
    + About this corpus
    +
    +
    +
    +

    +

    Topic models are about contexts, rather than individual words.
    + We search a topic model using full documents, rather than keywords.
    Enter the model by one of the + following methods:

    +
    +
    + +
    Document +
    +
    +
    +
    Topic
    +
    +
    + +
    +
    +
    +
    +

    About this corpus
    + Back to top

    +
    +
    - -
    -
    +
    +
    @@ -98,28 +113,64 @@
    -

    After typing words above, the topics most likely to generate that word will appear below. Clicking "View topic clusters" will go to a view of the topic space, weighted by the terms in the box. This makes it easy to find groups of topics related to your interests.

    +

    After typing words above, the topics most likely to generate that word will appear below. Clicking "View + topic clusters" will go to a view of the topic space, weighted by the terms in the box. This makes it easy to + find groups of topics related to your interests.

    - + - + + diff --git a/www/topicprint.js b/www/topicprint.js old mode 100644 new mode 100755 index e9c3b195..c470406f --- a/www/topicprint.js +++ b/www/topicprint.js @@ -1,62 +1,151 @@ var taTimeout; -$(".typeahead").typeahead({items: 12, - source: function(query, process) { +$(".typeahead").typeahead({ + items: 12, + source: function (query, process) { if (taTimeout) clearTimeout(taTimeout); this.$menu.find('.active').removeClass('active'); - var processAutocomplete = function(data) { - labels = []; - mapped = {}; - $.each(data, function(i, item) { - mapped[item.label] = item; - labels.push(item.label); - }) - - process(labels); + var processAutocomplete = function (data) { + labels = []; + mapped = {}; + $.each(data, function (i, item) { + mapped[item.label] = item; + labels.push(item.label); + }) + + process(labels); }; taTimeout = setTimeout(function () { $.getJSON('../docs.json?q=' + encodeURIComponent(query), processAutocomplete) - .error(function() { - $.getJSON('docs.json?q=' + encodeURIComponent(query), processAutocomplete) - })}, 300); + .error(function () { + $.getJSON('docs.json?q=' + encodeURIComponent(query), processAutocomplete) + }) + }, 300); }, - updater: function(item) { - if (!item) { - $('#hidden_id').val(''); - return this.$element.val(); - } else { - $('#hidden_id').val(mapped[item].id); - resetBars(); - return item; - } + updater: function (item) { + if (!item) { + $('#hidden_id').val(''); + return this.$element.val(); + } else { + $('#hidden_id').val(mapped[item].id); + resetBars(); + return item; + } }, - sorter: function(items) { - /*if (items.length == 1) { - $('#hidden_id').val(mapped[items[0]].id); - console.log("setting hidden_id" + $('#hidden_id').val()); - if (!$('#autocompleteDoc').hasClass('active')) { - $('#autocompleteDoc').addClass('active'); - $('span.icon-font', '#autocompleteDoc').removeClass('icon-font').addClass('icon-file'); - } - items.length = 0; - } else*/ - var query = this.query; - items = items.sort(); - var start = items.filter(function(item) { return item.lastIndexOf(query, 0) == 0;}); - var elsewhere = items.filter(function(item) { return item.lastIndexOf(query, 0) != 0;}); - return start.concat(elsewhere); + sorter: function (items) { + /*if (items.length == 1) { + $('#hidden_id').val(mapped[items[0]].id); + console.log("setting hidden_id" + $('#hidden_id').val()); + if (!$('#autocompleteDoc').hasClass('active')) { + $('#autocompleteDoc').addClass('active'); + $('span.icon-font', '#autocompleteDoc').removeClass('icon-font').addClass('icon-file'); + } + items.length = 0; + } else*/ + var query = this.query; + items = items.sort(); + var start = items.filter(function (item) { return item.lastIndexOf(query, 0) == 0; }); + var elsewhere = items.filter(function (item) { return item.lastIndexOf(query, 0) != 0; }); + return start.concat(elsewhere); } }); -$('#randomDoc', '#fingerprintModal').click(function() { - $.getJSON(fingerprint.host + 'docs.json?random=1', function(rand) { - $('#hidden_id', '#fingerprintModal').val(rand[0].id); - $('#doc', '#fingerprintModal').val(rand[0].label); - resetBars(); +var tour = new Tour({ + steps: [ + { + element: "#doc", + title: "Document Selection", + content: "There are a few ways to select a new focal document. One is by typing in a few letters in the focal document entry area.", + placement: "top" + }, { + element: "#randomDoc", + title: "Random Document Selection", + content: "You can also click the button to the right of the focal document entry area for a random document.", + placement: "top" + }, { + element: "#tutorial", + title: "Random Document Selection", + content: "You may use this button to visualize the focal document and you may use the dropdown menu attached to the button to switch to a model with a different number of topics.", + placement: "top" + }, { + element: "#visualize_button", + title: "Continuation of Tutorial", + content: "From this point onwards, you can either exit the tutorial, type in a document yourself and click the 'Visualize' button, or you can click 'Next' and allow the tutorial to pick a document for you.", + placement: "bottom", + onNext: function(tour) { + $("#randomDoc").click(); + //console.log(document.getElementById("#doc").value); + //$("#submit").click(); + + setTimeout(function () { + $("#submit").click(); + }, 500); + /* + if(tour.started() && !tour.ended()) + { + tour.init(); + tour.start(); + } + */ + + }, + + }, { + element:"#chart", + title: "Hypershelf", + content: "The Hypershelf shows up to 40 documents that are most similar to the focal document. Each document is represented by a bar whose colors show the mixture and proportions of topics assigned to each document by the training process. The relative lengths of the bars indicate the degree of similarity to the focal document according to the topic mixtures.", + placement: "top", + //delay: 1000 + //autoscroll: false, + //smartPlacement: false + + }, + { + element:"#legend", + title: "Legend", + content: "This is a legend designed to navigate and manipulate data on the Hypershelf easier. You can click on any of the colored squares to focus on a particular topic, alphabetize the papers, and normalize the topic bars.", + placement:"left" + }, + { + element: "#docDemonstration", + title: "Documents", + content:"The Hypershelf shows up to 40 documents that are most similar to the focal document. Each document is represented by a bar whose colors show the mixture and proportions of topics assigned to each document by the training process. The relative lengths of the bars indicate the degree of similarity to the focal document according to the topic mixtures.", + placement:"bottom" + }, + + { + element: "#home-page", + title: "Home Page", + content: "Click on this to return to the home page", + placement: "bottom" + } + + ] +}); +tour.init(); + +if(!tour.ended()) + { + tour.init(); + tour.start(); + } + +// start tour +jQuery(document).ready(function ($){ + $('#tour-button').click(function () { + tour.restart(); + }); +}); + +$('#randomDoc', '#fingerprintModal').click(function () { + $.getJSON(fingerprint.host + 'docs.json?random=1', function (rand) { + $('#hidden_id', '#fingerprintModal').val(rand[0].id); + $('#doc', '#fingerprintModal').val(rand[0].label); + resetBars(); }); }); -$('#randomDoc', '#fingerprintModal').tooltip({title: "Random Document", placement: 'bottom'}); +$('#randomDoc', '#fingerprintModal').tooltip({ title: "Random Document", placement: 'bottom' }); function resetBars() { $('#singleBarsDl').html(''); @@ -66,150 +155,155 @@ function resetBars() { function resetBar(i, k) { var base_bar = - '
    ' - + '
    ' - + '
    ' - + '
    Loading documents...
    ' - + '
    ' - + '
    ' - + '
    ' - + '
    '; - + '
    ' + + '
    ' + + '
    ' + + '
    Loading documents...
    ' + + '
    ' + + '
    ' + + '
    ' + + '
    '; + $('#fingerprintModal #topicBars dl').append(base_bar); fingerprint.visualize(k); - + } function showFingerprint(id, label) { $('#doc', '#fingerprintModal').val(label); $('#hidden_id', '#fingerprintModal').val(id); $('#fingerprintModal').modal('show'); - $('#fingerprintModal').on('shown.bs.modal', - function(e) { - if (!$('dd', '#singleBarsDl').length) - resetBars() + $('#fingerprintModal').on('shown.bs.modal', + function (e) { + if (!$('dd', '#singleBarsDl').length) + resetBars() }); } var fingerprint = { - 'host' : '', - 'visualize' : function(k) { - + 'host': '', + 'visualize': function (k) { + var maxRows = 25; var minCols = 2; - + //${c.entity.sep_dir} var docid = $('#fingerprintModal #hidden_id').val(); if (!docid || docid == '') return; - + var width = $('#fingerprintModal .bar-container').width(), - height = 20; - + height = 20; + var x = d3.scale.linear() - .range([0, width]) - .domain([0,1.0]); - // TODO: Clear existing #bar{k} content - var svg = d3.select("#bar"+k+" #chart").insert("svg") - .attr("width", width) - .attr("height", height) - .attr("id","main") - .attr("class", "main"); - $('#bar'+k).hide(); - - function calculateTopicMap(data, scale, sortFn){ - data.forEach(function(d) { + .range([0, width]) + .domain([0, 1.0]); + // TODO: Clear existing #bar{k} content + var svg = d3.select("#bar" + k + " #chart").insert("svg") + .attr("width", width) + .attr("height", height) + .attr("class", "main"); + $('#bar' + k).hide(); + + function calculateTopicMap(data, scale, sortFn) { + data.forEach(function (d) { var sizeFactor = (scale) ? d.prob : 1.0 var x0 = 0; - if (sortFn) d.topicMap = d3.keys(d.topics) + if (sortFn) d.topicMap = d3.keys(d.topics) .sort(sortFn) - .map(function(name) { return {name: name, x0: x0, x1: x0 += +(d.topics[name]*sizeFactor) }; }); + .map(function (name) { return { name: name, x0: x0, x1: x0 += +(d.topics[name] * sizeFactor) }; }); else d.topicMap = d3.keys(d.topics) - .map(function(name) { return {name: name, x0: x0, x1: x0 += +(d.topics[name]*sizeFactor) }; }); + .map(function (name) { return { name: name, x0: x0, x1: x0 += +(d.topics[name] * sizeFactor) }; }); }); - + } - + var url = "/docs_topics/" + $('#fingerprintModal #hidden_id').val() + '.json?n=1' var host = fingerprint.host + k; - - d3.json(host + url, function(error, data) { - $('#fingerprintModal #status .bar', '#bar'+k).css('width', '50%').text('Loading topics...'); + + d3.json(host + url, function (error, data) { + $('#fingerprintModal #status .bar', '#bar' + k).css('width', '50%').text('Loading topics...'); if (error) { var isError = $('.bar.bar-danger '); - $('#fingerprintModal #status .progress', '#bar'+k).removeClass('active progress-striped'); - if(isError[0]){ - $('#fingerprintModal #status .progress', '#bar'+k).remove(); + $('#fingerprintModal #status .progress', '#bar' + k).removeClass('active progress-striped'); + if (isError[0]) { + $('#fingerprintModal #status .progress', '#bar' + k).remove(); } - else{ + else { var errormsg = "Invalid document: " + docid + "."; - $('#fingerprintModal #status .bar', '#bar'+k).addClass('bar-danger').text(errormsg); + $('#fingerprintModal #status .bar', '#bar' + k).addClass('bar-danger').text(errormsg); } return false; } - d3.json(host + "/topics.json", function(error_top, topics) { - $('#fingerprintModal #status .bar', '#bar'+k).css('width', '75%').text('Rendering chart...'); + d3.json(host + "/topics.json", function (error_top, topics) { + $('#fingerprintModal #status .bar', '#bar' + k).css('width', '75%').text('Rendering chart...'); if (error_top) { - var isError = $('.bar.bar-danger '); - $('#fingerprintModal #status .progress', '#bar'+k).removeClass('active progress-striped'); - if(isError[0]){ - $('#fingerprintModal #status .progress', '#bar'+k).remove(); + var isError = $('.bar.bar-danger '); + $('#fingerprintModal #status .progress', '#bar' + k).removeClass('active progress-striped'); + if (isError[0]) { + $('#fingerprintModal #status .progress', '#bar' + k).remove(); } - else{ - $('#fingerprintModal #status .bar', '#bar'+k).addClass('bar-danger').text('Could not load topic list.'); + else { + $('#fingerprintModal #status .bar', '#bar' + k).addClass('bar-danger').text('Could not load topic list.'); } return false; } - + var k = d3.keys(topics).length; - var full_explorer_url = host+"/?doc="+encodeURIComponent(docid); - - calculateTopicMap(data, true, function(a,b) {return data[0].topics[b] - data[0].topics[a];}); - + var full_explorer_url = host + "/?doc=" + encodeURIComponent(docid); + + calculateTopicMap(data, true, function (a, b) { return data[0].topics[b] - data[0].topics[a]; }); + // draw total bar var doc = svg.selectAll("doc") - .data(data) + .data(data) .enter().append("g") - .attr("class","doc"); - + .attr("class", "doc") + .attr("id",data); + // Draw topic bars doc.selectAll("rect") - .data(function(d) { return d.topicMap; }) + .data(function (d) { return d.topicMap; }) .enter().append("rect") - .attr("height", height) - .attr("x", function(d) { return x(d.x0); }) - .attr("width", function(d) { return x(d.x1) - x(d.x0); }) - .attr("class", function(d) { return "top_" + d.name; }) - .attr("title", function(d) { return d3.keys(topics[d.name]['words']).sort(function(a,b) { - if (topics[d.name]['words'][a] > topics[d.name]['words'][b]) - return -1; - else if (topics[d.name]['words'][a] < topics[d.name]['words'][b]) - return 1; - else - return 0; - }).join(", ") + ", ..."; }) - .on("mouseover", function(d) { - // SVG element z-index determined by render order, not style sheet - // so element must be reappended to the end on hover so border - // is not occluded - var parent = $(this).parent(); - $(this).detach().appendTo(parent); - }) - .on("click", function(d) { window.location = full_explorer_url; }) - .style("fill", function(d) { return barColors(colors[k][d.name], d.name, svg); }); - - $(".doc rect").tooltip({container:'body', - animation: false, placement: 'top'}); - - $('#status .bar', '#bar'+k).addClass('bar-success').css('width', '100%').text("Complete!"); - setTimeout(function() {$('#status', '#bar'+k).hide()}, 250); - setTimeout(function() { + .attr("height", height) + .attr("x", function (d) { return x(d.x0); }) + .attr("width", function (d) { return x(d.x1) - x(d.x0); }) + .attr("class", function (d) { return "top_" + d.name; }) + .attr("title", function (d) { + return d3.keys(topics[d.name]['words']).sort(function (a, b) { + if (topics[d.name]['words'][a] > topics[d.name]['words'][b]) + return -1; + else if (topics[d.name]['words'][a] < topics[d.name]['words'][b]) + return 1; + else + return 0; + }).join(", ") + ", ..."; + }) + .on("mouseover", function (d) { + // SVG element z-index determined by render order, not style sheet + // so element must be reappended to the end on hover so border + // is not occluded + var parent = $(this).parent(); + $(this).detach().appendTo(parent); + }) + .on("click", function (d) { window.location = full_explorer_url; }) + .style("fill", function (d) { return barColors(colors[k][d.name], d.name, svg); }); + + $(".doc rect").tooltip({ + container: 'body', + animation: false, placement: 'top' + }); + + $('#status .bar', '#bar' + k).addClass('bar-success').css('width', '100%').text("Complete!"); + setTimeout(function () { $('#status', '#bar' + k).hide() }, 250); + setTimeout(function () { $('#loading').hide(); - $('#bar'+k).show(); - $("#bar" + k).before("
    "+k+" Topics
    "); - }, 250); - - }); }); - } + $('#bar' + k).show(); + $("#bar" + k).before("
    " + k + " Topics
    "); + }, 250); + + }); + }); + } }; @@ -218,54 +312,56 @@ var fingerprint = { //splash.mustache.html var converter = new showdown.Converter({ - simplifiedAutoLink: true, - headerLevelStart : 3, - literalMidWordUnderscores: true, - strikethrough: true, - tables: true + simplifiedAutoLink: true, + headerLevelStart: 3, + literalMidWordUnderscores: true, + strikethrough: true, + tables: true }); -var insertAboutText = function(data) { - var html = converter.makeHtml(data); - $('#aboutText').html(html); - } +var insertAboutText = function (data) { + var html = converter.makeHtml(data); + $('#aboutText').html(html); +} $.get('description.md') .done(insertAboutText) - .fail(function() { + .fail(function () { $.get('../description.md') .done(insertAboutText) .fail( - function(data) { $('#aboutText').html('To add a description of this corpus, create a Markdown file and edit the main:corpus_desc option in config.ini.'); -})}); - -var combineWords = function(words) { - return d3.keys(words).sort(function(a,b) { - if (words[a] > words[b]) - return -1; - else if (words[a] < words[b]) - return 1; - else - return 0; - }).join(", ") + ", ..."; + function (data) { + $('#aboutText').html('To add a description of this corpus, create a Markdown file and edit the main:corpus_desc option in config.ini.'); + }) + }); + +var combineWords = function (words) { + return d3.keys(words).sort(function (a, b) { + if (words[a] > words[b]) + return -1; + else if (words[a] < words[b]) + return 1; + else + return 0; + }).join(", ") + ", ..."; } var wordTimeout; -$('#words').on('input', function() { +$('#words').on('input', function () { if (wordTimeout) clearTimeout(wordTimeout); var words = $(this).val().split(' '); - wordTimeout = setTimeout(function() { - gettopics(words); + wordTimeout = setTimeout(function () { + gettopics(words); $('#wordsBtn').attr('href', 'topics?q=' + words.join('+')); $('#wordsBtn2').attr('href', 'topics?q=' + words.join('+')); }, 500); }); -var k_urls = ks.map(function(k) { return '../' + k + "/topics.json" }); +var k_urls = ks.map(function (k) { return '../' + k + "/topics.json" }); var topics = Promise.all(k_urls.map($.getJSON)).then(function (data) { - var t = {}; - data.forEach(function(d,i) { - t[ks[i]] = {}; - $.each(d, function(key, val) { t[ks[i]][key] = combineWords(val.words) }); + var t = {}; + data.forEach(function (d, i) { + t[ks[i]] = {}; + $.each(d, function (key, val) { t[ks[i]][key] = combineWords(val.words) }); }); return t; }); @@ -275,10 +371,10 @@ var color = d3.scale.category20(); d3.csv('../cluster.csv', function (error, data) { var prev = data[0].k; var currentTop = 0; - data.forEach(function(d) { + data.forEach(function (d) { if (d.k != prev) { prev = d.k; currentTop = 0; } d.topic = currentTop++; - d.color = color(d.cluster); + d.color = color(d.cluster); if (colors[d.k] == undefined) colors[d.k] = {}; colors[d.k][d.topic] = d.color; }) @@ -288,39 +384,39 @@ function gettopics(words) { var query = words.join('|'); $('#wordsDl').html('') $.getJSON('topics.json?q=' + query) - .error(function(error) { - console.log(error); - if (error.status == 404) { - $('#words').parents('.form-group').addClass('has-error'); - $('#words').attr('placeholder', 'Terms not in corpus: "' + words + '". Try another query...'); - $('#words').val(''); - } else if (error.status == 410) { - $('#words').parents('.form-group').addClass('has-warning'); - $('#words').attr('placeholder', 'Terms removed by stoplisting: "' + words + '". Try another query...'); - $('#words').val(''); - } - }).success(function(data) { - $('#words').parents('.form-group').removeClass('has-error'); - $('#words').parents('.form-group').removeClass('has-warning'); - $('#words').parents('.form-group').addClass('has-success'); - Promise.resolve(topics).then(function(val) { - for (var i = 0; i < 10; i++) { - var k = data[i]['k']; - var t = data[i]['t']; - - /*$('#wordsDl').append('
    ' + - 'Topic ' + t + - ' (k = ' + k + ')
    '); - $('#wordsDl').append('
    ' + - val[k][t] + '
    '); }*/ - $('#wordsDl').append('

    ' + - 'Topic ' + t + - ' (k = ' + k + ')

    ' + - val[k][t] + '

    '); + .error(function (error) { + console.log(error); + if (error.status == 404) { + $('#words').parents('.form-group').addClass('has-error'); + $('#words').attr('placeholder', 'Terms not in corpus: "' + words + '". Try another query...'); + $('#words').val(''); + } else if (error.status == 410) { + $('#words').parents('.form-group').addClass('has-warning'); + $('#words').attr('placeholder', 'Terms removed by stoplisting: "' + words + '". Try another query...'); + $('#words').val(''); } - $('#wordsDl').append('
     
    '); + }).success(function (data) { + $('#words').parents('.form-group').removeClass('has-error'); + $('#words').parents('.form-group').removeClass('has-warning'); + $('#words').parents('.form-group').addClass('has-success'); + Promise.resolve(topics).then(function (val) { + for (var i = 0; i < 10; i++) { + var k = data[i]['k']; + var t = data[i]['t']; + + /*$('#wordsDl').append('
    ' + + 'Topic ' + t + + ' (k = ' + k + ')
    '); + $('#wordsDl').append('
    ' + + val[k][t] + '
    '); }*/ + $('#wordsDl').append('

    ' + + 'Topic ' + t + + ' (k = ' + k + ')

    ' + + val[k][t] + '

    '); + } + $('#wordsDl').append('
     
    '); + }); }); - }); } @@ -334,8 +430,9 @@ function gettopics(words) { var topDoc = document.getElementById("topicBtn"); var mainDoc = document.getElementById("doc"); $(window).load(function () { - //Breaks out of function when there are no topic bars, like on the landing page - if (topDoc == null) return; + //Breaks out of function when there are no topic bars, like on the landing page + if (topDoc == null) return; + if (mainDoc.value == "") { if (roottopic == null) { $("#focalDoc").text(""); @@ -347,7 +444,7 @@ $(window).load(function () { else { topDoc.style.display = 'block'; if (roottopic == null) { - $("#focalDoc").text("Top 40 documents most similar to the focal document"); + $("#focalDoc").text("Top 40 documents most similar to the focal document"); } else { $("#focalDoc").text("Top 40 documents most similar to topic " + roottopic); } @@ -356,42 +453,42 @@ $(window).load(function () { $(document).ready(function () { $('#cite').hide(); - $('#citeBtn').tooltip({title: "Show citation info", placement: 'bottom'}); + $('#citeBtn').tooltip({ title: "Show citation info", placement: 'bottom' }); var visited = document.cookie.replace(/(?:(?:^|.*;\s*)visited\s*\=\s*([^;]*).*$)|^.*$/, "$1"); if (visited != null) { $('.help').hide(); - $('#helpBtn').tooltip({title: "Show help", placement: 'bottom'}); + $('#helpBtn').tooltip({ title: "Show help", placement: 'bottom' }); } else { - $('#helpBtn').tooltip({title: "Hide help", placement: 'bottom'}); + $('#helpBtn').tooltip({ title: "Hide help", placement: 'bottom' }); $('#helpBtn').addClass('active'); } document.cookie = "visited=true; max-age=31536000;"; }); var scrollLegend; -$('#helpBtn').click(function() { - $('.help').toggle(); - if (!$('#helpBtn').hasClass('active')) { - $('#helpBtn').data('tooltip').options.title = "Hide help"; - $('#helpBtn').addClass('active'); - } else { - $('#helpBtn').data('tooltip').options.title = "Show help"; - $('#helpBtn').removeClass('active'); - } - scrollLegend(); - }); -$('#citeBtn').click(function() { - $('#cite').toggle(); - if (!$('#citeBtn').hasClass('active')) { - $('#citeBtn').data('tooltip').options.title = "Hide citation info"; - $('#citeBtn').addClass('active'); - } else { - $('#citeBtn').data('tooltip').options.title = "Show citation info"; - $('#citeBtn').removeClass('active'); - } - scrollLegend(); - }); +$('#helpBtn').click(function () { + $('.help').toggle(); + if (!$('#helpBtn').hasClass('active')) { + $('#helpBtn').data('tooltip').options.title = "Hide help"; + $('#helpBtn').addClass('active'); + } else { + $('#helpBtn').data('tooltip').options.title = "Show help"; + $('#helpBtn').removeClass('active'); + } + scrollLegend(); +}); +$('#citeBtn').click(function () { + $('#cite').toggle(); + if (!$('#citeBtn').hasClass('active')) { + $('#citeBtn').data('tooltip').options.title = "Hide citation info"; + $('#citeBtn').addClass('active'); + } else { + $('#citeBtn').data('tooltip').options.title = "Show citation info"; + $('#citeBtn').removeClass('active'); + } + scrollLegend(); +}); var q = inpho.util.getValueForURLParam('q') || null; if (q) { @@ -400,43 +497,43 @@ if (q) { } $('#doc').css('font-weight', 'bold'); -$('#doc').on('change', function() { $(this).css('font-weight', 'normal')}); +$('#doc').on('change', function () { $(this).css('font-weight', 'normal') }); var docid = inpho.util.getValueForURLParam('doc') || null; if (docid) { docid = decodeURIComponent(docid); $('#hidden_id').val(docid); - $('.twitter-share-button').attr('data-text', "What's similar to " +docid+"? Discover with the #InPhO Topic Explorer"); - $.getJSON('../docs.json?id='+encodeURIComponent(docid), function(title) { + $('.twitter-share-button').attr('data-text', "What's similar to " + docid + "? Discover with the #InPhO Topic Explorer"); + $.getJSON('../docs.json?id=' + encodeURIComponent(docid), function (title) { if (title.length) { title = title[0].label; - $('.title').html('{{doc_title_format}}'.format(title,'{{doc_url_format}}'.format(docid))); + $('.title').html('{{doc_title_format}}'.format(title, '{{doc_url_format}}'.format(docid))); $('#doc').val(title); $('#autocompleteDoc').removeAttr('disabled').button('toggle'); $('span.glyphicon-font', '#autocompleteDoc').removeClass('glyphicon-font').addClass('glyphicon-file'); - $('.twitter-share-button').attr('data-text', "What's similar to " + title +"? Discover with the #InPhO Topic Explorer!"); + $('.twitter-share-button').attr('data-text', "What's similar to " + title + "? Discover with the #InPhO Topic Explorer!"); } else { - $('.title').html('{{doc_title_format}}'.format(docid,'{{doc_url_format}}'.format(docid))); + $('.title').html('{{doc_title_format}}'.format(docid, '{{doc_url_format}}'.format(docid))); $('#doc').val(docid); } }); } else if (q) { - $('#search-label').html("Words") - $('.title').html('the query "'+q+'"'); - $('#doc').val(q); -/* TODO: Migrate to call /docs.json?q=q - title = data.filter(function(item) { return item.label.toLowerCase().indexOf(q.toLowerCase()) >= 0 }); - if (title.length) { - $('#autocompleteDoc').removeAttr('disabled'); - } - */ + $('#search-label').html("Words") + $('.title').html('the query "' + q + '"'); + $('#doc').val(q); + /* TODO: Migrate to call /docs.json?q=q + title = data.filter(function(item) { return item.label.toLowerCase().indexOf(q.toLowerCase()) >= 0 }); + if (title.length) { + $('#autocompleteDoc').removeAttr('disabled'); + } + */ } var roottopic = inpho.util.getValueForURLParam('topic') || null; if (roottopic) { $('.title').html('Topic ' + roottopic); - $('.twitter-share-button').attr('data-text', "Check out topic "+ roottopic+" at the #InPhO Topic Explorer!"); + $('.twitter-share-button').attr('data-text', "Check out topic " + roottopic + " at the #InPhO Topic Explorer!"); $('.topdoc').text('Top Documents for Topic ' + roottopic); $('.topdoc').css('font-weight', 'bold'); $('.topdoc').addClass('btn-default'); @@ -449,8 +546,8 @@ if (docid || roottopic || q) else $('.null').show(); -$('#searchForm').submit(function(event) { - visualize(); +$('#searchForm').submit(function (event) { + visualize(); event.preventDefault(); }); @@ -458,10 +555,10 @@ function visualize(k) { var url = "{0}{1}".format(window.location.origin, window.location.pathname); if (k && tops) url = url.replace('/' + Object.keys(tops).length + '/', - '/' + k + '/'); + '/' + k + '/'); if ($("#autocompleteDoc").hasClass('active') && !($("#autocompleteDoc").attr("disabled") == 'disabled')) { - url += "?doc=" + encodeURIComponent($("#hidden_id").val() || docid) ; + url += "?doc=" + encodeURIComponent($("#hidden_id").val() || docid); } else { url += "?q=" + encodeURIComponent($("#doc").val()).replace(/%20/g, '|'); } @@ -470,71 +567,73 @@ function visualize(k) { } var taTimeout; -$(".typeahead").typeahead({items: 12, - source: function(query, process) { +$(".typeahead").typeahead({ + items: 12, + source: function (query, process) { if (taTimeout) clearTimeout(taTimeout); this.$menu.find('.active').removeClass('active'); taTimeout = setTimeout(function () { - $.getJSON('../docs.json?q=' + encodeURIComponent(query), function(data) { + $.getJSON('../docs.json?q=' + encodeURIComponent(query), function (data) { labels = []; mapped = {}; - $.each(data, function(i, item) { + $.each(data, function (i, item) { mapped[item.label] = item; labels.push(item.label); }); - + process(labels); - })}, 300); + }) + }, 300); }, - updater: function(item) { - if (!item) { - if ($('#autocompleteDoc').hasClass('active')) { - $('#autocompleteDoc').removeClass('active'); - $('span.glyphicon-file', '#autocompleteDoc').removeClass('glyphicon-file').addClass('glyphicon-font'); - } - $('#hidden_id').val(''); - return this.$element.val(); - } else { - if (!$('#autocompleteDoc').hasClass('active')) { - $('#autocompleteDoc').addClass('active'); - $('span.glyphicon-font', '#autocompleteDoc').removeClass('glyphicon-font').addClass('glyphicon-file'); - } - $('#autocompleteDoc').removeAttr('disabled'); - - $('#hidden_id').val(mapped[item].id); - return item; + updater: function (item) { + if (!item) { + if ($('#autocompleteDoc').hasClass('active')) { + $('#autocompleteDoc').removeClass('active'); + $('span.glyphicon-file', '#autocompleteDoc').removeClass('glyphicon-file').addClass('glyphicon-font'); + } + $('#hidden_id').val(''); + return this.$element.val(); + } else { + if (!$('#autocompleteDoc').hasClass('active')) { + $('#autocompleteDoc').addClass('active'); + $('span.glyphicon-font', '#autocompleteDoc').removeClass('glyphicon-font').addClass('glyphicon-file'); } + $('#autocompleteDoc').removeAttr('disabled'); + + $('#hidden_id').val(mapped[item].id); + return item; + } }, - sorter: function(items) { - /*if (items.length == 1) { - $('#hidden_id').val(mapped[items[0]].id); - console.log("setting hidden_id" + $('#hidden_id').val()); - if (!$('#autocompleteDoc').hasClass('active')) { - $('#autocompleteDoc').addClass('active'); - $('span.glyphicon-font', '#autocompleteDoc').removeClass('glyphicon-font').addClass('glyphicon-file'); - } - items.length = 0; - } else*/ - if(items.length > 0) { - $('#autocompleteDoc').removeAttr('disabled'); - } else if (items.length == 0) { - if ($('#autocompleteDoc').hasClass('active')) { - $('#autocompleteDoc').removeClass('active'); - $('span.glyphicon-file', '#autocompleteDoc').removeClass('glyphicon-file').addClass('glyphicon-font'); - } - $('#autocompleteDoc').attr('disabled','disabled'); + sorter: function (items) { + /*if (items.length == 1) { + $('#hidden_id').val(mapped[items[0]].id); + console.log("setting hidden_id" + $('#hidden_id').val()); + if (!$('#autocompleteDoc').hasClass('active')) { + $('#autocompleteDoc').addClass('active'); + $('span.glyphicon-font', '#autocompleteDoc').removeClass('glyphicon-font').addClass('glyphicon-file'); + } + items.length = 0; + } else*/ + if (items.length > 0) { + $('#autocompleteDoc').removeAttr('disabled'); + } else if (items.length == 0) { + if ($('#autocompleteDoc').hasClass('active')) { + $('#autocompleteDoc').removeClass('active'); + $('span.glyphicon-file', '#autocompleteDoc').removeClass('glyphicon-file').addClass('glyphicon-font'); } - var query = this.query; - items = items.sort(); - var start = items.filter(function(item) { return item.lastIndexOf(query, 0) == 0;}); - var elsewhere = items.filter(function(item) { return item.lastIndexOf(query, 0) != 0;}); - return start.concat(elsewhere); + $('#autocompleteDoc').attr('disabled', 'disabled'); + } + var query = this.query; + items = items.sort(); + var start = items.filter(function (item) { return item.lastIndexOf(query, 0) == 0; }); + var elsewhere = items.filter(function (item) { return item.lastIndexOf(query, 0) != 0; }); + return start.concat(elsewhere); } }); - -$('#autocompleteDoc').click(function() { + +$('#autocompleteDoc').click(function () { if (!$('#autocompleteDoc').hasClass('active')) $('.typeahead').typeahead('lookup').focus(); else { @@ -545,21 +644,21 @@ $('#autocompleteDoc').click(function() { }); -$('#randomDoc', '#searchForm').click(function() { - $.getJSON('../docs.json?random=1', function(rand) { - if (!$('#autocompleteDoc').hasClass('active')) { - $('#autocompleteDoc').button('toggle'); - $('span.glyphicon-font', '#autocompleteDoc').removeClass('glyphicon-font').addClass('glyphicon-file'); - } - $('#autocompleteDoc').removeAttr('disabled'); - $('#hidden_id', '#searchForm').val(rand[0].id); - $('#doc', '#searchForm').val(rand[0].label); - $('#doc', '#searchForm').css('font-weight', 'normal'); - $('#submit', '#searchForm').removeClass('btn-default'); - $('#submit', '#searchForm').addClass('btn-primary'); +$('#randomDoc', '#searchForm').click(function () { + $.getJSON('../docs.json?random=1', function (rand) { + if (!$('#autocompleteDoc').hasClass('active')) { + $('#autocompleteDoc').button('toggle'); + $('span.glyphicon-font', '#autocompleteDoc').removeClass('glyphicon-font').addClass('glyphicon-file'); + } + $('#autocompleteDoc').removeAttr('disabled'); + $('#hidden_id', '#searchForm').val(rand[0].id); + $('#doc', '#searchForm').val(rand[0].label); + $('#doc', '#searchForm').css('font-weight', 'normal'); + $('#submit', '#searchForm').removeClass('btn-default'); + $('#submit', '#searchForm').addClass('btn-primary'); }); }); -$('#randomDoc', '#searchForm').tooltip({title: "Random Document", placement: 'bottom'}); +$('#randomDoc', '#searchForm').tooltip({ title: "Random Document", placement: 'bottom' }); @@ -569,39 +668,39 @@ $('#randomDoc', '#searchForm').tooltip({title: "Random Document", placement: 'bo -if ($('.scale')[0] != null) $('.scale')[0].checked = (roottopic != null); +if ($('.scale')[0] != null) $('.scale')[0].checked = (roottopic != null); var ico; var maxRows = 25; var minCols = 2; -var margin = {top: 80, right: 40, bottom: 20, left: 15 + (icons.length * 20)}, - width = 960 - margin.left - margin.right, - height = 600 - margin.top - margin.bottom; +var margin = { top: 80, right: 40, bottom: 20, left: 15 + (icons.length * 20) }, + width = 960 - margin.left - margin.right, + height = 600 - margin.top - margin.bottom; var x = d3.scale.linear() - .range([0, width]); + .range([0, width]); var y = d3.scale.ordinal() - .rangeRoundBands([0, height], .1, 0); + .rangeRoundBands([0, height], .1, 0); var xAxis = d3.svg.axis() - .scale(x) - .orient("top") - .ticks(10, "%"); + .scale(x) + .orient("top") + .ticks(10, "%"); var yAxis = d3.svg.axis() - .scale(y) - .orient("left"); + .scale(y) + .orient("left"); -function computeWidth(numCols) { - $('#legend').attr("width", margin.right + (numCols*55) + 20 + margin.right); +function computeWidth(numCols) { + $('#legend').attr("width", margin.right + (numCols * 55) + 20 + margin.right); $('#chart #main').attr("width", Math.max($(window).width() - $('#legend').width() - 200 + margin.right, 750)); $('#controls').css("left", Math.max($(window).width() - $('#legend').width() - 200 + margin.right, 750) + 40); width = Math.max($(window).width() - $('#legend').width() - 200 + margin.right, 750) - margin.left - margin.right; x = d3.scale.linear() .range([0, width]); - x.domain([0,1]); + x.domain([0, 1]); xAxis = d3.svg.axis() .scale(x) .orient("top") @@ -609,11 +708,11 @@ function computeWidth(numCols) { } // Changed the height variable to better pair the topic bars with their documents -function computeHeight(data, numLegendRows) { +function computeHeight(data, numLegendRows) { height = (data.length * 35);// - margin.top - margin.bottom; y = d3.scale.ordinal() - .rangeRoundBands([0, height], .1, 0); - y.domain(data.map(function(d) { return d.id; })); + .rangeRoundBands([0, height], .1, 0); + y.domain(data.map(function (d) { return d.id; })); yAxis = d3.svg.axis() .scale(y) .orient("left"); @@ -623,35 +722,36 @@ var dataset; var original_root; var svg = d3.select("#chart").append("svg") - .attr("width", width + margin.left + margin.right) - .attr("height", height + margin.top + margin.bottom) - .attr("id","main") - .attr("class", "main") + .attr("width", width + margin.left + margin.right) + .attr("height", height + margin.top + margin.bottom) + .attr("id", "main") + .attr("class", "main") .append("g") - .attr("transform", "translate(" + margin.left + "," + margin.top + ")") - .on("mouseleave", function() { - $(".legend rect").removeClass('hover').tooltip('hide'); - }); + .attr("transform", "translate(" + margin.left + "," + margin.top + ")") + .on("mouseleave", function () { + $(".legend rect").removeClass('hover').tooltip('hide'); + }); var legend = d3.select("#chart").append("svg") - .attr("width", "350") - .attr("id", "legend") - .attr("class", "main") + .attr("width", "350") + .attr("id", "legend") + .attr("class", "main") .append("g") - .attr("transform","translate("+margin.right+","+ margin.top + ")"); + .attr("transform", "translate(" + margin.right + "," + margin.top + ")") + -function calculateTopicMap(data, scale, sortFn){ - data.forEach(function(d) { +function calculateTopicMap(data, scale, sortFn) { + data.forEach(function (d) { var sizeFactor = (scale) ? d.prob : 1.0 var x0 = 0; if (sortFn) d.topicMap = d3.keys(original_root.topics) .sort(sortFn) - .map(function(name) { return {name: name, x0: x0, x1: x0 += +(d.topics[name]*sizeFactor) }; }); + .map(function (name) { return { name: name, x0: x0, x1: x0 += +(d.topics[name] * sizeFactor) }; }); else // maintain sort order d.topicMap = d.topicMap.map(function (topic) { return topic.name; }) - .map(function(name) { return {name: name, x0: x0, x1: x0 += +(d.topics[name]*sizeFactor) }; }); + .map(function (name) { return { name: name, x0: x0, x1: x0 += +(d.topics[name] * sizeFactor) }; }); }); - + } var url; @@ -665,154 +765,154 @@ var n = inpho.util.getValueForURLParam('n'); if (n) url += "?n=" + n; var tops; -if (url) -d3.json(url, function(error, data) { - $('#status .bar').css('width', '50%').text('Loading topics...'); - if (error) { - $('#status .progress-bar').removeClass('active progress-bar-striped'); - var errormsg; - - if (roottopic) errormsg = "Invalid topic: " + roottopic + "."; - else if (q) errormsg = "Search terms not in model, try a different query." - else errormsg = "Invalid document: " + docid + "."; - - $('#status .bar').addClass('progress-bar-danger').text(errormsg); - return false; - } - d3.json("topics.json", function(error_top, topics) { - $('#status .bar').css('width', '75%').text('Rendering chart...'); - if (error_top) { +if (url) + d3.json(url, function (error, data) { + $('#status .bar').css('width', '50%').text('Loading topics...'); + if (error) { + $('#status .progress-bar').removeClass('active progress-bar-striped'); + var errormsg; + + if (roottopic) errormsg = "Invalid topic: " + roottopic + "."; + else if (q) errormsg = "Search terms not in model, try a different query." + else errormsg = "Invalid document: " + docid + "."; + + $('#status .bar').addClass('progress-bar-danger').text(errormsg); + return false; + } + d3.json("topics.json", function (error_top, topics) { + $('#status .bar').css('width', '75%').text('Rendering chart...'); + if (error_top) { $('#status .progress-bar').removeClass('active progress-bar-striped'); $('#status .progress-bar-danger').addClass('progress-bar-error').text('Could not load topic list.'); return false; } - console.log(topics); + console.log(topics); $('#submit').text(d3.keys(topics).length + ' Topics'); - - - var legendCols = Math.max(Math.ceil(d3.keys(topics).length / Math.min(data.length, maxRows)), minCols); - var legendFactor = Math.ceil(d3.keys(topics).length / legendCols); - computeHeight(data,legendFactor); - $("#chart #main").attr("height", height + margin.top + margin.bottom); - $("#legend").attr("height", (legendFactor * 20) + margin.top + margin.bottom); - computeWidth(legendCols); - - - x.domain([0, 1.0]); - tops = topics; + + + var legendCols = Math.max(Math.ceil(d3.keys(topics).length / Math.min(data.length, maxRows)), minCols); + var legendFactor = Math.ceil(d3.keys(topics).length / legendCols); + computeHeight(data, legendFactor); + $("#chart #main").attr("height", height + margin.top + margin.bottom); + $("#legend").attr("height", (legendFactor * 20) + margin.top + margin.bottom); + computeWidth(legendCols); + + + x.domain([0, 1.0]); + tops = topics; //.sort(); - dataset = data; - original_root = data[0]; - if (roottopic) docid = data[0]['doc']; - - svg.append("g") + dataset = data; + original_root = data[0]; + if (roottopic) docid = data[0]['doc']; + + svg.append("g") .attr("class", "x axis") .attr("transform", "translate(10,-10)") .call(xAxis) - .append("text") + .append("text") //.attr("transform", "rotate(-120)") .attr("class", "axis_label") .attr("dy", "-2em") .style("text-anchor", "start") .text("Similarity to " + $('.title').first().text()); - - svg.append("g") + + svg.append("g") .attr("class", "y axis") .call(yAxis) .selectAll("text") - .attr("class", function(d) { return (q == null && d == docid && roottopic == null) ? "primary" : "" }) - .on("click", function(d) { window.location.href = window.location.origin + window.location.pathname + "?doc=" + encodeURIComponent(d);}) - - svg.select(".y.axis").selectAll("g") + .attr("class", function (d) { return (q == null && d == docid && roottopic == null) ? "primary" : "" }) + .on("click", function (d) { window.location.href = window.location.origin + window.location.pathname + "?doc=" + encodeURIComponent(d); }) + + svg.select(".y.axis").selectAll("g") .insert("rect", ":first-child") - .attr("x", -margin.left + 5) - .attr("y", -9) - .attr("width", margin.left-5) - .attr("height", 18) - .style("opacity", "0"); - - var ticks = svg.select(".y.axis").selectAll("g") - .on("mouseenter", function(d) { + .attr("x", -margin.left + 5) + .attr("y", -9) + .attr("width", margin.left - 5) + .attr("height", 18) + .style("opacity", "0"); + + var ticks = svg.select(".y.axis").selectAll("g") + .on("mouseenter", function (d) { $('text', this).attr('text-decoration', 'underline') - .attr('font-weight', 'bold'); + .attr('font-weight', 'bold'); svg.selectAll(".doc") - .filter(function(doc,i) { return doc.doc == d}) - .attr("class", function(d) { return ((q == null && d.doc == docid && roottopic == null) ? "doc primary" : "doc") + " hover"}); - }) - .on("mouseleave", function(d) { - $('text',this).removeAttr('text-decoration') - .removeAttr('font-weight'); + .filter(function (doc, i) { return doc.doc == d }) + .attr("class", function (d) { return ((q == null && d.doc == docid && roottopic == null) ? "doc primary" : "doc") + " hover" }); + }) + .on("mouseleave", function (d) { + $('text', this).removeAttr('text-decoration') + .removeAttr('font-weight'); svg.selectAll(".doc") - .filter(function(doc,i) { return doc.doc == d}) - .attr("class", function(d) { return (q == null && d.doc == docid && roottopic == null) ? "doc primary" : "doc"}); - }); - - for (var i = 0; i < icons.length; i++) { - icon_fns[icons[i]](ticks,i, data); - } - - // draw total bar - var doc = svg.selectAll("doc") + .filter(function (doc, i) { return doc.doc == d }) + .attr("class", function (d) { return (q == null && d.doc == docid && roottopic == null) ? "doc primary" : "doc" }); + }); + + for (var i = 0; i < icons.length; i++) { + icon_fns[icons[i]](ticks, i, data); + } + + // draw total bar + var doc = svg.selectAll("doc") .data(data) - .enter().append("g") - .attr("class", function(d) { return (q == null && d.doc == docid && roottopic == null) ? "doc primary" : "doc"}) - .attr("transform", function(d) { return "translate(10," + y(d.id) + ")"; }) - .on("mouseover", function(d) { - var tick = $("text:contains(" + d.id +")") - .filter(function() { return $(this).text().trim() == d.id }) - .attr("font-weight", "bold"); - icons.reduce(function(prev,cur) { - return prev.next(".{0}Icon".format(cur)).css('opacity', '1.0'); - }, tick); - }) - .on("mouseout", function(d) { - var tick = $("text:contains(" + d.id +")") - .filter(function() {return $(this).text().trim() == d.id }) - .attr("font-weight", "normal"); - icons.reduce(function(prev, cur) { - return prev.next(".{0}Icon".format(cur)).css('opacity', ''); - }, tick); - }); - - var label2 = document.createElement('label'); - label2.classList.add("checkbox"); - label2.innerHTML = "Normalize Topic Bars";i - label2.style.left = "20px"; - - document.body.append(label2); - d3.select(".scale").on("change", scaleTopics); - - calculateTopicMap(data, !($('.scale')[0].checked), function(a,b) {return data[0].topics[b] - data[0].topics[a];}); - - var k = d3.keys(topics).length; - - // Draw topic bars - doc.selectAll("rect") - .data(function(d) { return d.topicMap; }) - .enter().append("rect") - .attr("height", y.rangeBand()/2.75) - .attr("x", function(d) { return x(d.x0); }) - .attr("y", 10) - .attr("width", function(d) { return x(d.x1) - x(d.x0); }) - .attr("class", function(d) { return "top_" + d.name; }) - .on("mouseover", function(d) { - // SVG element z-index determined by render order, not style sheet - // so element must be reappended to the end on hover so border - // is not occluded - var parent = $(this).parent(); - $(this).detach().appendTo(parent); - $(".docLabel", parent).detach().appendTo(parent); - $(".docLabel", parent).addClass("hover"); - $('.legend rect').not('.top_' + d.name).tooltip('hide'); - $(".top_" + d.name).addClass('hover'); - $('.legend rect.top_' + d.name).tooltip('show'); - }) - .on("mouseout", function(d) { - var parent = $(this).parent(); - $(".docLabel", parent).removeClass("hover"); - $(".top_" + d.name).removeClass('hover'); - }) - .on("click", function(d) { + .enter().append("g") + .attr("class", function (d) { return (q == null && d.doc == docid && roottopic == null) ? "doc primary" : "doc" }) + .attr("transform", function (d) { return "translate(10," + y(d.id) + ")"; }) + .on("mouseover", function (d) { + var tick = $("text:contains(" + d.id + ")") + .filter(function () { return $(this).text().trim() == d.id }) + .attr("font-weight", "bold"); + icons.reduce(function (prev, cur) { + return prev.next(".{0}Icon".format(cur)).css('opacity', '1.0'); + }, tick); + }) + .on("mouseout", function (d) { + var tick = $("text:contains(" + d.id + ")") + .filter(function () { return $(this).text().trim() == d.id }) + .attr("font-weight", "normal"); + icons.reduce(function (prev, cur) { + return prev.next(".{0}Icon".format(cur)).css('opacity', ''); + }, tick); + }); + + var label2 = document.createElement('label'); + label2.classList.add("checkbox"); + label2.innerHTML = "Normalize Topic Bars"; i + label2.style.left = "20px"; + + document.body.append(label2); + d3.select(".scale").on("change", scaleTopics); + + calculateTopicMap(data, !($('.scale')[0].checked), function (a, b) { return data[0].topics[b] - data[0].topics[a]; }); + + var k = d3.keys(topics).length; + + // Draw topic bars + doc.selectAll("rect") + .data(function (d) { return d.topicMap; }) + .enter().append("rect") + .attr("height", y.rangeBand() / 2.75) + .attr("x", function (d) { return x(d.x0); }) + .attr("y", 10) + .attr("width", function (d) { return x(d.x1) - x(d.x0); }) + .attr("class", function (d) { return "top_" + d.name; }) + .on("mouseover", function (d) { + // SVG element z-index determined by render order, not style sheet + // so element must be reappended to the end on hover so border + // is not occluded + var parent = $(this).parent(); + $(this).detach().appendTo(parent); + $(".docLabel", parent).detach().appendTo(parent); + $(".docLabel", parent).addClass("hover"); + $('.legend rect').not('.top_' + d.name).tooltip('hide'); + $(".top_" + d.name).addClass('hover'); + $('.legend rect.top_' + d.name).tooltip('show'); + }) + .on("mouseout", function (d) { + var parent = $(this).parent(); + $(".docLabel", parent).removeClass("hover"); + $(".top_" + d.name).removeClass('hover'); + }) + .on("click", function (d) { //Handles when to update the descriptor based off which mode it is in and what topic bar was clicked on. //Indicates whether the model is sorted by proportion of a specific topic or not. if (roottopic == null) { @@ -824,51 +924,53 @@ d3.json(url, function(error, data) { topDoc.style.display = 'block'; $("#focalDoc").text("Top 40 documents most similar to topic " + roottopic + " sorted by proportion of topic " + d.name); } - topicSort(d.name); }) - .style("fill", function(d) { return barColors(colors[k][d.name], d.name, svg); }); - - doc.append("text") - .text(function(d) { return d.label; }) - .attr("class","docLabel") - .attr("dx", "3") - .attr("dy", "8") - .filter(function(d) { return q && (d.label.indexOf(q) >= 0);}) - .each(function(d) { - if (q) { - var splits = q.split(' '); - var new_html = d.label; - for (var i = 0; i < splits.length; i++) { - var myRe = new RegExp(splits[i], 'gi'); - new_html = new_html.replace(myRe, '= 0); }) + .each(function (d) { + if (q) { + var splits = q.split(' '); + var new_html = d.label; + for (var i = 0; i < splits.length; i++) { + var myRe = new RegExp(splits[i], 'gi'); + new_html = new_html.replace(myRe, 'Topic {0}:".format(d) + "
    " - + d3.keys(topics[d].words).sort(function(a,b) { + + d3.keys(topics[d].words).sort(function (a, b) { if (topics[d].words[a] > topics[d].words[b]) return -1; else if (topics[d].words[a] < topics[d].words[b]) return 1; else return 0; - }).join(", ") + ", ..."; }) - .on("click", function(d) { + }).join(", ") + ", ..."; + }) + .on("click", function (d) { //Handles when to update the descriptor based off which mode it is in and what topic bar was clicked on. //Indicates whether the model is sorted by proportion of a specific topic or not. if (roottopic == null) { @@ -880,38 +982,39 @@ d3.json(url, function(error, data) { topDoc.style.display = 'block'; $("#focalDoc").text("Top 40 documents most similar to topic " + roottopic + " sorted by proportion of topic " + d); } - topicSort(d); }) - .on("mouseover", function(d) { - $(".top_" + d).addClass('hover').tooltip('show'); - }) - .on("mouseout", function(d) { - $(".top_" + d).removeClass('hover').tooltip('hide'); - }); - - $(".legend rect").tooltip({container:'body', trigger: 'manual', animation: false, html: true}); - - legendElts.append("text") + topicSort(d); + }) + .on("mouseover", function (d) { + $(".top_" + d).addClass('hover').tooltip('show'); + }) + .on("mouseout", function (d) { + $(".top_" + d).removeClass('hover').tooltip('hide'); + }); + + $(".legend rect").tooltip({ container: 'body', trigger: 'manual', animation: false, html: true }); + + legendElts.append("text") .attr("dx", -6) .attr("y", 9) .attr("dy", ".35em") .style("text-anchor", "end") - .text(function(d) { return d; }); - - - legend.append("text") + .text(function (d) { return d; }); + + + legend.append("text") .attr("dx", -6) .attr("dy", "-.35em") .attr("font-weight", "bold") .style("text-anchor", "end") .text(d3.keys(topics).length); - legend.append("text") + legend.append("text") //.attr("transform", "rotate(-120)") .attr("class", "axis_label") .attr("dy", "-.35em") .attr("font-weight", "bold") .style("text-anchor", "start") .text("Topics"); - legend.append("text") + legend.append("text") //.attr("transform", "rotate(-120)") .attr("class", "axis_label") .attr("dy", "-.45em") @@ -921,293 +1024,299 @@ d3.json(url, function(error, data) { .style("overflow-wrap", "normal") .text("ordered by proportion of T in " + (docid ? "focal document" : "corpus")); - var ns = 'http://www.w3.org/2000/svg'; - var newLegend = document.getElementById('legend'); - - var foreignObject = document.createElementNS(ns, 'foreignObject'); - foreignObject.setAttribute("width", 140); - foreignObject.setAttribute("height", 140); - foreignObject.setAttribute("transform", "translate(20, " + (((d3.keys(topics).length / 2) + 1) * 20 + 65) + ")"); - var div = document.createElement('div'); - div.innerHTML = 'Display Options'; - var label = document.createElement('label'); - label.classList.add("checkbox"); - label.innerHTML = "Alphabetical Sort"; - label.style.left = "20px"; - div.appendChild(label); - div.appendChild(label2); - - var button = document.createElement('button'); - $(button).addClass("btn btn-default reset"); - $(button).attr('disabled', true); - var t = document.createTextNode("Reset Topic Sort"); - button.appendChild(t); - div.appendChild(button); - div.appendChild(document.createElement('br')); - foreignObject.appendChild(div); - newLegend.append(foreignObject); - - button.onclick = resetTopicSort; - - d3.select(window).on('resize', resize); + var ns = 'http://www.w3.org/2000/svg'; + var newLegend = document.getElementById('legend'); + + var foreignObject = document.createElementNS(ns, 'foreignObject'); + foreignObject.setAttribute("width", 140); + foreignObject.setAttribute("height", 140); + foreignObject.setAttribute("transform", "translate(20, " + (((d3.keys(topics).length / 2) + 1) * 20 + 65) + ")"); + var div = document.createElement('div'); + div.innerHTML = 'Display Options'; + var label = document.createElement('label'); + label.classList.add("checkbox"); + label.innerHTML = "Alphabetical Sort"; + label.style.left = "20px"; + div.appendChild(label); + div.appendChild(label2); + + var button = document.createElement('button'); + $(button).addClass("btn btn-default reset"); + $(button).attr('disabled', true); + var t = document.createTextNode("Reset Topic Sort"); + button.appendChild(t); + div.appendChild(button); + div.appendChild(document.createElement('br')); + foreignObject.appendChild(div); + newLegend.append(foreignObject); + + button.onclick = resetTopicSort; + + d3.select(window).on('resize', resize); + + function resize() { + computeWidth(legendCols); + + /* Update the axis with the new scale */ + svg.select('.x.axis') + .call(xAxis); + + doc.selectAll('rect') + .attr("x", function (d) { return x(d.x0); }) + .attr("width", function (d) { return x(d.x1) - x(d.x0); }); + } - function resize() { - computeWidth(legendCols); - - /* Update the axis with the new scale */ - svg.select('.x.axis') - .call(xAxis); - - doc.selectAll('rect') - .attr("x", function(d) { return x(d.x0); }) - .attr("width", function(d) { return x(d.x1) - x(d.x0); }); } - d3.select(".sort").on("change", alphabetSort); - + $('#status .bar').addClass('bar-success').css('width', '100%').text("Complete!"); - setTimeout(function() { + setTimeout(function () { $('#status').hide(500); - setTimeout(function() {$('#controls').css({'top' : $('#legend').height() + $('#legend').position().top}).show();}, 500); - } , 500); - + setTimeout(function () { $('#controls').css({ 'top': $('#legend').height() + $('#legend').position().top }).show(); }, 500); + }, 500); + $(window).on("scroll", scrollLegend); - scrollLegend = function() { + scrollLegend = function () { var scrollPos = $(window).scrollTop(); var chartHeight = $('#chart').position().top; var legendHeight = $('#legend').height(); var heightFac = -60; - if((scrollPos - chartHeight - margin.top - heightFac) <= 0) { - $('#legend').css({'position': 'absolute', 'top' : chartHeight}); - $('#controls').css({'position': 'absolute', 'top' : legendHeight + chartHeight}); + if ((scrollPos - chartHeight - margin.top - heightFac) <= 0) { + $('#legend').css({ 'position': 'absolute', 'top': chartHeight }); + $('#controls').css({ 'position': 'absolute', 'top': legendHeight + chartHeight }); } else if ((scrollPos - chartHeight - heightFac) < (margin.top)) { - $('#legend').css({'position': 'absolute', 'top' : scrollPos + heightFac}); - $('#controls').css({'position': 'absolute', 'top' : legendHeight+ scrollPos + heightFac}); + $('#legend').css({ 'position': 'absolute', 'top': scrollPos + heightFac }); + $('#controls').css({ 'position': 'absolute', 'top': legendHeight + scrollPos + heightFac }); } else { - $('#legend').css({'position': 'fixed', 'top' : heightFac}); - $('#controls').css({'position': 'fixed', 'top' : legendHeight + heightFac}); - }} - + $('#legend').css({ 'position': 'fixed', 'top': heightFac }); + $('#controls').css({ 'position': 'fixed', 'top': legendHeight + heightFac }); + } + } + for (var i = 0; i < icons.length; i++) { - $(".{0}Icon".format(icons[i])).tooltip({placement: 'top', title: icon_tooltips[icons[i]], container: 'body', html: true, animation: false}); + $(".{0}Icon".format(icons[i])).tooltip({ placement: 'top', title: icon_tooltips[icons[i]], container: 'body', html: true, animation: false }); } - }); + document.querySelector(".doc").setAttribute("id", "docDemonstration"); + document.querySelector(".doc").setAttribute("data-intro", "The Hypershelf shows up to 40 documents that are most similar to the focal document. Each document is represented by a bar whose colors show the mixture and proportions of topics assigned to each document by the training process. The relative lengths of the bars indicate the degree of similarity to the focal document according to the topic mixtures.") + $("#doc").attr("data-intro", "To select a NEW document, you can type here or select random document"); + }); }); - function scaleTopics() { - var numTopics = dataset[0].topics.length; - var delay = function(d, i) { return i * (500/numTopics); }, - negdelay = function(d, i) { return (numTopics-i) * (500/numTopics); }; +function scaleTopics() { + var numTopics = dataset[0].topics.length; + var delay = function (d, i) { return i * (500 / numTopics); }, + negdelay = function (d, i) { return (numTopics - i) * (500 / numTopics); }; - calculateTopicMap(dataset, !this.checked); + calculateTopicMap(dataset, !this.checked); - $(".doc").each(function(i,elt) { - $(elt).children() - .sort(function(a,b) { return $(a).attr('x') - $(b).attr('x'); }) - .each(function(j,child) { - $(child).detach().appendTo($(elt)); - }) - }); + $(".doc").each(function (i, elt) { + $(elt).children() + .sort(function (a, b) { return $(a).attr('x') - $(b).attr('x'); }) + .each(function (j, child) { + $(child).detach().appendTo($(elt)); + }) + }); - svg.selectAll(".doc") - .selectAll("rect") - .data(function(d) { return d.topicMap; }) - .style("fill", function(d) { return barColors(colors[k][d.name], d.name, svg); }) - /*.on("mouseover", function(d) { - // SVG element z-index determined by render order, not style sheet - // so element must be reappended to the end on hover so border - // is not occluded - var parent = $(this).parent(); - $(this).detach().appendTo(parent); - $(".docLabel", parent).detach().appendTo(parent); - $('.legend rect').not('.top_' + d.name).tooltip('hide'); - $(".top_" + d.name).addClass('hover'); - $('.legend rect.top_' + d.name).tooltip('show'); - }) - .on("mouseout", function(d) { - $(".top_" + d.name).removeClass('hover'); - })*/ - .transition().duration(500).ease("linear").delay(this.checked ? delay : negdelay) - .attr("x", function(d) { return x(d.x0); }) - .attr("width", function(d) { return x(d.x1) - x(d.x0); }) - .attr("class", function(d) { return "top_" + d.name; }); - - svg.selectAll(".x.axis text.axis_label").text(this.checked ? - "Proportion of document assigned to topic" : - ("Similarity to " + $('.title').first().text())); - } + svg.selectAll(".doc") + .selectAll("rect") + .data(function (d) { return d.topicMap; }) + .style("fill", function (d) { return barColors(colors[k][d.name], d.name, svg); }) + /*.on("mouseover", function(d) { + // SVG element z-index determined by render order, not style sheet + // so element must be reappended to the end on hover so border + // is not occluded + var parent = $(this).parent(); + $(this).detach().appendTo(parent); + $(".docLabel", parent).detach().appendTo(parent); + $('.legend rect').not('.top_' + d.name).tooltip('hide'); + $(".top_" + d.name).addClass('hover'); + $('.legend rect.top_' + d.name).tooltip('show'); + }) + .on("mouseout", function(d) { + $(".top_" + d.name).removeClass('hover'); + })*/ + .transition().duration(500).ease("linear").delay(this.checked ? delay : negdelay) + .attr("x", function (d) { return x(d.x0); }) + .attr("width", function (d) { return x(d.x1) - x(d.x0); }) + .attr("class", function (d) { return "top_" + d.name; }); + + svg.selectAll(".x.axis text.axis_label").text(this.checked ? + "Proportion of document assigned to topic" : + ("Similarity to " + $('.title').first().text())); +} - function sortDataset(sortFn) { - dataset = dataset.sort(sortFn); +function sortDataset(sortFn) { + dataset = dataset.sort(sortFn); - var y0 = y.domain(dataset - .map(function(d) { return d.id; })) - .copy(); + var y0 = y.domain(dataset + .map(function (d) { return d.id; })) + .copy(); - var transition = svg.transition().duration(500), - delay = function(d, i) { return i * 25; }; + var transition = svg.transition().duration(500), + delay = function (d, i) { return i * 25; }; - transition.selectAll(".doc") - .delay(delay) - .attr("transform", function(d) { return "translate(10," + y(d.id) + ")"; }); - //.attr("y", function(d) { return y(d.id); }); + transition.selectAll(".doc") + .delay(delay) + .attr("transform", function (d) { return "translate(10," + y(d.id) + ")"; }); + //.attr("y", function(d) { return y(d.id); }); - transition.select(".y.axis") - .call(yAxis) - .selectAll("g") - .selectAll("text") - .delay(delay); - } + transition.select(".y.axis") + .call(yAxis) + .selectAll("g") + .selectAll("text") + .delay(delay); +} - function alphabetSort() { - // Copy-on-write since tweens are evaluated after a delay. - if (this.checked) - sortDataset(function(a, b) { return d3.ascending(a.label, b.label); }); - else - sortDataset(function(a, b) { return b.prob - a.prob; }); - } - - function resetTopicSort() { - $('.reset').attr('disabled',true); - $('.topicsort').attr('disabled',true); - $('.selected').removeClass('selected'); - $('.topdoc').text('Click a topic segment below to find related documents.'); - $('.topdoc').removeClass('btn-primary'); - $('.topdoc').addClass('btn-default'); - $('.topdoc').attr('disabled', 'disabled'); - $(document).ready(function() { - if (!($('.sort')[0].checked)) - sortDataset(function(a,b) { return b.prob - a.prob; }); - }); - redrawBars(function(a,b) { return original_root.topics[b] - original_root.topics[a]; }); - } +function alphabetSort() { + // Copy-on-write since tweens are evaluated after a delay. + if (this.checked) + sortDataset(function (a, b) { return d3.ascending(a.label, b.label); }); + else + sortDataset(function (a, b) { return b.prob - a.prob; }); +} - function topicSort(topic) { - // Copy-on-write since tweens are evaluated after a delay. - $('.sort').removeAttr('checked'); - if (topic) { - sortDataset(function(a, b) { return b.topics[topic] - a.topics[topic]; }); - $('.selected').removeClass('selected'); - $(".top_" + topic).addClass('selected'); - $('.reset').removeAttr('disabled'); - if (topic == roottopic) { - $('.topdoc').css('font-weight', 'bold'); - $('.topdoc').removeClass('btn-primary'); - $('.topdoc').addClass('btn-default'); - } else { - $('.topdoc').css('font-weight', 'normal'); - $('.topdoc').removeClass('btn-default'); - $('.topdoc').addClass('btn-primary'); - } - $('.topdoc').removeAttr('disabled'); - $('.topdoc').text('Retrieve Documents for Topic ' + topic); - $('.topdoc').click(function() { location.href = location.origin + location.pathname + '?topic=' + topic;}); - $('.topdoc').mouseenter(function() { - $('.legend rect').not('.top_' + topic).tooltip('hide'); - $(".legend rect.top_" + topic).tooltip('show'); }); - $('.topdoc').mouseleave(function() { $(".top_" + topic).tooltip('hide'); }); +function resetTopicSort() { + $('.reset').attr('disabled', true); + $('.topicsort').attr('disabled', true); + $('.selected').removeClass('selected'); + $('.topdoc').text('Click a topic segment below to find related documents.'); + $('.topdoc').removeClass('btn-primary'); + $('.topdoc').addClass('btn-default'); + $('.topdoc').attr('disabled', 'disabled'); + $(document).ready(function () { + if (!($('.sort')[0].checked)) + sortDataset(function (a, b) { return b.prob - a.prob; }); + }); + redrawBars(function (a, b) { return original_root.topics[b] - original_root.topics[a]; }); +} +function topicSort(topic) { + // Copy-on-write since tweens are evaluated after a delay. + $('.sort').removeAttr('checked'); + if (topic) { + sortDataset(function (a, b) { return b.topics[topic] - a.topics[topic]; }); + $('.selected').removeClass('selected'); + $(".top_" + topic).addClass('selected'); + $('.reset').removeAttr('disabled'); + if (topic == roottopic) { + $('.topdoc').css('font-weight', 'bold'); + $('.topdoc').removeClass('btn-primary'); + $('.topdoc').addClass('btn-default'); } else { - $('.selected').removeClass('selected'); - sortDataset(function(a, b) { return b.prob - a.prob; }); + $('.topdoc').css('font-weight', 'normal'); + $('.topdoc').removeClass('btn-default'); + $('.topdoc').addClass('btn-primary'); } + $('.topdoc').removeAttr('disabled'); + $('.topdoc').text('Retrieve Documents for Topic ' + topic); + $('.topdoc').click(function () { location.href = location.origin + location.pathname + '?topic=' + topic; }); + $('.topdoc').mouseenter(function () { + $('.legend rect').not('.top_' + topic).tooltip('hide'); + $(".legend rect.top_" + topic).tooltip('show'); + }); + $('.topdoc').mouseleave(function () { $(".top_" + topic).tooltip('hide'); }); - - var sortFn = function(a,b) { - if (a == topic) return -1; - else if (b == topic) return 1; - else return dataset[0].topics[b] - dataset[0].topics[a]; - //else return original_root.topics[b] - original_root.topics[a]; - } - redrawBars(sortFn); + } else { + $('.selected').removeClass('selected'); + sortDataset(function (a, b) { return b.prob - a.prob; }); } - function redrawBars(sortFn) { - $("#legend .hover").removeClass("hover"); - var numTopics = dataset[0].topics.length; - var delay = function(d, i) { return i * (1000/numTopics); }, - negdelay = function(d, i) { return (numTopics-i) * (1000/numTopics); }; - calculateTopicMap(dataset, !($('.scale')[0].checked), sortFn); - - svg.selectAll(".doc") - .selectAll("rect") - .data(function(d) { return d.topicMap; }) - .style("fill", function(d) { return barColors(colors[k][d.name], d.name, svg); }) - /* - .on("mouseover", function(d) { - // SVG element z-index determined by render order, not style sheet - // so element must be reappended to the end on hover so border - // is not occluded - var parent = $(this).parent(); - $(this).detach().appendTo(parent); - $(".docLabel", parent).detach().appendTo(parent); - $('.legend rect').not('.top_' + d.name).tooltip('hide'); - $(".top_" + d.name).addClass('hover'); - $('.legend rect.top_' + d.name).tooltip('show'); - }) - .on("mouseout", function(d) { - $(".top_" + d.name).removeClass('hover'); - })*/ - .transition().duration(1000).ease("linear").delay(this.checked ? delay : negdelay) - .attr("x", function(d) { return x(d.x0); }) - .attr("width", function(d) { return x(d.x1) - x(d.x0); }) - .attr("class", function(d) { return "top_" + d.name; }); + var sortFn = function (a, b) { + if (a == topic) return -1; + else if (b == topic) return 1; + else return dataset[0].topics[b] - dataset[0].topics[a]; + //else return original_root.topics[b] - original_root.topics[a]; } + redrawBars(sortFn); +} - // From StackOverflow: https://stackoverflow.com/a/21648508 - function hexToRgbA(hex, a){ - var c; - if(/^#([A-Fa-f0-9]{3}){1,2}$/.test(hex)){ - c= hex.substring(1).split(''); - if(c.length== 3){ - c= [c[0], c[0], c[1], c[1], c[2], c[2]]; - } - c= '0x'+c.join(''); - return 'rgba('+[(c>>16)&255, (c>>8)&255, c&255].join(',')+',' + a + ')'; +function redrawBars(sortFn) { + $("#legend .hover").removeClass("hover"); + var numTopics = dataset[0].topics.length; + var delay = function (d, i) { return i * (1000 / numTopics); }, + negdelay = function (d, i) { return (numTopics - i) * (1000 / numTopics); }; + calculateTopicMap(dataset, !($('.scale')[0].checked), sortFn); + + svg.selectAll(".doc") + .selectAll("rect") + .data(function (d) { return d.topicMap; }) + .style("fill", function (d) { return barColors(colors[k][d.name], d.name, svg); }) + /* + .on("mouseover", function(d) { + // SVG element z-index determined by render order, not style sheet + // so element must be reappended to the end on hover so border + // is not occluded + var parent = $(this).parent(); + $(this).detach().appendTo(parent); + $(".docLabel", parent).detach().appendTo(parent); + $('.legend rect').not('.top_' + d.name).tooltip('hide'); + $(".top_" + d.name).addClass('hover'); + $('.legend rect.top_' + d.name).tooltip('show'); + }) + .on("mouseout", function(d) { + $(".top_" + d.name).removeClass('hover'); + })*/ + .transition().duration(1000).ease("linear").delay(this.checked ? delay : negdelay) + .attr("x", function (d) { return x(d.x0); }) + .attr("width", function (d) { return x(d.x1) - x(d.x0); }) + .attr("class", function (d) { return "top_" + d.name; }); + +} + +// From StackOverflow: https://stackoverflow.com/a/21648508 +function hexToRgbA(hex, a) { + var c; + if (/^#([A-Fa-f0-9]{3}){1,2}$/.test(hex)) { + c = hex.substring(1).split(''); + if (c.length == 3) { + c = [c[0], c[0], c[1], c[1], c[2], c[2]]; } - throw new Error('Bad Hex'); + c = '0x' + c.join(''); + return 'rgba(' + [(c >> 16) & 255, (c >> 8) & 255, c & 255].join(',') + ',' + a + ')'; } + throw new Error('Bad Hex'); +} - function barColors(myColor, myId, svg) { - var mainGradient = svg.append('linearGradient') - .attr('id', myId); - mainGradient.append('stop') - .attr('stop-color', myColor) - .attr('offset', '0'); - mainGradient.append('stop') - .attr('stop-color', hexToRgbA(myColor, .7)) - .attr('offset', '1'); - return "url(#" + myId + ")"; - } +function barColors(myColor, myId, svg) { + var mainGradient = svg.append('linearGradient') + .attr('id', myId); + mainGradient.append('stop') + .attr('stop-color', myColor) + .attr('offset', '0'); + mainGradient.append('stop') + .attr('stop-color', hexToRgbA(myColor, .7)) + .attr('offset', '1'); + return "url(#" + myId + ")"; +} fingerprint.host = '../'; $('#home-link').attr('href', '../'); $('#cluster-link').attr('href', '../topics'); -$('.topic-link').each(function(i,elt) { - var url = '../' + $(elt).attr('href'); - if(docid) url += '?doc=' + docid; - $(this).attr('href', url); - }); +$('.topic-link').each(function (i, elt) { + var url = '../' + $(elt).attr('href'); + if (docid) url += '?doc=' + docid; + $(this).attr('href', url); +}); $.fn.followTo = function (pos) { - var $this = this, - $window = $(window); - - $window.scroll(function (e) { - if ($window.scrollTop() > pos) { - $this.css({ - position: 'fixed', - top: 120 - }); - } else { - $this.css({ - position: 'absolute', - top:405 - }); - } - }); + var $this = this, + $window = $(window); + + $window.scroll(function (e) { + if ($window.scrollTop() > pos) { + $this.css({ + position: 'fixed', + top: 120 + }); + } else { + $this.css({ + position: 'absolute', + top: 405 + }); + } + }); }; $('#legend').followTo(285);