{ "version": 3, "sources": ["../../../node_modules/@rails/actioncable/src/adapters.js", "../../../node_modules/@rails/actioncable/src/logger.js", "../../../node_modules/@rails/actioncable/src/connection_monitor.js", "../../../node_modules/@rails/actioncable/src/internal.js", "../../../node_modules/@rails/actioncable/src/connection.js", "../../../node_modules/@rails/actioncable/src/subscription.js", "../../../node_modules/@rails/actioncable/src/subscription_guarantor.js", "../../../node_modules/@rails/actioncable/src/subscriptions.js", "../../../node_modules/@rails/actioncable/src/consumer.js", "../../../node_modules/@rails/actioncable/src/index.js", "../../../node_modules/rater-js/index.js", "../../../node_modules/simplelightbox/dist/simple-lightbox.modules.js", "../../../node_modules/@hotwired/turbo/dist/turbo.es2017-esm.js", "../../../node_modules/@hotwired/turbo-rails/app/javascript/turbo/cable.js", "../../../node_modules/@hotwired/turbo-rails/app/javascript/turbo/snakeize.js", "../../../node_modules/@hotwired/turbo-rails/app/javascript/turbo/cable_stream_source_element.js", "../../../node_modules/@hotwired/turbo-rails/app/javascript/turbo/fetch_requests.js", "../../../node_modules/@hotwired/turbo-rails/app/javascript/turbo/index.js", "../../../node_modules/@hotwired/stimulus/dist/stimulus.js", "../../javascript/controllers/application.js", "../../javascript/controllers/add_tax_controller.ts", "../../../node_modules/ts-deepmerge/esm/index.js", "../../../node_modules/@kurkle/color/dist/color.esm.js", "../../../node_modules/chart.js/src/helpers/helpers.core.ts", "../../../node_modules/chart.js/src/helpers/helpers.math.ts", "../../../node_modules/chart.js/src/helpers/helpers.collection.ts", "../../../node_modules/chart.js/src/helpers/helpers.extras.ts", "../../../node_modules/chart.js/src/helpers/helpers.easing.ts", "../../../node_modules/chart.js/src/helpers/helpers.color.ts", "../../../node_modules/chart.js/src/core/core.animations.defaults.js", "../../../node_modules/chart.js/src/core/core.layouts.defaults.js", "../../../node_modules/chart.js/src/helpers/helpers.intl.ts", "../../../node_modules/chart.js/src/core/core.ticks.js", "../../../node_modules/chart.js/src/core/core.scale.defaults.js", "../../../node_modules/chart.js/src/core/core.defaults.js", "../../../node_modules/chart.js/src/helpers/helpers.canvas.ts", "../../../node_modules/chart.js/src/helpers/helpers.options.ts", "../../../node_modules/chart.js/src/helpers/helpers.config.ts", "../../../node_modules/chart.js/src/helpers/helpers.curve.ts", "../../../node_modules/chart.js/src/helpers/helpers.dom.ts", "../../../node_modules/chart.js/src/helpers/helpers.interpolation.ts", "../../../node_modules/chart.js/src/helpers/helpers.rtl.ts", "../../../node_modules/chart.js/src/helpers/helpers.segment.js", "../../../node_modules/chart.js/src/core/core.animator.js", "../../../node_modules/chart.js/src/core/core.animation.js", "../../../node_modules/chart.js/src/core/core.animations.js", "../../../node_modules/chart.js/src/core/core.datasetController.js", "../../../node_modules/chart.js/src/controllers/controller.bar.js", "../../../node_modules/chart.js/src/controllers/controller.bubble.js", "../../../node_modules/chart.js/src/controllers/controller.doughnut.js", "../../../node_modules/chart.js/src/controllers/controller.line.js", "../../../node_modules/chart.js/src/controllers/controller.polarArea.js", "../../../node_modules/chart.js/src/controllers/controller.pie.js", "../../../node_modules/chart.js/src/controllers/controller.radar.js", "../../../node_modules/chart.js/src/controllers/controller.scatter.js", "../../../node_modules/chart.js/src/core/core.adapters.ts", "../../../node_modules/chart.js/src/core/core.interaction.js", "../../../node_modules/chart.js/src/core/core.layouts.js", "../../../node_modules/chart.js/src/platform/platform.base.js", "../../../node_modules/chart.js/src/platform/platform.basic.js", "../../../node_modules/chart.js/src/platform/platform.dom.js", "../../../node_modules/chart.js/src/platform/index.js", "../../../node_modules/chart.js/src/core/core.element.ts", "../../../node_modules/chart.js/src/core/core.scale.autoskip.js", "../../../node_modules/chart.js/src/core/core.scale.js", "../../../node_modules/chart.js/src/core/core.typedRegistry.js", "../../../node_modules/chart.js/src/core/core.registry.js", "../../../node_modules/chart.js/src/core/core.plugins.js", "../../../node_modules/chart.js/src/core/core.config.js", "../../../node_modules/chart.js/src/core/core.controller.js", "../../../node_modules/chart.js/src/elements/element.arc.ts", "../../../node_modules/chart.js/src/elements/element.line.js", "../../../node_modules/chart.js/src/elements/element.point.ts", "../../../node_modules/chart.js/src/elements/element.bar.js", "../../../node_modules/chart.js/src/plugins/plugin.colors.ts", "../../../node_modules/chart.js/src/plugins/plugin.decimation.js", "../../../node_modules/chart.js/src/plugins/plugin.filler/filler.segment.js", "../../../node_modules/chart.js/src/plugins/plugin.filler/filler.helper.js", "../../../node_modules/chart.js/src/plugins/plugin.filler/filler.options.js", "../../../node_modules/chart.js/src/plugins/plugin.filler/filler.target.stack.js", "../../../node_modules/chart.js/src/plugins/plugin.filler/simpleArc.js", "../../../node_modules/chart.js/src/plugins/plugin.filler/filler.target.js", "../../../node_modules/chart.js/src/plugins/plugin.filler/filler.drawing.js", "../../../node_modules/chart.js/src/plugins/plugin.filler/index.js", "../../../node_modules/chart.js/src/plugins/plugin.legend.js", "../../../node_modules/chart.js/src/plugins/plugin.title.js", "../../../node_modules/chart.js/src/plugins/plugin.subtitle.js", "../../../node_modules/chart.js/src/plugins/plugin.tooltip.js", "../../../node_modules/chart.js/src/scales/scale.category.js", "../../../node_modules/chart.js/src/scales/scale.linearbase.js", "../../../node_modules/chart.js/src/scales/scale.linear.js", "../../../node_modules/chart.js/src/scales/scale.logarithmic.js", "../../../node_modules/chart.js/src/scales/scale.radialLinear.js", "../../../node_modules/chart.js/src/scales/scale.time.js", "../../../node_modules/chart.js/src/scales/scale.timeseries.js", "../../../node_modules/chart.js/src/index.ts", "../../javascript/taste_graph/taste_graph.ts", "../../javascript/controllers/input_taste_graph_controller.ts", "../../javascript/controllers/rating_controller.ts", "../../javascript/controllers/sake_kura_controller.ts", "../../../node_modules/just-zip-it/index.mjs", "../../javascript/completion/meigara_dict.ts", "../../javascript/completion/meigara.ts", "../../javascript/completion/detail_dict.ts", "../../javascript/completion/detail.ts", "../../javascript/controllers/sake_name_controller.ts", "../../javascript/controllers/sake_size_controller.ts", "../../javascript/controllers/share_controller.ts", "../../javascript/controllers/show_taste_graph_controller.ts", "../../javascript/controllers/simple_lightbox_controller.ts", "../../javascript/controllers/index.js", "../../../node_modules/@popperjs/core/lib/index.js", "../../../node_modules/@popperjs/core/lib/enums.js", "../../../node_modules/@popperjs/core/lib/dom-utils/getNodeName.js", "../../../node_modules/@popperjs/core/lib/dom-utils/getWindow.js", "../../../node_modules/@popperjs/core/lib/dom-utils/instanceOf.js", "../../../node_modules/@popperjs/core/lib/modifiers/applyStyles.js", "../../../node_modules/@popperjs/core/lib/utils/getBasePlacement.js", "../../../node_modules/@popperjs/core/lib/utils/math.js", "../../../node_modules/@popperjs/core/lib/utils/userAgent.js", "../../../node_modules/@popperjs/core/lib/dom-utils/isLayoutViewport.js", "../../../node_modules/@popperjs/core/lib/dom-utils/getBoundingClientRect.js", "../../../node_modules/@popperjs/core/lib/dom-utils/getLayoutRect.js", "../../../node_modules/@popperjs/core/lib/dom-utils/contains.js", "../../../node_modules/@popperjs/core/lib/dom-utils/getComputedStyle.js", "../../../node_modules/@popperjs/core/lib/dom-utils/isTableElement.js", "../../../node_modules/@popperjs/core/lib/dom-utils/getDocumentElement.js", "../../../node_modules/@popperjs/core/lib/dom-utils/getParentNode.js", "../../../node_modules/@popperjs/core/lib/dom-utils/getOffsetParent.js", "../../../node_modules/@popperjs/core/lib/utils/getMainAxisFromPlacement.js", "../../../node_modules/@popperjs/core/lib/utils/within.js", "../../../node_modules/@popperjs/core/lib/utils/getFreshSideObject.js", "../../../node_modules/@popperjs/core/lib/utils/mergePaddingObject.js", "../../../node_modules/@popperjs/core/lib/utils/expandToHashMap.js", "../../../node_modules/@popperjs/core/lib/modifiers/arrow.js", "../../../node_modules/@popperjs/core/lib/utils/getVariation.js", "../../../node_modules/@popperjs/core/lib/modifiers/computeStyles.js", "../../../node_modules/@popperjs/core/lib/modifiers/eventListeners.js", "../../../node_modules/@popperjs/core/lib/utils/getOppositePlacement.js", "../../../node_modules/@popperjs/core/lib/utils/getOppositeVariationPlacement.js", "../../../node_modules/@popperjs/core/lib/dom-utils/getWindowScroll.js", "../../../node_modules/@popperjs/core/lib/dom-utils/getWindowScrollBarX.js", "../../../node_modules/@popperjs/core/lib/dom-utils/getViewportRect.js", "../../../node_modules/@popperjs/core/lib/dom-utils/getDocumentRect.js", "../../../node_modules/@popperjs/core/lib/dom-utils/isScrollParent.js", "../../../node_modules/@popperjs/core/lib/dom-utils/getScrollParent.js", "../../../node_modules/@popperjs/core/lib/dom-utils/listScrollParents.js", "../../../node_modules/@popperjs/core/lib/utils/rectToClientRect.js", "../../../node_modules/@popperjs/core/lib/dom-utils/getClippingRect.js", "../../../node_modules/@popperjs/core/lib/utils/computeOffsets.js", "../../../node_modules/@popperjs/core/lib/utils/detectOverflow.js", "../../../node_modules/@popperjs/core/lib/utils/computeAutoPlacement.js", "../../../node_modules/@popperjs/core/lib/modifiers/flip.js", "../../../node_modules/@popperjs/core/lib/modifiers/hide.js", "../../../node_modules/@popperjs/core/lib/modifiers/offset.js", "../../../node_modules/@popperjs/core/lib/modifiers/popperOffsets.js", "../../../node_modules/@popperjs/core/lib/utils/getAltAxis.js", "../../../node_modules/@popperjs/core/lib/modifiers/preventOverflow.js", "../../../node_modules/@popperjs/core/lib/dom-utils/getHTMLElementScroll.js", "../../../node_modules/@popperjs/core/lib/dom-utils/getNodeScroll.js", "../../../node_modules/@popperjs/core/lib/dom-utils/getCompositeRect.js", "../../../node_modules/@popperjs/core/lib/utils/orderModifiers.js", "../../../node_modules/@popperjs/core/lib/utils/debounce.js", "../../../node_modules/@popperjs/core/lib/utils/mergeByName.js", "../../../node_modules/@popperjs/core/lib/createPopper.js", "../../../node_modules/@popperjs/core/lib/popper-lite.js", "../../../node_modules/@popperjs/core/lib/popper.js", "../../../node_modules/bootstrap/js/src/dom/data.js", "../../../node_modules/bootstrap/js/src/util/index.js", "../../../node_modules/bootstrap/js/src/dom/event-handler.js", "../../../node_modules/bootstrap/js/src/dom/manipulator.js", "../../../node_modules/bootstrap/js/src/util/config.js", "../../../node_modules/bootstrap/js/src/base-component.js", "../../../node_modules/bootstrap/js/src/dom/selector-engine.js", "../../../node_modules/bootstrap/js/src/util/component-functions.js", "../../../node_modules/bootstrap/js/src/alert.js", "../../../node_modules/bootstrap/js/src/button.js", "../../../node_modules/bootstrap/js/src/util/swipe.js", "../../../node_modules/bootstrap/js/src/carousel.js", "../../../node_modules/bootstrap/js/src/collapse.js", "../../../node_modules/bootstrap/js/src/dropdown.js", "../../../node_modules/bootstrap/js/src/util/backdrop.js", "../../../node_modules/bootstrap/js/src/util/focustrap.js", "../../../node_modules/bootstrap/js/src/util/scrollbar.js", "../../../node_modules/bootstrap/js/src/modal.js", "../../../node_modules/bootstrap/js/src/offcanvas.js", "../../../node_modules/bootstrap/js/src/util/sanitizer.js", "../../../node_modules/bootstrap/js/src/util/template-factory.js", "../../../node_modules/bootstrap/js/src/tooltip.js", "../../../node_modules/bootstrap/js/src/popover.js", "../../../node_modules/bootstrap/js/src/scrollspy.js", "../../../node_modules/bootstrap/js/src/tab.js", "../../../node_modules/bootstrap/js/src/toast.js"], "sourcesContent": ["export default {\n logger: self.console,\n WebSocket: self.WebSocket\n}\n", "import adapters from \"./adapters\"\n\n// The logger is disabled by default. You can enable it with:\n//\n// ActionCable.logger.enabled = true\n//\n// Example:\n//\n// import * as ActionCable from '@rails/actioncable'\n//\n// ActionCable.logger.enabled = true\n// ActionCable.logger.log('Connection Established.')\n//\n\nexport default {\n log(...messages) {\n if (this.enabled) {\n messages.push(Date.now())\n adapters.logger.log(\"[ActionCable]\", ...messages)\n }\n },\n}\n", "import logger from \"./logger\"\n\n// Responsible for ensuring the cable connection is in good health by validating the heartbeat pings sent from the server, and attempting\n// revival reconnections if things go astray. Internal class, not intended for direct user manipulation.\n\nconst now = () => new Date().getTime()\n\nconst secondsSince = time => (now() - time) / 1000\n\nclass ConnectionMonitor {\n constructor(connection) {\n this.visibilityDidChange = this.visibilityDidChange.bind(this)\n this.connection = connection\n this.reconnectAttempts = 0\n }\n\n start() {\n if (!this.isRunning()) {\n this.startedAt = now()\n delete this.stoppedAt\n this.startPolling()\n addEventListener(\"visibilitychange\", this.visibilityDidChange)\n logger.log(`ConnectionMonitor started. stale threshold = ${this.constructor.staleThreshold} s`)\n }\n }\n\n stop() {\n if (this.isRunning()) {\n this.stoppedAt = now()\n this.stopPolling()\n removeEventListener(\"visibilitychange\", this.visibilityDidChange)\n logger.log(\"ConnectionMonitor stopped\")\n }\n }\n\n isRunning() {\n return this.startedAt && !this.stoppedAt\n }\n\n recordPing() {\n this.pingedAt = now()\n }\n\n recordConnect() {\n this.reconnectAttempts = 0\n this.recordPing()\n delete this.disconnectedAt\n logger.log(\"ConnectionMonitor recorded connect\")\n }\n\n recordDisconnect() {\n this.disconnectedAt = now()\n logger.log(\"ConnectionMonitor recorded disconnect\")\n }\n\n // Private\n\n startPolling() {\n this.stopPolling()\n this.poll()\n }\n\n stopPolling() {\n clearTimeout(this.pollTimeout)\n }\n\n poll() {\n this.pollTimeout = setTimeout(() => {\n this.reconnectIfStale()\n this.poll()\n }\n , this.getPollInterval())\n }\n\n getPollInterval() {\n const { staleThreshold, reconnectionBackoffRate } = this.constructor\n const backoff = Math.pow(1 + reconnectionBackoffRate, Math.min(this.reconnectAttempts, 10))\n const jitterMax = this.reconnectAttempts === 0 ? 1.0 : reconnectionBackoffRate\n const jitter = jitterMax * Math.random()\n return staleThreshold * 1000 * backoff * (1 + jitter)\n }\n\n reconnectIfStale() {\n if (this.connectionIsStale()) {\n logger.log(`ConnectionMonitor detected stale connection. reconnectAttempts = ${this.reconnectAttempts}, time stale = ${secondsSince(this.refreshedAt)} s, stale threshold = ${this.constructor.staleThreshold} s`)\n this.reconnectAttempts++\n if (this.disconnectedRecently()) {\n logger.log(`ConnectionMonitor skipping reopening recent disconnect. time disconnected = ${secondsSince(this.disconnectedAt)} s`)\n } else {\n logger.log(\"ConnectionMonitor reopening\")\n this.connection.reopen()\n }\n }\n }\n\n get refreshedAt() {\n return this.pingedAt ? this.pingedAt : this.startedAt\n }\n\n connectionIsStale() {\n return secondsSince(this.refreshedAt) > this.constructor.staleThreshold\n }\n\n disconnectedRecently() {\n return this.disconnectedAt && (secondsSince(this.disconnectedAt) < this.constructor.staleThreshold)\n }\n\n visibilityDidChange() {\n if (document.visibilityState === \"visible\") {\n setTimeout(() => {\n if (this.connectionIsStale() || !this.connection.isOpen()) {\n logger.log(`ConnectionMonitor reopening stale connection on visibilitychange. visibilityState = ${document.visibilityState}`)\n this.connection.reopen()\n }\n }\n , 200)\n }\n }\n\n}\n\nConnectionMonitor.staleThreshold = 6 // Server::Connections::BEAT_INTERVAL * 2 (missed two pings)\nConnectionMonitor.reconnectionBackoffRate = 0.15\n\nexport default ConnectionMonitor\n", "export default {\n \"message_types\": {\n \"welcome\": \"welcome\",\n \"disconnect\": \"disconnect\",\n \"ping\": \"ping\",\n \"confirmation\": \"confirm_subscription\",\n \"rejection\": \"reject_subscription\"\n },\n \"disconnect_reasons\": {\n \"unauthorized\": \"unauthorized\",\n \"invalid_request\": \"invalid_request\",\n \"server_restart\": \"server_restart\"\n },\n \"default_mount_path\": \"/cable\",\n \"protocols\": [\n \"actioncable-v1-json\",\n \"actioncable-unsupported\"\n ]\n}\n", "import adapters from \"./adapters\"\nimport ConnectionMonitor from \"./connection_monitor\"\nimport INTERNAL from \"./internal\"\nimport logger from \"./logger\"\n\n// Encapsulate the cable connection held by the consumer. This is an internal class not intended for direct user manipulation.\n\nconst {message_types, protocols} = INTERNAL\nconst supportedProtocols = protocols.slice(0, protocols.length - 1)\n\nconst indexOf = [].indexOf\n\nclass Connection {\n constructor(consumer) {\n this.open = this.open.bind(this)\n this.consumer = consumer\n this.subscriptions = this.consumer.subscriptions\n this.monitor = new ConnectionMonitor(this)\n this.disconnected = true\n }\n\n send(data) {\n if (this.isOpen()) {\n this.webSocket.send(JSON.stringify(data))\n return true\n } else {\n return false\n }\n }\n\n open() {\n if (this.isActive()) {\n logger.log(`Attempted to open WebSocket, but existing socket is ${this.getState()}`)\n return false\n } else {\n logger.log(`Opening WebSocket, current state is ${this.getState()}, subprotocols: ${protocols}`)\n if (this.webSocket) { this.uninstallEventHandlers() }\n this.webSocket = new adapters.WebSocket(this.consumer.url, protocols)\n this.installEventHandlers()\n this.monitor.start()\n return true\n }\n }\n\n close({allowReconnect} = {allowReconnect: true}) {\n if (!allowReconnect) { this.monitor.stop() }\n // Avoid closing websockets in a \"connecting\" state due to Safari 15.1+ bug. See: https://github.com/rails/rails/issues/43835#issuecomment-1002288478\n if (this.isOpen()) {\n return this.webSocket.close()\n }\n }\n\n reopen() {\n logger.log(`Reopening WebSocket, current state is ${this.getState()}`)\n if (this.isActive()) {\n try {\n return this.close()\n } catch (error) {\n logger.log(\"Failed to reopen WebSocket\", error)\n }\n finally {\n logger.log(`Reopening WebSocket in ${this.constructor.reopenDelay}ms`)\n setTimeout(this.open, this.constructor.reopenDelay)\n }\n } else {\n return this.open()\n }\n }\n\n getProtocol() {\n if (this.webSocket) {\n return this.webSocket.protocol\n }\n }\n\n isOpen() {\n return this.isState(\"open\")\n }\n\n isActive() {\n return this.isState(\"open\", \"connecting\")\n }\n\n // Private\n\n isProtocolSupported() {\n return indexOf.call(supportedProtocols, this.getProtocol()) >= 0\n }\n\n isState(...states) {\n return indexOf.call(states, this.getState()) >= 0\n }\n\n getState() {\n if (this.webSocket) {\n for (let state in adapters.WebSocket) {\n if (adapters.WebSocket[state] === this.webSocket.readyState) {\n return state.toLowerCase()\n }\n }\n }\n return null\n }\n\n installEventHandlers() {\n for (let eventName in this.events) {\n const handler = this.events[eventName].bind(this)\n this.webSocket[`on${eventName}`] = handler\n }\n }\n\n uninstallEventHandlers() {\n for (let eventName in this.events) {\n this.webSocket[`on${eventName}`] = function() {}\n }\n }\n\n}\n\nConnection.reopenDelay = 500\n\nConnection.prototype.events = {\n message(event) {\n if (!this.isProtocolSupported()) { return }\n const {identifier, message, reason, reconnect, type} = JSON.parse(event.data)\n switch (type) {\n case message_types.welcome:\n this.monitor.recordConnect()\n return this.subscriptions.reload()\n case message_types.disconnect:\n logger.log(`Disconnecting. Reason: ${reason}`)\n return this.close({allowReconnect: reconnect})\n case message_types.ping:\n return this.monitor.recordPing()\n case message_types.confirmation:\n this.subscriptions.confirmSubscription(identifier)\n return this.subscriptions.notify(identifier, \"connected\")\n case message_types.rejection:\n return this.subscriptions.reject(identifier)\n default:\n return this.subscriptions.notify(identifier, \"received\", message)\n }\n },\n\n open() {\n logger.log(`WebSocket onopen event, using '${this.getProtocol()}' subprotocol`)\n this.disconnected = false\n if (!this.isProtocolSupported()) {\n logger.log(\"Protocol is unsupported. Stopping monitor and disconnecting.\")\n return this.close({allowReconnect: false})\n }\n },\n\n close(event) {\n logger.log(\"WebSocket onclose event\")\n if (this.disconnected) { return }\n this.disconnected = true\n this.monitor.recordDisconnect()\n return this.subscriptions.notifyAll(\"disconnected\", {willAttemptReconnect: this.monitor.isRunning()})\n },\n\n error() {\n logger.log(\"WebSocket onerror event\")\n }\n}\n\nexport default Connection\n", "// A new subscription is created through the ActionCable.Subscriptions instance available on the consumer.\n// It provides a number of callbacks and a method for calling remote procedure calls on the corresponding\n// Channel instance on the server side.\n//\n// An example demonstrates the basic functionality:\n//\n// App.appearance = App.cable.subscriptions.create(\"AppearanceChannel\", {\n// connected() {\n// // Called once the subscription has been successfully completed\n// },\n//\n// disconnected({ willAttemptReconnect: boolean }) {\n// // Called when the client has disconnected with the server.\n// // The object will have an `willAttemptReconnect` property which\n// // says whether the client has the intention of attempting\n// // to reconnect.\n// },\n//\n// appear() {\n// this.perform('appear', {appearing_on: this.appearingOn()})\n// },\n//\n// away() {\n// this.perform('away')\n// },\n//\n// appearingOn() {\n// $('main').data('appearing-on')\n// }\n// })\n//\n// The methods #appear and #away forward their intent to the remote AppearanceChannel instance on the server\n// by calling the `perform` method with the first parameter being the action (which maps to AppearanceChannel#appear/away).\n// The second parameter is a hash that'll get JSON encoded and made available on the server in the data parameter.\n//\n// This is how the server component would look:\n//\n// class AppearanceChannel < ApplicationActionCable::Channel\n// def subscribed\n// current_user.appear\n// end\n//\n// def unsubscribed\n// current_user.disappear\n// end\n//\n// def appear(data)\n// current_user.appear on: data['appearing_on']\n// end\n//\n// def away\n// current_user.away\n// end\n// end\n//\n// The \"AppearanceChannel\" name is automatically mapped between the client-side subscription creation and the server-side Ruby class name.\n// The AppearanceChannel#appear/away public methods are exposed automatically to client-side invocation through the perform method.\n\nconst extend = function(object, properties) {\n if (properties != null) {\n for (let key in properties) {\n const value = properties[key]\n object[key] = value\n }\n }\n return object\n}\n\nexport default class Subscription {\n constructor(consumer, params = {}, mixin) {\n this.consumer = consumer\n this.identifier = JSON.stringify(params)\n extend(this, mixin)\n }\n\n // Perform a channel action with the optional data passed as an attribute\n perform(action, data = {}) {\n data.action = action\n return this.send(data)\n }\n\n send(data) {\n return this.consumer.send({command: \"message\", identifier: this.identifier, data: JSON.stringify(data)})\n }\n\n unsubscribe() {\n return this.consumer.subscriptions.remove(this)\n }\n}\n", "import logger from \"./logger\"\n\n// Responsible for ensuring channel subscribe command is confirmed, retrying until confirmation is received.\n// Internal class, not intended for direct user manipulation.\n\nclass SubscriptionGuarantor {\n constructor(subscriptions) {\n this.subscriptions = subscriptions\n this.pendingSubscriptions = []\n }\n\n guarantee(subscription) {\n if(this.pendingSubscriptions.indexOf(subscription) == -1){ \n logger.log(`SubscriptionGuarantor guaranteeing ${subscription.identifier}`)\n this.pendingSubscriptions.push(subscription) \n }\n else {\n logger.log(`SubscriptionGuarantor already guaranteeing ${subscription.identifier}`)\n }\n this.startGuaranteeing()\n }\n\n forget(subscription) {\n logger.log(`SubscriptionGuarantor forgetting ${subscription.identifier}`)\n this.pendingSubscriptions = (this.pendingSubscriptions.filter((s) => s !== subscription))\n }\n\n startGuaranteeing() {\n this.stopGuaranteeing()\n this.retrySubscribing()\n }\n \n stopGuaranteeing() {\n clearTimeout(this.retryTimeout)\n }\n\n retrySubscribing() {\n this.retryTimeout = setTimeout(() => {\n if (this.subscriptions && typeof(this.subscriptions.subscribe) === \"function\") {\n this.pendingSubscriptions.map((subscription) => {\n logger.log(`SubscriptionGuarantor resubscribing ${subscription.identifier}`)\n this.subscriptions.subscribe(subscription)\n })\n }\n }\n , 500)\n }\n}\n\nexport default SubscriptionGuarantor", "import Subscription from \"./subscription\"\nimport SubscriptionGuarantor from \"./subscription_guarantor\"\nimport logger from \"./logger\"\n\n// Collection class for creating (and internally managing) channel subscriptions.\n// The only method intended to be triggered by the user is ActionCable.Subscriptions#create,\n// and it should be called through the consumer like so:\n//\n// App = {}\n// App.cable = ActionCable.createConsumer(\"ws://example.com/accounts/1\")\n// App.appearance = App.cable.subscriptions.create(\"AppearanceChannel\")\n//\n// For more details on how you'd configure an actual channel subscription, see ActionCable.Subscription.\n\nexport default class Subscriptions {\n constructor(consumer) {\n this.consumer = consumer\n this.guarantor = new SubscriptionGuarantor(this)\n this.subscriptions = []\n }\n\n create(channelName, mixin) {\n const channel = channelName\n const params = typeof channel === \"object\" ? channel : {channel}\n const subscription = new Subscription(this.consumer, params, mixin)\n return this.add(subscription)\n }\n\n // Private\n\n add(subscription) {\n this.subscriptions.push(subscription)\n this.consumer.ensureActiveConnection()\n this.notify(subscription, \"initialized\")\n this.subscribe(subscription)\n return subscription\n }\n\n remove(subscription) {\n this.forget(subscription)\n if (!this.findAll(subscription.identifier).length) {\n this.sendCommand(subscription, \"unsubscribe\")\n }\n return subscription\n }\n\n reject(identifier) {\n return this.findAll(identifier).map((subscription) => {\n this.forget(subscription)\n this.notify(subscription, \"rejected\")\n return subscription\n })\n }\n\n forget(subscription) {\n this.guarantor.forget(subscription)\n this.subscriptions = (this.subscriptions.filter((s) => s !== subscription))\n return subscription\n }\n\n findAll(identifier) {\n return this.subscriptions.filter((s) => s.identifier === identifier)\n }\n\n reload() {\n return this.subscriptions.map((subscription) =>\n this.subscribe(subscription))\n }\n\n notifyAll(callbackName, ...args) {\n return this.subscriptions.map((subscription) =>\n this.notify(subscription, callbackName, ...args))\n }\n\n notify(subscription, callbackName, ...args) {\n let subscriptions\n if (typeof subscription === \"string\") {\n subscriptions = this.findAll(subscription)\n } else {\n subscriptions = [subscription]\n }\n\n return subscriptions.map((subscription) =>\n (typeof subscription[callbackName] === \"function\" ? subscription[callbackName](...args) : undefined))\n }\n\n subscribe(subscription) {\n if (this.sendCommand(subscription, \"subscribe\")) {\n this.guarantor.guarantee(subscription)\n }\n }\n\n confirmSubscription(identifier) {\n logger.log(`Subscription confirmed ${identifier}`)\n this.findAll(identifier).map((subscription) =>\n this.guarantor.forget(subscription))\n }\n\n sendCommand(subscription, command) {\n const {identifier} = subscription\n return this.consumer.send({command, identifier})\n }\n}\n", "import Connection from \"./connection\"\nimport Subscriptions from \"./subscriptions\"\n\n// The ActionCable.Consumer establishes the connection to a server-side Ruby Connection object. Once established,\n// the ActionCable.ConnectionMonitor will ensure that its properly maintained through heartbeats and checking for stale updates.\n// The Consumer instance is also the gateway to establishing subscriptions to desired channels through the #createSubscription\n// method.\n//\n// The following example shows how this can be set up:\n//\n// App = {}\n// App.cable = ActionCable.createConsumer(\"ws://example.com/accounts/1\")\n// App.appearance = App.cable.subscriptions.create(\"AppearanceChannel\")\n//\n// For more details on how you'd configure an actual channel subscription, see ActionCable.Subscription.\n//\n// When a consumer is created, it automatically connects with the server.\n//\n// To disconnect from the server, call\n//\n// App.cable.disconnect()\n//\n// and to restart the connection:\n//\n// App.cable.connect()\n//\n// Any channel subscriptions which existed prior to disconnecting will\n// automatically resubscribe.\n\nexport default class Consumer {\n constructor(url) {\n this._url = url\n this.subscriptions = new Subscriptions(this)\n this.connection = new Connection(this)\n }\n\n get url() {\n return createWebSocketURL(this._url)\n }\n\n send(data) {\n return this.connection.send(data)\n }\n\n connect() {\n return this.connection.open()\n }\n\n disconnect() {\n return this.connection.close({allowReconnect: false})\n }\n\n ensureActiveConnection() {\n if (!this.connection.isActive()) {\n return this.connection.open()\n }\n }\n}\n\nexport function createWebSocketURL(url) {\n if (typeof url === \"function\") {\n url = url()\n }\n\n if (url && !/^wss?:/i.test(url)) {\n const a = document.createElement(\"a\")\n a.href = url\n // Fix populating Location properties in IE. Otherwise, protocol will be blank.\n a.href = a.href\n a.protocol = a.protocol.replace(\"http\", \"ws\")\n return a.href\n } else {\n return url\n }\n}\n", "import Connection from \"./connection\"\nimport ConnectionMonitor from \"./connection_monitor\"\nimport Consumer, { createWebSocketURL } from \"./consumer\"\nimport INTERNAL from \"./internal\"\nimport Subscription from \"./subscription\"\nimport Subscriptions from \"./subscriptions\"\nimport SubscriptionGuarantor from \"./subscription_guarantor\"\nimport adapters from \"./adapters\"\nimport logger from \"./logger\"\n\nexport {\n Connection,\n ConnectionMonitor,\n Consumer,\n INTERNAL,\n Subscription,\n Subscriptions,\n SubscriptionGuarantor,\n adapters,\n createWebSocketURL,\n logger,\n}\n\nexport function createConsumer(url = getConfig(\"url\") || INTERNAL.default_mount_path) {\n return new Consumer(url)\n}\n\nexport function getConfig(name) {\n const element = document.head.querySelector(`meta[name='action-cable-${name}']`)\n if (element) {\n return element.getAttribute(\"content\")\n }\n}\n", "(function(f){if(typeof exports===\"object\"&&typeof module!==\"undefined\"){module.exports=f()}else if(typeof define===\"function\"&&define.amd){define([],f)}else{var g;if(typeof window!==\"undefined\"){g=window}else if(typeof global!==\"undefined\"){g=global}else if(typeof self!==\"undefined\"){g=self}else{g=this}g.raterJs = f()}})(function(){var define,module,exports;return (function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c=\"function\"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error(\"Cannot find module '\"+i+\"'\");throw a.code=\"MODULE_NOT_FOUND\",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u=\"function\"==typeof require&&require,i=0;i 1) {\r\n throw new Error(\"step must be a number between 0 and 1\");\r\n }\r\n }\r\n\r\n var elem = options.element;\r\n var reverse = options.reverse;\r\n var stars = options.max || 5;\r\n var starSize = options.starSize || 16;\r\n var step = options.step || 1;\r\n var onHover = options.onHover;\r\n var onLeave = options.onLeave;\r\n var rating = null;\r\n var myRating;\r\n elem.classList.add(\"star-rating\");\r\n var div = document.createElement(\"div\");\r\n div.classList.add(\"star-value\");\r\n\r\n if (reverse) {\r\n div.classList.add(\"rtl\");\r\n }\r\n\r\n div.style.backgroundSize = starSize + \"px\";\r\n elem.appendChild(div);\r\n elem.style.width = starSize * stars + \"px\";\r\n elem.style.height = starSize + \"px\";\r\n elem.style.backgroundSize = starSize + \"px\";\r\n var callback = options.rateCallback;\r\n var disabled = !!options.readOnly;\r\n var disableText;\r\n var isRating = false;\r\n var isBusyText = options.isBusyText;\r\n var currentRating;\r\n var ratingText;\r\n\r\n if (typeof options.disableText !== \"undefined\") {\r\n disableText = options.disableText;\r\n } else {\r\n disableText = \"{rating}/{maxRating}\";\r\n }\r\n\r\n if (typeof options.ratingText !== \"undefined\") {\r\n ratingText = options.ratingText;\r\n } else {\r\n ratingText = \"{rating}/{maxRating}\";\r\n }\r\n\r\n if (options.rating) {\r\n setRating(options.rating);\r\n } else {\r\n var dataRating = elem.dataset.rating;\r\n\r\n if (dataRating) {\r\n setRating(+dataRating);\r\n }\r\n }\r\n\r\n if (!rating) {\r\n elem.querySelector(\".star-value\").style.width = \"0px\";\r\n }\r\n\r\n if (disabled) {\r\n disable();\r\n } //private methods\r\n\r\n\r\n function onMouseMove(e) {\r\n onMove(e, false);\r\n }\r\n /**\r\n * Called by eventhandlers when mouse or touch events are triggered\r\n * @param {MouseEvent} e\r\n */\r\n\r\n\r\n function onMove(e, isTouch) {\r\n if (disabled === true || isRating === true) {\r\n return;\r\n }\r\n\r\n var xCoor = null;\r\n var percent;\r\n var width = elem.offsetWidth;\r\n var parentOffset = elem.getBoundingClientRect();\r\n\r\n if (reverse) {\r\n if (isTouch) {\r\n xCoor = e.changedTouches[0].pageX - parentOffset.left;\r\n } else {\r\n xCoor = e.pageX - window.scrollX - parentOffset.left;\r\n }\r\n\r\n var relXRtl = width - xCoor;\r\n var valueForDivision = width / 100;\r\n percent = relXRtl / valueForDivision;\r\n } else {\r\n if (isTouch) {\r\n xCoor = e.changedTouches[0].pageX - parentOffset.left;\r\n } else {\r\n xCoor = e.offsetX;\r\n }\r\n\r\n percent = xCoor / width * 100;\r\n }\r\n\r\n if (percent < 101) {\r\n if (step === 1) {\r\n currentRating = Math.ceil(percent / 100 * stars);\r\n } else {\r\n var rat = percent / 100 * stars;\r\n\r\n for (var i = 0;; i += step) {\r\n if (i >= rat) {\r\n currentRating = i;\r\n break;\r\n }\r\n }\r\n } //todo: check why this happens and fix\r\n\r\n\r\n if (currentRating > stars) {\r\n currentRating = stars;\r\n }\r\n\r\n elem.querySelector(\".star-value\").style.width = currentRating / stars * 100 + \"%\";\r\n\r\n if (showToolTip) {\r\n var toolTip = ratingText.replace(\"{rating}\", currentRating);\r\n toolTip = toolTip.replace(\"{maxRating}\", stars);\r\n elem.setAttribute(\"title\", toolTip);\r\n }\r\n\r\n if (typeof onHover === \"function\") {\r\n onHover(currentRating, rating);\r\n }\r\n }\r\n }\r\n /**\r\n * Called when mouse is released. This function will update the view with the rating.\r\n * @param {MouseEvent} e\r\n */\r\n\r\n\r\n function onStarOut(e) {\r\n if (!rating) {\r\n elem.querySelector(\".star-value\").style.width = \"0%\";\r\n elem.removeAttribute(\"data-rating\");\r\n } else {\r\n elem.querySelector(\".star-value\").style.width = rating / stars * 100 + \"%\";\r\n elem.setAttribute(\"data-rating\", rating);\r\n }\r\n\r\n if (typeof onLeave === \"function\") {\r\n onLeave(currentRating, rating);\r\n }\r\n }\r\n /**\r\n * Called when star is clicked.\r\n * @param {MouseEvent} e\r\n */\r\n\r\n\r\n function onStarClick(e) {\r\n if (disabled === true) {\r\n return;\r\n }\r\n\r\n if (isRating === true) {\r\n return;\r\n }\r\n\r\n if (typeof callback !== \"undefined\") {\r\n isRating = true;\r\n myRating = currentRating;\r\n\r\n if (typeof isBusyText === \"undefined\") {\r\n elem.removeAttribute(\"title\");\r\n } else {\r\n elem.setAttribute(\"title\", isBusyText);\r\n }\r\n\r\n elem.classList.add(\"is-busy\");\r\n callback.call(this, myRating, function () {\r\n if (disabled === false) {\r\n elem.removeAttribute(\"title\");\r\n }\r\n\r\n isRating = false;\r\n elem.classList.remove(\"is-busy\");\r\n });\r\n }\r\n }\r\n /**\r\n * Disables the rater so that it's not possible to click the stars.\r\n */\r\n\r\n\r\n function disable() {\r\n disabled = true;\r\n elem.classList.add(\"disabled\");\r\n\r\n if (showToolTip && !!disableText) {\r\n var toolTip = disableText.replace(\"{rating}\", !!rating ? rating : 0);\r\n toolTip = toolTip.replace(\"{maxRating}\", stars);\r\n elem.setAttribute(\"title\", toolTip);\r\n } else {\r\n elem.removeAttribute(\"title\");\r\n }\r\n }\r\n /**\r\n * Enabled the rater so that it's possible to click the stars.\r\n */\r\n\r\n\r\n function enable() {\r\n disabled = false;\r\n elem.removeAttribute(\"title\");\r\n elem.classList.remove(\"disabled\");\r\n }\r\n /**\r\n * Sets the rating\r\n */\r\n\r\n\r\n function setRating(value) {\r\n if (typeof value === \"undefined\") {\r\n throw new Error(\"Value not set.\");\r\n }\r\n\r\n if (value === null) {\r\n throw new Error(\"Value cannot be null.\");\r\n }\r\n\r\n if (typeof value !== \"number\") {\r\n throw new Error(\"Value must be a number.\");\r\n }\r\n\r\n if (value < 0 || value > stars) {\r\n throw new Error(\"Value too high. Please set a rating of \" + stars + \" or below.\");\r\n }\r\n\r\n rating = value;\r\n elem.querySelector(\".star-value\").style.width = value / stars * 100 + \"%\";\r\n elem.setAttribute(\"data-rating\", value);\r\n }\r\n /**\r\n * Gets the rating\r\n */\r\n\r\n\r\n function getRating() {\r\n return rating;\r\n }\r\n /**\r\n * Set the rating to a value to inducate it's not rated.\r\n */\r\n\r\n\r\n function clear() {\r\n rating = null;\r\n elem.querySelector(\".star-value\").style.width = \"0px\";\r\n elem.removeAttribute(\"title\");\r\n }\r\n /**\r\n * Remove event handlers.\r\n */\r\n\r\n\r\n function dispose() {\r\n elem.removeEventListener(\"mousemove\", onMouseMove);\r\n elem.removeEventListener(\"mouseleave\", onStarOut);\r\n elem.removeEventListener(\"click\", onStarClick);\r\n elem.removeEventListener(\"touchmove\", handleMove, false);\r\n elem.removeEventListener(\"touchstart\", handleStart, false);\r\n elem.removeEventListener(\"touchend\", handleEnd, false);\r\n elem.removeEventListener(\"touchcancel\", handleCancel, false);\r\n }\r\n\r\n elem.addEventListener(\"mousemove\", onMouseMove);\r\n elem.addEventListener(\"mouseleave\", onStarOut);\r\n var module = {\r\n setRating: setRating,\r\n getRating: getRating,\r\n disable: disable,\r\n enable: enable,\r\n clear: clear,\r\n dispose: dispose,\r\n\r\n get element() {\r\n return elem;\r\n }\r\n\r\n };\r\n /**\r\n * Handles touchmove event.\r\n * @param {TouchEvent} e\r\n */\r\n\r\n function handleMove(e) {\r\n e.preventDefault();\r\n onMove(e, true);\r\n }\r\n /**\r\n * Handles touchstart event.\r\n * @param {TouchEvent} e \r\n */\r\n\r\n\r\n function handleStart(e) {\r\n e.preventDefault();\r\n onMove(e, true);\r\n }\r\n /**\r\n * Handles touchend event.\r\n * @param {TouchEvent} e \r\n */\r\n\r\n\r\n function handleEnd(evt) {\r\n evt.preventDefault();\r\n onMove(evt, true);\r\n onStarClick.call(module);\r\n }\r\n /**\r\n * Handles touchend event.\r\n * @param {TouchEvent} e \r\n */\r\n\r\n\r\n function handleCancel(e) {\r\n e.preventDefault();\r\n onStarOut(e);\r\n }\r\n\r\n elem.addEventListener(\"click\", onStarClick.bind(module));\r\n elem.addEventListener(\"touchmove\", handleMove, false);\r\n elem.addEventListener(\"touchstart\", handleStart, false);\r\n elem.addEventListener(\"touchend\", handleEnd, false);\r\n elem.addEventListener(\"touchcancel\", handleCancel, false);\r\n return module;\r\n};\r\n\r\n},{\"./style.css\":2}],2:[function(require,module,exports){\r\nvar css = \".star-rating {\\n width: 0;\\n position: relative;\\n display: inline-block;\\n background-image: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDguOSIgaGVpZ2h0PSIxMDMuNiIgdmlld0JveD0iMCAwIDEwOC45IDEwMy42Ij48ZGVmcz48c3R5bGU+LmNscy0xe2ZpbGw6I2UzZTZlNjt9PC9zdHlsZT48L2RlZnM+PHRpdGxlPnN0YXJfMDwvdGl0bGU+PGcgaWQ9IkxheWVyXzIiIGRhdGEtbmFtZT0iTGF5ZXIgMiI+PGcgaWQ9IkxheWVyXzEtMiIgZGF0YS1uYW1lPSJMYXllciAxIj48cG9seWdvbiBjbGFzcz0iY2xzLTEiIHBvaW50cz0iMTA4LjkgMzkuNiA3MS4zIDM0LjEgNTQuNCAwIDM3LjYgMzQuMSAwIDM5LjYgMjcuMiA2Ni4xIDIwLjggMTAzLjYgNTQuNCA4NS45IDg4LjEgMTAzLjYgODEuNyA2Ni4xIDEwOC45IDM5LjYiLz48L2c+PC9nPjwvc3ZnPg0K);\\n background-position: 0 0;\\n background-repeat: repeat-x;\\n cursor: pointer;\\n}\\n.star-rating .star-value {\\n position: absolute;\\n height: 100%;\\n width: 100%;\\n background: url('data:image/svg+xml;base64,PHN2Zw0KCXhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgd2lkdGg9IjEwOC45IiBoZWlnaHQ9IjEwMy42IiB2aWV3Qm94PSIwIDAgMTA4LjkgMTAzLjYiPg0KCTxkZWZzPg0KCQk8c3R5bGU+LmNscy0xe2ZpbGw6I2YxYzk0Nzt9PC9zdHlsZT4NCgk8L2RlZnM+DQoJPHRpdGxlPnN0YXIxPC90aXRsZT4NCgk8ZyBpZD0iTGF5ZXJfMiIgZGF0YS1uYW1lPSJMYXllciAyIj4NCgkJPGcgaWQ9IkxheWVyXzEtMiIgZGF0YS1uYW1lPSJMYXllciAxIj4NCgkJCTxwb2x5Z29uIGNsYXNzPSJjbHMtMSIgcG9pbnRzPSI1NC40IDAgNzEuMyAzNC4xIDEwOC45IDM5LjYgODEuNyA2Ni4xIDg4LjEgMTAzLjYgNTQuNCA4NS45IDIwLjggMTAzLjYgMjcuMiA2Ni4xIDAgMzkuNiAzNy42IDM0LjEgNTQuNCAwIi8+DQoJCTwvZz4NCgk8L2c+DQo8L3N2Zz4NCg==');\\n background-repeat: repeat-x;\\n}\\n.star-rating.disabled {\\n cursor: default;\\n}\\n.star-rating.is-busy {\\n cursor: wait;\\n}\\n.star-rating .star-value.rtl {\\n -moz-transform: scaleX(-1);\\n -o-transform: scaleX(-1);\\n -webkit-transform: scaleX(-1);\\n transform: scaleX(-1);\\n filter: FlipH;\\n -ms-filter: \\\"FlipH\\\";\\n right: 0;\\n left: auto;\\n}\\n\"; (require(\"browserify-css\").createStyle(css, { \"href\": \"lib\\\\style.css\" }, { \"insertAt\": \"bottom\" })); module.exports = css;\r\n},{\"browserify-css\":3}],3:[function(require,module,exports){\r\n'use strict';\r\n// For more information about browser field, check out the browser field at https://github.com/substack/browserify-handbook#browser-field.\r\n\r\nvar styleElementsInsertedAtTop = [];\r\n\r\nvar insertStyleElement = function(styleElement, options) {\r\n var head = document.head || document.getElementsByTagName('head')[0];\r\n var lastStyleElementInsertedAtTop = styleElementsInsertedAtTop[styleElementsInsertedAtTop.length - 1];\r\n\r\n options = options || {};\r\n options.insertAt = options.insertAt || 'bottom';\r\n\r\n if (options.insertAt === 'top') {\r\n if (!lastStyleElementInsertedAtTop) {\r\n head.insertBefore(styleElement, head.firstChild);\r\n } else if (lastStyleElementInsertedAtTop.nextSibling) {\r\n head.insertBefore(styleElement, lastStyleElementInsertedAtTop.nextSibling);\r\n } else {\r\n head.appendChild(styleElement);\r\n }\r\n styleElementsInsertedAtTop.push(styleElement);\r\n } else if (options.insertAt === 'bottom') {\r\n head.appendChild(styleElement);\r\n } else {\r\n throw new Error('Invalid value for parameter \\'insertAt\\'. Must be \\'top\\' or \\'bottom\\'.');\r\n }\r\n};\r\n\r\nmodule.exports = {\r\n // Create a tag with optional data attributes\r\n createLink: function(href, attributes) {\r\n var head = document.head || document.getElementsByTagName('head')[0];\r\n var link = document.createElement('link');\r\n\r\n link.href = href;\r\n link.rel = 'stylesheet';\r\n\r\n for (var key in attributes) {\r\n if ( ! attributes.hasOwnProperty(key)) {\r\n continue;\r\n }\r\n var value = attributes[key];\r\n link.setAttribute('data-' + key, value);\r\n }\r\n\r\n head.appendChild(link);\r\n },\r\n // Create a