diff --git a/README.md b/README.md index 779ee48..8e49399 100644 --- a/README.md +++ b/README.md @@ -48,7 +48,7 @@ Use this to hide the **Web to Plex** button. It changes the button's opacity to Use this to reload **Web to Plex** on the current page. This can sometimes fix loading issues or cache errors. ### Plex URL -This is a *moderately advance* setting, but is very useful to know. If you know your Plex server's URL (e.g. `https://localhost:32400`), then you can specify this and avoid bandwidth usage, as the extension will communicate with Plex on your device instead of `https://app.plex.tv/`. +This is a *moderately advanced* setting, but is very useful to know. If you know your Plex server's URL (e.g. `https://localhost:32400`), then you can specify this and avoid bandwidth usage, as the extension will communicate with Plex on your device instead of `https://app.plex.tv/`. ### Find this | Right Click If you aren't satisfied with a found item, or it is incorrect, you can right click the page and use the **Web to Plex | Find "XYZ"** feature to search for the item. diff --git a/moz.xpi b/moz.xpi index 514ae83..56038ad 100644 Binary files a/moz.xpi and b/moz.xpi differ diff --git a/moz.zip b/moz.zip index bb2a84e..ed06c33 100644 Binary files a/moz.zip and b/moz.zip differ diff --git a/moz/Glyphicons Social.woff b/moz/Glyphicons Social.woff new file mode 100644 index 0000000..2ccc999 Binary files /dev/null and b/moz/Glyphicons Social.woff differ diff --git a/moz/Glyphicons.woff b/moz/Glyphicons.woff new file mode 100644 index 0000000..c0114ce Binary files /dev/null and b/moz/Glyphicons.woff differ diff --git a/moz/Plex.bold.woff b/moz/Plex.bold.woff new file mode 100644 index 0000000..cde225e Binary files /dev/null and b/moz/Plex.bold.woff differ diff --git a/moz/Plex.bold.woff2 b/moz/Plex.bold.woff2 new file mode 100644 index 0000000..9217544 Binary files /dev/null and b/moz/Plex.bold.woff2 differ diff --git a/moz/Plex.woff b/moz/Plex.woff new file mode 100644 index 0000000..133f3d6 Binary files /dev/null and b/moz/Plex.woff differ diff --git a/moz/Plex.woff2 b/moz/Plex.woff2 new file mode 100644 index 0000000..b82c15a Binary files /dev/null and b/moz/Plex.woff2 differ diff --git a/moz/__layout__.js b/moz/__layout__.js new file mode 100644 index 0000000..9e2b387 --- /dev/null +++ b/moz/__layout__.js @@ -0,0 +1,39 @@ +// optional +// "Friendly Name" requires: api|username|password|token +// api - the user's API keys (external, such as TMDb/OMDb) +// username - the user's usernames (internal, such as Radarr/Sonarr/etc.) +// password - the user's passwords (internal) +// token - the user's tokens (internal) +// Example: "Web to Plex" requires: api, token + +let script = { + // required + "url": "< URL RegExp >", + // Example: *://*.amazon.*/*/video/(detail|buy)/* + // *:// - match any protocol (http, https, etc.) + // *.amazon - match any sub-domain (www, ww5, etc.) + // .* - match any TLD (com, net, org, etc.) + // /* - match any path + // (detail|buy) - match one of the items + + // optional + "ready": () => { /* return a boolean to describe if the page is ready */ }, + + // optional + "timeout": 1000, // if the script fails to complete, retry after ... milliseconds + + // required + "init": (ready) => { + let _title, _year, _image, R = RegExp; + + let title = $('#title').first, + year = $('#year').first, + image = $('#image').first, + type = script.getType(); // described below + + return { type, title, year, image }; + }, + + // optional | functionality only + "getType": () => 'movie' || 'show', +}; diff --git a/moz/__test__.js b/moz/__test__.js new file mode 100644 index 0000000..5ec2c9a --- /dev/null +++ b/moz/__test__.js @@ -0,0 +1,37 @@ +let script = { + // required + "url": "*://webtoplex.github.io/web/test/*", + // Example: *://*.amazon.com/*/video/(detail|buy)/* + // *:// - match any protocol (http, https, etc.) + // *.amazon.com - match any sub-domain (www, ww5, etc.) + // /* - match any path + // (detail|buy) - match one of the items + + // optional + "ready": () => { + /* return a boolean to describe if the page is ready */ + return !!$('#title').first.textContent.length; + }, + + // optional + "timeout": 1000, // if the script fails to complete, retry after ... milliseconds + + // required + "init": (ready) => { + let _title, _year, _image, R = RegExp; + + let title = $('#title').first, + year = $('#year').first, + image = $('#poster').first, + type = script.getType(); // described below + + title = title.textContent; + year = +year.textContent; + image = image.src || ''; + + return { type, title, year, image }; + }, + + // optional | functionality only + "getType": () => $('#example').first.getAttribute('type'), +}; diff --git a/moz/_blank.orange.png b/moz/_blank.orange.png new file mode 100644 index 0000000..968654c Binary files /dev/null and b/moz/_blank.orange.png differ diff --git a/moz/_blank.png b/moz/_blank.png new file mode 100644 index 0000000..a88188b Binary files /dev/null and b/moz/_blank.png differ diff --git a/moz/amazon.css b/moz/amazon.css new file mode 100644 index 0000000..dac83ab --- /dev/null +++ b/moz/amazon.css @@ -0,0 +1,32 @@ +.web-to-plex-minion { + margin-right: 8px; + color: #fff!important; + margin-bottom: 8px!important; + line-height: 2!important; + text-decoration: none!important; + width: auto!important; +} + +.web-to-plex-minion.wtp--download { + background: linear-gradient(180deg, #f67e56, #f45a26)!important; +} + +.web-to-plex-minion.wtp--download:hover { + background: linear-gradient(180deg, #ff8960, #fd6430)!important; +} + +.web-to-plex-minion.wtp--found { + background: linear-gradient(180deg, #f7dfa5, #ca7c1f)!important; +} + +.web-to-plex-minion.wtp--found:hover { + background: linear-gradient(180deg, #f7dfa5, #f8c022)!important; +} + +#tt--0-0 { + background: linear-gradient(180deg, #70767d 0, #696f78 0, #3d444e 100%)!important; +} + +#tt--0-0:hover { + background: linear-gradient(180deg, #70767d 0, #696f78 0, #3d444e 100%)!important; +} diff --git a/moz/amazon.js b/moz/amazon.js index 0779401..e0b3fe1 100644 --- a/moz/amazon.js +++ b/moz/amazon.js @@ -52,4 +52,18 @@ let script = { 'tv': 'movie' }, + + "minions": () => { + let actions = $(script.getType() == 'tv'? '#dv-action-box .av-action-button-box': '#dv-action-box'); + + if(actions.empty) + return; + + actions.forEach(element => { + let minion = furnish('a.av-button.av-button--default', {}, 'Web to Plex'); + + addMinions(minion); + element.appendChild(minion); + }); + }, }; diff --git a/moz/background.js b/moz/background.js index cffc29b..bd9edb5 100644 --- a/moz/background.js +++ b/moz/background.js @@ -17,7 +17,6 @@ let date = (new Date), let BACKGROUND_STORAGE = browser.storage.sync || browser.storage.local; let BACKGROUND_CONFIGURATION; - // returns the proper CORS mode of the URL let cors = url => ((/^(https|sftp)\b/i.test(url) || /\:(443|22)\b/i.test(url)? '': 'no-') + 'cors'); @@ -68,7 +67,7 @@ function ChangeStatus({ ITEM_ID, ITEM_TITLE, ITEM_TYPE, ID_PROVIDER, ITEM_YEAR, // File friendly title SEARCH_TITLE = ITEM_TITLE.replace(/[\-\s]+/g, '-').replace(/\s*&\s*/g, ' and ').replace(/[^\w\-\'\*\#]+/g, ''), // Search friendly title - SEARCH_PROVIDER = /\b(tv|show|series)\b/i.test(ITEM_TYPE)? 'GG': /^im/i.test(ID_PROVIDER)? 'VO': /^tm/i.test(ID_PROVIDER)? 'GX': 'GG'; + SEARCH_PROVIDER = (ITEM_TYPE == 'show')? 'GG': /^im/i.test(ID_PROVIDER)? 'VO': /^tm/i.test(ID_PROVIDER)? 'GX': 'GG'; ITEM_ID = (ITEM_ID && !/^tt$/i.test(ITEM_ID)? ITEM_ID: '') + ''; ITEM_ID = ITEM_ID.replace(/^.*\b(tt\d+)\b.*$/, '$1').replace(/^.*\bid=(\d+)\b.*$/, '$1').replace(/^.*(?:movie|tv|(?:tv-?)?(?:shows?|series|episodes?))\/(\d+).*$/, '$1'); @@ -900,6 +899,11 @@ browser.runtime.onMessage.addListener((request = {}, sender, callback) => { FILE_PATH = (item.path || ''), ITEM_ID = ((i, I)=>{for(let p in i)if(RegExp('^'+I,'i').test(p))return i[p]})(item, ID_PROVIDER); + if(/movie|film|cinema|theat[re]{2}/i.test(ITEM_TYPE)) + ITEM_TYPE = 'movie'; + else if(/tv|show|series|episode/i.test(ITEM_TYPE)) + ITEM_TYPE = 'show'; + if(request.type) { try { switch(request.type) { diff --git a/moz/colors.css b/moz/colors.css new file mode 100644 index 0000000..5c66061 --- /dev/null +++ b/moz/colors.css @@ -0,0 +1,572 @@ +:root { + --light-black: #222; + --black: #282828; + --dark-black: #000; + + --light-blue: #197bcc; + --blue: #265af4; + --dark-blue: #2a2aff; + + --light-grey: #999; + --grey: #666; + --dark-grey: #3f4245; + + --green: #80f080; + + --light-orange: #e59029; + --orange: #cc7b19; + --dark-orange: #f45a26; + + --light-red: #f44; + --red: #ff2a2a; + --dark-red: #d11; + + --light-white: #fff; + --white: #eee; + + --disabled: #909090ee; + --half-black: #0008; + --half-green: #80f08088; + --half-grey: #8888; + --half-white: #fff8; + --quarter-black: #0004; + --quarter-grey: #8884; + --quarter-white: #fff4; + --transparent: #0000; +} + +/* Text Color */ +[light-black]:not([gradient]) { + color: var(--light-black) +} + +[black]:not([gradient]) { + color: var(--black) +} + +[dark-black]:not([gradient]) { + color: var(--dark-black) +} + +[light-blue]:not([gradient]) { + color: var(--light-blue) +} + +[blue]:not([gradient]) { + color: var(--blue) +} + +[dark-blue]:not([gradient]) { + color: var(--dark-blue) +} + +[light-grey]:not([gradient]) { + color: var(--light-grey) +} + +[grey]:not([gradient]) { + color: var(--grey) +} + +[dark-grey]:not([gradient]) { + color: var(--dark-grey) +} + +[green]:not([gradient]) { + color: var(--green) +} + +[light-orange]:not([gradient]) { + color: var(--light-orange) +} + +[orange]:not([gradient]) { + color: var(--orange) +} + +[dark-orange]:not([gradient]) { + color: var(--dark-orange) +} + +[light-red]:not([gradient]) { + color: var(--light-red) +} + +[red]:not([gradient]) { + color: var(--red) +} + +[dark-red]:not([gradient]) { + color: var(--dark-red) +} + +[light-white]:not([gradient]) { + color: var(--light-white) +} + +[white]:not([gradient]) { + color: var(--white) +} + +[disabled]:not([gradient]) { + color: var(--disabled) +} + +[half-black]:not([gradient]) { + color: var(--half-black) +} + +[half-green]:not([gradient]) { + color: var(--half-green) +} + +[half-grey]:not([gradient]) { + color: var(--half-grey) +} + +[half-white]:not([gradient]) { + color: var(--half-white) +} + +[quarter-black]:not([gradient]) { + color: var(--quarter-black) +} + +[quarter-grey]:not([gradient]) { + color: var(--quarter-grey) +} + +[quarter-white]:not([gradient]) { + color: var(--quarter-white) +} + +[transparent]:not([gradient]) { + color: var(--transparent) +} + +/* Text Color (Hover) */ +[light-black\$]:hover { + color: var(--light-black) +} + +[black\$]:hover { + color: var(--black) +} + +[dark-black\$]:hover { + color: var(--dark-black) +} + +[light-blue\$]:hover { + color: var(--light-blue) +} + +[blue\$]:hover { + color: var(--blue) +} + +[dark-blue\$]:hover { + color: var(--dark-blue) +} + +[light-grey\$]:hover { + color: var(--light-grey) +} + +[grey\$]:hover { + color: var(--grey) +} + +[dark-grey\$]:hover { + color: var(--dark-grey) +} + +[green\$]:hover { + color: var(--green) +} + +[light-orange\$]:hover { + color: var(--light-orange) +} + +[orange\$]:hover { + color: var(--orange) +} + +[dark-orange\$]:hover { + color: var(--dark-orange) +} + +[light-red\$]:hover { + color: var(--light-red) +} + +[red\$]:hover { + color: var(--red) +} + +[dark-red\$]:hover { + color: var(--dark-red) +} + +[light-white\$]:hover { + color: var(--light-white) +} + +[white\$]:hover { + color: var(--white) +} + +[disabled\$]:hover { + color: var(--disabled) +} + +[half-black\$]:hover { + color: var(--half-black) +} + +[half-green\$]:hover { + color: var(--half-green) +} + +[half-grey\$]:hover { + color: var(--half-grey) +} + +[half-white\$]:hover { + color: var(--half-white) +} + +[quarter-black\$]:hover { + color: var(--quarter-black) +} + +[quarter-grey\$]:hover { + color: var(--quarter-grey) +} + +[quarter-white\$]:hover { + color: var(--quarter-white) +} + +[transparent\$]:hover { + color: var(--transparent) +} + +/* Background Color */ +[light-black-bg], [light-black][gradient] { + background: var(--light-black) +} + +[black-bg], [black][gradient] { + background: var(--black) +} + +[dark-black-bg], [dark-black][gradient] { + background: var(--dark-black) +} + +[light-blue-bg], [light-blue][gradient] { + background: var(--light-blue) +} + +[blue-bg], [blue][gradient] { + background: var(--blue) +} + +[dark-blue-bg], [dark-blue][gradient] { + background: var(--dark-blue) +} + +[light-grey-bg], [light-grey][gradient] { + background: var(--light-grey) +} + +[grey-bg], [grey][gradient] { + background: var(--grey) +} + +[dark-grey-bg], [dark-grey][gradient] { + background: var(--dark-grey) +} + +[green], [green][gradient] { + background: var(--green) +} + +[light-orange-bg], [light-orange][gradient] { + background: var(--light-orange) +} + +[orange-bg], [orange][gradient] { + background: var(--orange) +} + +[dark-orange-bg], [dark-orange][gradient] { + background: var(--dark-orange) +} + +[light-red-bg], [light-red][gradient] { + background: var(--light-red) +} + +[red-bg], [red][gradient] { + background: var(--red) +} + +[dark-red-bg], [dark-red][gradient] { + background: var(--dark-red) +} + +[light-white-bg], [light-white][gradient] { + background: var(--light-white) +} + +[white-bg], [white][gradient] { + background: var(--white) +} + +[disabled-bg] { + background: var(--disabled) +} + +[half-black-bg] { + background: var(--half-black) +} + +[half-green-bg] { + background: var(--half-green) +} + +[half-grey-bg] { + background: var(--half-grey) +} + +[half-white-bg] { + background: var(--half-white) +} + +[quarter-black-bg] { + background: var(--quarter-black) +} + +[quarter-grey-bg] { + background: var(--quarter-grey) +} + +[quarter-white-bg] { + background: var(--quarter-white) +} + +[transparent-bg] { + background: var(--transparent) +} + +/* Background Color (Hover) */ +[light-black-bg\$]:hover { + background: var(--light-black) +} + +[black-bg\$]:hover { + background: var(--black) +} + +[dark-black-bg\$]:hover { + background: var(--dark-black) +} + +[light-blue-bg\$]:hover { + background: var(--light-blue) +} + +[blue-bg\$]:hover { + background: var(--blue) +} + +[dark-blue-bg\$]:hover { + background: var(--dark-blue) +} + +[light-grey-bg\$]:hover { + background: var(--light-grey) +} + +[grey-bg\$]:hover { + background: var(--grey) +} + +[dark-grey-bg\$]:hover { + background: var(--dark-grey) +} + +[green-bg\$]:hover { + background: var(--green) +} + +[light-orange-bg\$]:hover { + background: var(--light-orange) +} + +[orange-bg\$]:hover { + background: var(--orange) +} + +[dark-orange-bg\$]:hover { + background: var(--dark-orange) +} + +[light-red-bg\$]:hover { + background: var(--light-red) +} + +[red-bg\$]:hover { + background: var(--red) +} + +[dark-red-bg\$]:hover { + background: var(--dark-red) +} + +[light-white-bg\$]:hover { + background: var(--light-white) +} + +[white-bg\$]:hover { + background: var(--white) +} + +[disabled-bg\$]:hover { + background: var(--disabled) +} + +[half-black-bg\$]:hover { + background: var(--half-black) +} + +[half-green-bg\$]:hover { + background: var(--half-green) +} + +[half-grey-bg\$]:hover { + background: var(--half-grey) +} + +[half-white-bg\$]:hover { + background: var(--half-white) +} + +[quarter-black-bg\$]:hover { + background: var(--quarter-black) +} + +[quarter-grey-bg\$]:hover { + background: var(--quarter-grey) +} + +[quarter-white-bg\$]:hover { + background: var(--quarter-white) +} + +[transparent-bg\$]:hover { + background: var(--transparent) +} + +/* Gradient Text (Darken) */ +[light-black][gradient="darken"i] { + background: linear-gradient(var(--light-black), var(--black)) +} + +[black][gradient="darken"i] { + background: linear-gradient(var(--black), var(--dark-black)) +} + +[light-blue][gradient="darken"i] { + background: linear-gradient(var(--light-blue), var(--blue)) +} + +[blue][gradient="darken"i] { + background: linear-gradient(var(--blue), var(--dark-blue)) +} + +[light-grey][gradient="darken"i] { + background: linear-gradient(var(--light-grey), var(--grey)) +} + +[grey][gradient="darken"i] { + background: linear-gradient(var(--grey), var(--dark-grey)) +} + +[light-orange][gradient="darken"i] { + background: linear-gradient(var(--light-orange), var(--orange)) +} + +[orange][gradient="darken"i] { + background: linear-gradient(var(--orange), var(--dark-orange)) +} + +[light-red][gradient="darken"i] { + background: linear-gradient(var(--light-red), var(--red)) +} + +[red][gradient="darken"i] { + background: linear-gradient(var(--red), var(--dark-red)) +} + +[light-white][gradient="darken"i] { + background: linear-gradient(var(--light-white), var(--white)) +} + +/* Gradient Text (Lighten) */ +[black][gradient="lighten"i] { + background: linear-gradient(var(--black), var(--light-black)) +} + +[dark-black][gradient="lighten"i] { + background: linear-gradient(var(--dark-black), var(--black)) +} + +[blue][gradient="lighten"i] { + background: linear-gradient(var(--blue), var(--light-blue)) +} + +[dark-blue][gradient="lighten"i] { + background: linear-gradient(var(--dark-blue), var(--blue)) +} + +[grey][gradient="lighten"i] { + background: linear-gradient(var(--grey), var(--light-grey)) +} + +[dark-grey][gradient="lighten"i] { + background: linear-gradient(var(--dark-grey), var(--grey)) +} + +[orange][gradient="lighten"i] { + background: linear-gradient(var(--orange), var(--light-orange)) +} + +[dark-orange][gradient="lighten"i] { + background: linear-gradient(var(--dark-orange), var(--orange)) +} + +[red][gradient="lighten"i] { + background: linear-gradient(var(--red), var(--light-red)) +} + +[dark-red][gradient="lighten"i] { + background: linear-gradient(var(--dark-red), var(--red)) +} + +[white][gradient="lighten"i] { + background: linear-gradient(var(--white), var(--light-white)) +} + +/* All Gradient Properties */ +[gradient] { + background-clip: text; + -moz-background-clip: text; + -webkit-background-clip: text; + + color: #0000; + -webkit-text-fill-color: #0000; +} diff --git a/moz/common.css b/moz/common.css index b73e392..ef7c08f 100644 --- a/moz/common.css +++ b/moz/common.css @@ -1,98 +1,123 @@ /** Common CSS - * Web to Plex - */ - /* Basic/Global Styling */ - [class*="web-to-plex"]::-webkit-scrollbar, [class*="web-to-plex"]::-moz-scrollbar { - width: 10px !important; - } - - [class*="web-to-plex"]::-webkit-scrollbar-thumb, [class*="web-to-plex"]::-moz-scrollbar-thumb { - min-height: 50px !important; - background: rgba(255, 255, 255, 0.15) !important; - border: 2px solid rgba(0, 0, 0, 0) !important; - border-radius: 8px !important; - background-clip: padding-box !important; - } - - [class*="web-to-plex"]::-webkit-scrollbar-track, [class*="web-to-plex"]::-moz-scrollbar-track { - background: #0000 !important; - } - - [class*="web-to-plex"]::-webkit-input-placeholder, [class*="web-to-plex"]::-moz-placeholder, [class*="web-to-plex"]:-moz-placeholder { - color: #999 !important; - } - - [class*="web-to-plex"] input[type="text"], [class*="web-to-plex"] input[type="password"], [class*="web-to-plex"] select { - width: 30vw !important; - line-height: 1.5em !important; - transition: background 0.2s !important; - display: block !important; - height: 38px !important; - padding: 6px 12px !important; - font-size: 16px !important; - color: #eee !important; - vertical-align: middle; - background: rgba(255, 255, 255, 0.25) !important; - border: 3px solid rgba(0, 0, 0, 0) !important; - border-radius: 3px !important; - font-family: inherit !important; - margin: 0 !important; - } - - [class*="web-to-plex"] select { - margin-left: 10px !important; - font-size: 16px !important; - line-height: inherit !important; - text-transform: none !important; - } - - [class*="web-to-plex"] option { - background: #3f4245 !important; - } - - [class*="web-to-plex"] button { - padding: 10px 18px !important; - font-size: 16px !important; - line-height: 1.33 !important; - border-radius: 3px !important; - font-family: inherit !important; - text-transform: uppercase !important; - border: 0 !important; - box-shadow: none !important; - position: relative !important; - overflow: hidden !important; - color: #fff; - background: #cc7b19; - margin-bottom: 0 !important; - font-weight: 400 !important; - vertical-align: middle; - cursor: pointer !important; - white-space: nowrap; - user-select: none; - transition: all 0.1s !important; - } +* Web to Plex +*/ + +@charset "utf-8"; +@font-face { + font-family: "Plex"; + src: local(Plex), + url(//webtoplex.github.io/font/Plex.woff2) format('woff2'), + url(//webtoplex.github.io/font/Plex.woff) format('woff'); + font-weight: 400; + font-style: normal; +} + +@font-face { + font-family: "Plex-bold"; + src: local(Plex-bold), + url(//webtoplex.github.io/font/Plex.bold.woff2) format('woff2'), + url(//webtoplex.github.io/font/Plex.bold.woff) format('woff'); + font-weight: 400; + font-style: normal; +} + +/* Basic/Global Styling */ +*::-moz-scrollbar { + width: 10px; +} + +*::-moz-scrollbar-thumb { + min-height: 50px; + background: rgba(255, 255, 255, 0.15); + border: 2px solid rgba(0, 0, 0, 0); + border-radius: 8px; + background-clip: padding-box; +} - [class*="web-to-plex"] button:hover { - background: #e59029; - } +*::-moz-scrollbar-track { + /* background: url(noise.png) fixed, #3f4245 !important; */ +} + +*::placeholder { + color: #999 !important; +} - [class*="web-to-plex"] input::placeholder, [class*="web-to-plex"] input:placeholder { +*::-webkit-input-placeholder { color: #999 !important; - } +} + +/* Web to Plex */ +[class*="web-to-plex"] input[type="text"], [class*="web-to-plex"] input[type="password"], [class*="web-to-plex"] select { + width: 30vw !important; + line-height: 1.5em !important; + transition: background 0.2s !important; + display: block !important; + height: 38px !important; + padding: 6px 12px !important; + font-size: 16px !important; + color: var(--white) !important; + vertical-align: middle; + background: rgba(255, 255, 255, 0.25) !important; + border: 3px solid rgba(0, 0, 0, 0) !important; + border-radius: 3px !important; + font-family: inherit !important; + margin: 0 !important; +} + +[class*="web-to-plex"] select { + margin-left: 10px !important; + font-size: 16px !important; + line-height: inherit !important; + text-transform: none !important; +} + +[class*="web-to-plex"] option { + background: var(--light-grey) !important; +} + +[class*="web-to-plex"] button { + padding: 10px 18px !important; + font-size: 16px !important; + line-height: 1.33 !important; + border-radius: 3px !important; + font-family: inherit !important; + text-transform: uppercase !important; + border: 0 !important; + box-shadow: none !important; + position: relative !important; + overflow: hidden !important; + color: var(--light-white); + background: var(--orange); + margin-bottom: 0 !important; + font-weight: 400 !important; + vertical-align: middle; + cursor: pointer !important; + white-space: nowrap; + user-select: none; + transition: all 0.1s !important; +} - [class*="web-to-plex"][disabled], [class*="web-to-plex"] [disabled] { - cursor: not-allowed !important; - color: #909090EE !important; - } +[class*="web-to-plex"] button:hover { + background: var(--light-orange); +} + +[class*="web-to-plex"] input::placeholder, [class*="web-to-plex"] input:placeholder { + color: var(--dark-grey) !important; +} + +[class*="web-to-plex"][disabled], [class*="web-to-plex"] [disabled] { + cursor: not-allowed !important; + color: var(--disabled) !important; +} /* Web to Plex notifications */ .web-to-plex-notification { - background: #F45A26 !important; + background: var(--dark-orange) !important; border-radius: 4px !important; - color: #FFF !important; + color: var(--light-white) !important; cursor: pointer !important; display: block !important; - font-family: arial, verdana, sans-serif !important; + font-family: "Plex", "Open Sans Regular", "Helvetica Neue", Helvetica, Arial, sans-serif, system !important; font-size: 20px !important; text-align: center !important; @@ -108,31 +133,31 @@ /* Web to Plex general information notifications */ .web-to-plex-notification.info { - background: #666 !important; + background: var(--grey) !important; } /* Web to Plex update notifications */ .web-to-plex-notification.update { - background: #2A2AFF !important; + background: var(--dark-blue) !important; } /* Web to Plex success notifications */ .web-to-plex-notification.success { - background: #03BDF9 !important; + background: var(--light-blue) !important; } /* Web to Plex error/warning notifications */ .web-to-plex-notification.warning, .web-to-plex-notification.error { - background: #FF2A2A !important; + background: var(--red) !important; } /* Web to Plex prompts */ .web-to-plex-prompt { - background: #0008 !important; + background: var(--half-black) !important; box-sizing: border-box !important; - color: #eee !important; + color: var(--white) !important; display: block !important; - font-family: Open Sans Regular, Helvetica Neue, Helvetica, Arial, sans-serif !important; + font-family: "Plex", "Open Sans Regular", "Helvetica Neue", Helvetica, Arial, sans-serif, system !important; font-size: 14px !important; line-height: 24px !important; overflow: auto !important; @@ -149,10 +174,11 @@ } .web-to-plex-prompt-body { - background: #282828; + background: var(--black); border: 0 !important; border-radius: 3px !important; box-shadow: 0 5px 15px #0008 !important; + box-sizing: content-box !important; display: block !important; left: 20% !important; @@ -166,10 +192,10 @@ } .web-to-plex-prompt-header, .web-to-plex-prompt-footer { - background: #32323240 !important; + background: var(--quarter-grey) !important; border: 1px solid #0000 !important; box-sizing: border-box !important; - color: #eee !important; + color: var(--white) !important; font: inherit !important; font-size: 2em !important; line-height: initial !important; @@ -182,11 +208,11 @@ height: 65px !important; width: 100% !important; - -webkit-tap-highlight-color: #0000; + -webkit-tap-highlight-color: var(--transparent); } .web-to-plex-prompt-header { - border-bottom-color: #222 !important; + border-bottom-color: var(--light-black) !important; border-bottom-width: 1px !important; border-top-left-radius: 3px !important; border-top-right-radius: 3px !important; @@ -203,7 +229,7 @@ overflow-x: hidden !important; overflow-y: auto !important; - padding: 12px !important; + padding: 0 12px 5px !important; position: relative !important; top: 65px !important; @@ -211,10 +237,10 @@ } .web-to-plex-prompt-option { - background: #32323240 !important; + background: var(--quarter-grey) !important; border: 1px solid #202020 !important; border-radius: 3px !important; - color: #999 !important; + color: var(--light-grey) !important; display: block !important; text-align: left !important; @@ -225,22 +251,21 @@ } .web-to-plex-prompt-option.mutable { + cursor: pointer; max-width: 60% !important; } .web-to-plex-prompt-option.mutable > h2 { - background: #0000 !important; - color: inherit !important; - font-family: inherit !important; - font-size: initial !important; - text-align: inherit !important; + background: var(--transparent) !important; + color: inherit !important; + font-family: inherit !important; + font-size: initial !important; + text-align: inherit !important; - margin: inherit !important; + margin: inherit !important; } -.web-to-plex-prompt-option.mutable > .remove, .web-to-plex-prompt-option.mutable > .choose { - background: #ffffff40 !important; - border-radius: 3px !important; +.web-to-plex-prompt-option.mutable [glyph] { transition: all 0.1s !important; height: 30px !important; @@ -248,67 +273,67 @@ float: right !important; margin-right: -2% !important; - margin-top: -8% !important; + margin-top: -5% !important; padding: 0 !important; } -.web-to-plex-prompt-option.mutable > .remove:hover, .web-to-plex-prompt-option.mutable > .choose:hover { - background: #ffffff4d !important; +.web-to-plex-prompt-option.mutable.choose [glyph] { + color: var(--grey); } -.web-to-plex-prompt-option.mutable > .remove::after { - content: '\00d7' !important; +.web-to-plex-prompt-option.mutable.chosen:first-child { + border-color: var(--green) !important; } -.web-to-plex-prompt-option.mutable > .choose::after { - content: '\2218' !important; -} - -.web-to-plex-prompt-option.mutable:first-child:last-child > .choose, .web-to-plex-prompt-option.mutable.chosen > .choose { - background: #80F08088 !important; +.web-to-plex-prompt-option.mutable.chosen:first-child [glyph] { + color: var(--green); } .web-to-plex-prompt-option.mutable > .quality { - width: 50% !important; + width: 50% !important; } .web-to-plex-prompt-option.mutable > .location { - width: 90% !important; + width: 90% !important; } .web-to-plex-prompt-option.mutable > .location:last-child:not(:first-child) { - margin-top: 5px !important; + margin-top: 5px !important; } .web-to-plex-permission:after { - background: #0000; - border-radius: 3px; - border: 0; - color: #cc7b19; - content: "\29eb"; - display: inline-block; - font-size: 150%; - padding: 0; - text-align: center; + background: var(--transparent); + border-radius: 3px; + border: 0; + color: var(--orange); + content: "\29eb"; + display: inline-block; + font-size: 150%; + padding: 0; + text-align: center; - margin: 0; - position: absolute; - right: 3%; + margin: 0; + position: absolute; + right: 3%; - height: 2em; - width: 2em; + height: 2em; + width: 2em; } .web-to-plex-prompt-footer { text-align: right !important; border-bottom-left-radius: 3px !important; border-bottom-right-radius: 3px !important; - border-top-color: #222 !important; + border-top-color: var(--light-black) !important; border-top-width: 1px !important; bottom: 0 !important; } +.web-to-plex-prompt-footer > * { + vertical-align: text-bottom !important; +} + .web-to-plex-prompt-input { float: left !important; position: relative !important; @@ -321,338 +346,359 @@ } .web-to-plex-prompt-accept { - background: #cc7b19 !important; + background: var(--orange) !important; margin-left: 5px !important; } .web-to-plex-prompt-accept:hover { - background: #e59029 !important; + background: var(--light-orange) !important; } .web-to-plex-prompt-decline { - background: #ffffff40 !important; + background: var(--quarter-white) !important; } .web-to-plex-prompt-decline:hover { - background: #ffffff4d !important; + background: var(--half-white) !important; } /* Web to Plex buttons */ .web-to-plex-button [module] { - position: relative !important; + position: relative !important; } .web-to-plex-button * { - border: none !important; - text-transform: none !important; + border: none !important; + text-transform: none !important; } .web-to-plex-button { - background-color: #3F4245 !important; - border: none !important; - color: #FFF !important; - font-family: Open Sans Semibold, Helvetica Neue, Helvetica, Arial, sans-serif !important; - font-size: 1em !important; - font-weight: 100 !important; - text-align: center !important; - - bottom: 5px !important; - left: 5px !important; - padding: 10px !important; - position: fixed !important; - right: unset !important; - z-index: 999999 !important; - - min-height: 0 !important; - min-width: 0 !important; - height: 72px !important; - width: 180px !important; - - transition: all 0.3s ease !important; + background-color: var(--light-grey) !important; + border: none !important; + color: var(--light-white) !important; + display: block !important; + font-family: "Plex", "Open Sans Regular", "Helvetica Neue", Helvetica, Arial, sans-serif, system !important; + font-size: 1em !important; + font-weight: 100 !important; + text-align: center !important; + + bottom: 5px !important; + left: 5px !important; + padding: 10px !important; + position: fixed !important; + right: unset !important; + z-index: 999999 !important; + + min-height: 0 !important; + min-width: 0 !important; + height: 72px !important; + width: 180px !important; + + transition: all 0.3s ease !important; } .web-to-plex-button.hide { - display: initial !important; + display: initial !important; } .web-to-plex-button.hide:not(:hover), .web-to-plex-button.sleeper { - opacity: 0.1; + opacity: 0.1; } *:not(#plexit-bookmarklet-frame) ~ .web-to-plex-button { - margin-left: 0px !important; + margin-left: 0px; } #plexit-bookmarklet-frame ~ .web-to-plex-button { - margin-left: 280px !important; + margin-left: 280px !important; } *:not(#plexit-bookmarklet-frame) + .web-to-plex-button #plexit, #plexit-bookmarklet-frame + .web-to-plex-button #wtp-plexit { - display: none !important; + display: none !important; } .floating.web-to-plex-button { - border-radius: 50px !important; - box-shadow: 0 4px 10px 0 rgba(0, 0, 0, 0.3) !important; + border-radius: 50px !important; + box-shadow: 0 4px 10px 0 rgba(0, 0, 0, 0.3) !important; - height: 75px !important; - width: 75px !important; + height: 75px !important; + width: 75px !important; } .floating.web-to-plex-button::after { - content: ' ' !important; + content: ' ' !important; - background: #666 !important; - border: 1px solid #888 !important; - border-radius: 16px !important; + background: var(--grey) !important; + border: 1px solid #888 !important; + border-radius: 16px !important; - right: 0 !important; - top: 0 !important; - position: absolute !important; + right: 0 !important; + top: 0 !important; + position: absolute !important; - height: 16px !important; - width: 16px !important; + height: 16px !important; + width: 16px !important; - transition: background 0.4s linear !important; + transition: background 0.4s linear !important; } .floating.web-to-plex-button:not(.restarting):active, .floating.web-to-plex-button:not(.restarting):hover { - box-shadow: 1px 5px 20px 0 rgba(0, 0, 0, 0.6) !important; - cursor: pointer !important; + box-shadow: 1px 5px 20px 0 rgba(0, 0, 0, 0.6) !important; + cursor: pointer !important; } .floating.web-to-plex-button:focus { - outline: #0000 !important; + outline: #0000 !important; } .web-to-plex-button.wtp--download::after, .web-to-plex-button.wtp--download::before { - background: #265AF4 !important; + background: var(--blue) !important; } .web-to-plex-button.wtp--queued::after, .web-to-plex-button.wtp--queued::before { - background: #568AF4 !important; + background: var(--light-blue) !important; } .web-to-plex-button.wtp--found::after, .web-to-plex-button.wtp--found::before { - background: #F9BD03 !important; + background: var(--light-orange) !important; } .web-to-plex-button.wtp--error::after, .web-to-plex-button.wtp--error::before { - background: #FF2A2A !important; + background: var(--red) !important; } .web-to-plex-button::before { - content: ' ' !important; + content: ' ' !important; - background: #FFF6 !important; - border-radius: inherit !important; - display: none !important; + background: var(--half-white) !important; + border-radius: inherit !important; + display: none !important; - margin-top: -10px !important; - margin-left: -10px !important; + margin-top: -10px !important; + margin-left: -10px !important; - height: 75px !important; - width: 75px !important; + height: 75px !important; + width: 75px !important; - position: absolute !important; - z-index: 9999999 !important; + position: absolute !important; + z-index: 9999999 !important; } .web-to-plex-button.animate::before { - display: block !important; + display: block !important; - -webkit-transform: scale(0); - -moz-transform: scale(0); - -o-transform: scale(0); - transform: scale(0); + -webkit-transform: scale(0); + -moz-transform: scale(0); + -o-transform: scale(0); + transform: scale(0); - -webkit-animation: web-to-plex-ripple 0.5s linear; - -moz-animation: web-to-plex-ripple 0.5s linear; - -o-animation: web-to-plex-ripple 0.5s linear; - animation: web-to-plex-ripple 0.5s linear; + -webkit-animation: web-to-plex-ripple 0.5s linear; + -moz-animation: web-to-plex-ripple 0.5s linear; + -o-animation: web-to-plex-ripple 0.5s linear; + animation: web-to-plex-ripple 0.5s linear; } @-webkit-keyframes web-to-plex-ripple { - 100% { - opacity: 0; - -webkit-transform: scale(2.5); - } + 100% { + opacity: 0; + -webkit-transform: scale(2.5); + } } @-moz-keyframes web-to-plex-ripple { - 100% { - opacity: 0; - -moz-transform: scale(2.5); - } + 100% { + opacity: 0; + -moz-transform: scale(2.5); + } } @-o-keyframes web-to-plex-ripple { - 100% { - opacity: 0; - -o-transform: scale(2.5); - } + 100% { + opacity: 0; + -o-transform: scale(2.5); + } } @keyframes web-to-plex-ripple { - 100% { - opacity: 0; - transform: scale(2.5); - } + 100% { + opacity: 0; + transform: scale(2.5); + } } .web-to-plex-button.open, #plexit-bookmarklet-frame + .web-to-plex-button { - opacity: 1; + opacity: 1; - width: 350px !important; + width: 350px !important; } + .web-to-plex-button .list-name { - float: left !important; + float: left !important; } .web-to-plex-button ul { - margin: 0 !important; - padding-left: 0 !important; + margin: 0 !important; + padding-left: 0 !important; } .web-to-plex-button li { - display: inline-block !important; - list-style: none !important; + display: inline-block !important; + list-style: none !important; - margin: 0 !important; - padding: 5px !important; - vertical-align: bottom; + margin: 0 !important; + padding: 5px !important; + vertical-align: bottom; } .web-to-plex-button li > img { - display: inline !important; + display: inline !important; - margin-top: 0 !important; + margin-top: 0 !important; } .web-to-plex-button.hide.closed li:not(:first-child) { - display: none !important; + display: none !important; } *:not(#plexit-bookmarklet-frame) + .web-to-plex-button.closed .list-item { - float: left !important; - opacity: 0; - transition: opacity 0 !important; + float: left !important; + opacity: 0; + transition: opacity 0 !important; } .web-to-plex-button.open .list-item { - opacity: 1; - transition: opacity 2s !important; + opacity: 1; + transition: opacity 2s !important; } .web-to-plex-button.open li:hover [tooltip]::before, .web-to-plex-button.open [tooltip]:hover::before { - content: attr(tooltip) !important; + content: attr(tooltip) !important; - background: #3F424599 !important; - border-radius: 3px !important; - color: #fff !important; - font-family: arial, calibri, sans-serif, sans, monospace !important; - font-size: 15px !important; + background: var(--quarter-grey) !important; + border-radius: 3px !important; + color: var(--light-white) !important; + display: inline-block !important; + font-family: "Plex", "Open Sans Regular", "Helvetica Neue", Helvetica, Arial, sans-serif, system !important; + font-size: 15px !important; - bottom: 85px !important; - left: 35px !important; - padding: 3px 6px !important; - position: absolute !important; + bottom: 85px !important; + left: 35px !important; + padding: 3px 6px !important; + position: absolute !important; +} + +.web-to-plex-minion { + cursor: pointer; } /* bbodine @CodePen - https://codepen.io/bbodine1/pen/novBm */ [class*="web-to-plex"] .checkbox { - width: 80px; - height: 26px; - background: #000; - margin: 15px 0; - position: relative; - border-radius: 50px; - box-shadow: inset 0px 1px 1px rgba(0, 0, 0, 0.5), 0px 1px 0px rgba(255, 255, 255, 0.2); + width: 80px; + height: 26px; + background: var(--dark-black); + margin: 15px 0; + position: relative; + border-radius: 50px; + box-shadow: inset 0px 1px 1px rgba(0, 0, 0, 0.5), 0px 1px 0px rgba(255, 255, 255, 0.2); } [class*="web-to-plex"] span.checkbox { - display: inline-block; + display: inline-block; - margin: 0; - vertical-align: text-bottom; + margin: 0; + vertical-align: text-bottom; } [class*="web-to-plex"] .checkbox::after { - content: 'OFF'; - color: #666; - position: absolute; - right: 10px; - z-index: 0; - font: 12px/26px Arial, sans-serif; - font-weight: bold; - text-shadow: 1px 1px 0px rgba(255, 255, 255, 0.15); + content: 'OFF'; + color: var(--grey); + position: absolute; + right: 10px; + z-index: 0; + font: 12px/26px Arial, sans-serif; + font-weight: bold; + text-shadow: 1px 1px 0px rgba(255, 255, 255, 0.15); } [class*="web-to-plex"] .checkbox::before { - content: 'ON'; - color: #cc7b19; - position: absolute; - left: 10px; - z-index: 0; - font: 12px/26px Arial, sans-serif; - font-weight: bold; + content: 'ON'; + color: var(--orange); + position: absolute; + left: 10px; + z-index: 0; + font: 12px/26px Arial, sans-serif; + font-weight: bold; } [class*="web-to-plex"] .checkbox[prompt-yes]::before { - content: attr(prompt-yes); - text-transform: uppercase; + content: attr(prompt-yes); + text-transform: uppercase; } [class*="web-to-plex"] .checkbox[prompt-no]::after { - content: attr(prompt-no); - text-transform: uppercase; + content: attr(prompt-no); + text-transform: uppercase; } [class*="web-to-plex"] .checkbox[prompt-size="large"i]::before, .checkbox[prompt-size="large"i]::after { - font-size: 30px !important; + font-size: 30px !important; } [class*="web-to-plex"] .checkbox[prompt-size="medium"i]::before, .checkbox[prompt-size="medium"i]::after { - font-size: 21px !important; + font-size: 21px !important; } [class*="web-to-plex"] .checkbox[prompt-size="normal"i]::before, .checkbox[prompt-size="normal"i]::after { - font-size: 12px !important; + font-size: 12px !important; } [class*="web-to-plex"] .checkbox[prompt-size="small"i]::before, .checkbox[prompt-size="small"i]::after { - font-size: 6px !important; + font-size: 6px !important; } [class*="web-to-plex"] .checkbox[prompt="y/n"i]::before { - content: 'YES'; + content: 'YES'; } [class*="web-to-plex"] .checkbox[prompt="y/n"i]::after { - content: 'NO'; + content: 'NO'; } [class*="web-to-plex"] .checkbox label { - display: block; - width: 34px; - height: 20px; - cursor: pointer; - position: absolute; - top: 3px; - left: 3px; - z-index: 1; - background: #666; - border-radius: 50px; - transition: all 0.4s ease; - box-shadow: 0px 2px 5px 0px rgba(0, 0, 0, 0.3); + display: block; + width: 34px; + height: 20px; + cursor: pointer; + position: absolute; + top: 3px; + left: 3px; + z-index: 1; + background: var(--grey); + border-radius: 50px; + transition: all 0.4s ease; + box-shadow: 0px 2px 5px 0px rgba(0, 0, 0, 0.3); } [class*="web-to-plex"] .checkbox input[type=checkbox] { - visibility: hidden; + visibility: hidden; } [class*="web-to-plex"] .checkbox input[type=checkbox]:checked + label { - left: 43px; - background: #cc7b19; + left: 43px; + background: var(--orange); } [class*="web-to-plex"] .checkbox[disabled] { - opacity: 0.25 !important; + opacity: 0.25 !important; +} + +/* Minor Bugs */ +[glyph], [class*="glyphicon"] { + width: 1.1em; +} + +[glyph][gradient], [class*="glyphicon"][gradient] { + background-clip: text !important; + -moz-background-clip: text !important; + -webkit-background-clip: text !important; + + color: #0000 !important; + -webkit-text-fill-color: #0000 !important; } diff --git a/moz/couchpotato.css b/moz/couchpotato.css new file mode 100644 index 0000000..6e2a6b1 --- /dev/null +++ b/moz/couchpotato.css @@ -0,0 +1,23 @@ +.web-to-plex-minion.wtp--download { + color: var(--blue)!important; +} + +.web-to-plex-minion.wtp--download:hover { + color: var(--light-blue)!important; +} + +.web-to-plex-minion.wtp--found { + color: var(--orange)!important; +} + +.web-to-plex-minion.wtp--found:hover { + color: var(--light-orange)!important; +} + +#tt--0-0 { + color: var(--grey)!important; +} + +#tt--0-0:hover { + color: var(--light-grey)!important; +} diff --git a/moz/couchpotato.js b/moz/couchpotato.js index f8f6ddc..f6dee2b 100644 --- a/moz/couchpotato.js +++ b/moz/couchpotato.js @@ -37,4 +37,22 @@ let script = { .replace(/^.*imdb\.com\/title\//, '') .replace(/\/(?:maindetails\/?)?$/, ''); }, + + "minions": () => { + let actions = $('[href*="imdb.com/title/tt"] < *'); + + if(actions.empty) + return; + + actions.forEach(element => { + let minion; + let parent = furnish('span', {}, + furnish('img', { src: IMAGES.icon_16 }), + minion = furnish('a.web-to-plex-minion', {}, 'Web to Plex') + ); + + addMinions(minion); + element.appendChild(parent); + }); + }, }; diff --git a/moz/fandango.css b/moz/fandango.css new file mode 100644 index 0000000..5f5284d --- /dev/null +++ b/moz/fandango.css @@ -0,0 +1,27 @@ +.web-to-plex-minion { + color: #727272; +} + +.web-to-plex-minion.wtp--download { + color: #f45a26!important; +} + +.web-to-plex-minion.wtp--found { + color: #e5a00d!important; +} + +.web-to-plex-wrapper:hover, .web-to-plex-wrapper:hover > *, .web-to-plex-minion.wtp--download:hover { + color: #f67e56!important; +} + +.web-to-plex-minion.wtp--found:hover { + color: #f8c022!important; +} + +#tt--0-0 { + color: #666!important; +} + +#tt--0-0:hover { + color: #888!important; +} diff --git a/moz/fandango.js b/moz/fandango.js index 04a1a4e..de216c8 100644 --- a/moz/fandango.js +++ b/moz/fandango.js @@ -17,4 +17,21 @@ let script = { return { type, title, year, image }; }, + + "minions": () => { + let actions = $('.subnav ul'); + + if(actions.empty) + return; + + actions.forEach(element => { + let minion; + let parent = furnish('li.web-to-plex-wrapper.subnav__link-item', {}, + minion = furnish('a.web-to-plex-minion.subnav__link', {}, 'Web to Plex') + ); + + addMinions(minion); + element.appendChild(parent); + }); + }, }; diff --git a/moz/glyphs.css b/moz/glyphs.css new file mode 100644 index 0000000..31d5a74 --- /dev/null +++ b/moz/glyphs.css @@ -0,0 +1,2463 @@ +/** Available Icons: + * adjust + * airplane + * alarm + * albums + * amazon + * anchor + * android + * apple + * asterisk + * ax + * badoo + * ban + * bank + * barcode + * baseball + * basketball + * bathrobe + * beer + * behance + * bell + * bicycle + * bin + * binoculars + * blacksmith + * blog + * blogger + * bluetooth + * boat + * bold + * bomb + * book + * bookmark + * bowling + * briefcase + * brush + * bug + * building + * bullets + * bullhorn + * buoy + * bus + * cake + * calculator + * calendar + * camera + * candle + * car + * cardio + * cargo + * cars + * celebration + * certificate + * charts + * chat + * check + * cleaning + * clock + * cloud + * cogwheel + * cogwheels + * coins + * collapse + * comments + * compass + * compressed + * conversation + * crop + * crown + * cup + * cutlery + * dashboard + * delete + * deviantart + * direction + * dislikes + * display + * divide + * dog + * download + * dress + * dribbble + * drink + * dropbox + * dumbbell + * earphone + * edit + * eject + * electricity + * embed + * envelope + * euro + * evernote + * exit + * expand + * eyedropper + * fabric + * facebook + * factory + * fax + * female + * file + * film + * filter + * fins + * fire + * fishes + * flag + * flash + * flickr + * flower + * font + * forrst + * forward + * foursquare + * fullscreen + * gamepad + * gbp + * gift + * girl + * github + * glass + * global + * globe + * golf + * goodreads + * google_plus + * grater + * group + * hdd + * header + * headphones + * headset + * heart + * heat + * history + * hockey + * home + * hospital + * imac + * inbox + * instagram + * instapaper + * ios + * ipad + * iphone + * ipod + * italic + * jolicloud + * justify + * kettle + * keynote + * keys + * kiosk + * last_fm + * leaf + * leather + * lightbulb + * link + * linked_in + * list + * lock + * luggage + * macbook + * magic + * magnet + * male + * microphone + * minus + * money + * moon + * more + * move + * music + * mute + * myspace + * nails + * nameplate + * note + * notes + * ok + * package + * pants + * paperclip + * parents + * pause + * pen + * pencil + * piano + * picasa + * picture + * pin + * pinboard + * pinterest + * pipe + * pizza + * play + * playlist + * playstation + * plus + * podium + * pool + * posterous_spaces + * pot + * power + * print + * projector + * pushpin + * qrcode + * quora + * rabbit + * radar + * random + * read_it_later + * readability + * record + * redo + * refresh + * remove + * repeat + * restart + * retweet + * rewind + * riflescope + * ring + * road + * roundabout + * router + * rss + * rugby + * ruller + * sampler + * scissors + * screenshot + * search + * send + * server + * settings + * share + * shield + * shirt + * shop + * signal + * skateboard + * skitch + * skull + * skype + * smoking + * snowflake + * sort + * sorting + * spade + * spotify + * spray + * star + * stats + * stop + * stopwatch + * stroller + * stumbleupon + * subtitles + * suitcase + * sun + * sweater + * table + * tablet + * tag + * tags + * tie + * tint + * tower + * train + * transfer + * translate + * truck + * tumblr + * turtle + * twitter + * umbrella + * unchecked + * underwear + * undo + * unlock + * unshare + * upload + * usd + * user + * vases + * vcard + * vimeo + * vine + * wallet + * webcam + * wifi + * windows + * woman + * wordpress + * wrench + * xbox + * xing + * yahoo + * yelp + * youtube + * zootool + */ + +@charset "utf-8"; +@font-face { + font-family: Glyphicons Regular; + src: local(Glyphicons), + url(Glyphicons.woff) format("woff"), + url(//webtoplex.github.io/font/Glyphicons.woff) format("woff"); + font-weight: 400; + font-style: normal +} + +@font-face { + font-family: Glyphicons Social Regular; + src: local(Glyphicons Social), + url(Glyphicons Social.woff) format("woff"), + url(//webtoplex.github.io/font/Glyphicons Social.woff) format("woff"); + font-weight: 400; + font-style: normal +} + +[class*="glyphicon"i], [glyph] { + position: relative; + top: 1px; + display: inline-block; + font-family: Glyphicons Regular !important; + font-style: normal; + font-weight: 400; + line-height: 1; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale + width: 1em; +} + +.glyphicon-social, [glyph~="social"] { + font-family: Glyphicons Social Regular +} + +.glyphicon-lg, [glyph~="lg"] { + font-size: 1.33333333em; + line-height: .75em; + vertical-align: -15% +} + +.glyphicon-2x, [glyph~="2x"] { + font-size: 2em +} + +.glyphicon-3x, [glyph~="3x"] { + font-size: 3em +} + +.glyphicon-4x, [glyph~="4x"] { + font-size: 4em +} + +.glyphicon-5x, [glyph~="5x"] { + font-size: 5em +} + +[class*="glyphicon"].glass:before, [glyph~="glass"]:before { + content: "\e001" +} + +[class*="glyphicon"].leaf:before, [glyph~="leaf"]:before { + content: "\e002" +} + +[class*="glyphicon"].dog:before, [glyph~="dog"]:before { + content: "\1F415" +} + +[class*="glyphicon"].user:before, [glyph~="user"]:before { + content: "\e004" +} + +[class*="glyphicon"].girl:before, [glyph~="girl"]:before { + content: "\1F467" +} + +[class*="glyphicon"].car:before, [glyph~="car"]:before { + content: "\e006" +} + +[class*="glyphicon"].user-add:before { + content: "\e007" +} + +[class*="glyphicon"].user-remove:before { + content: "\e008" +} + +[class*="glyphicon"].film:before, [glyph~="film"]:before { + content: "\e009" +} + +[class*="glyphicon"].magic:before, [glyph~="magic"]:before { + content: "\e010" +} + +[class*="glyphicon"].envelope:before, [glyph~="envelope"]:before { + content: "\2709" +} + +[class*="glyphicon"].camera:before, [glyph~="camera"]:before { + content: "\1F4F7" +} + +[class*="glyphicon"].heart:before, [glyph~="heart"]:before { + content: "\e013" +} + +[class*="glyphicon"].beach-umbrella:before { + content: "\e014" +} + +[class*="glyphicon"].train:before, [glyph~="train"]:before { + content: "\1F686" +} + +[class*="glyphicon"].print:before, [glyph~="print"]:before { + content: "\e016" +} + +[class*="glyphicon"].bin:before, [glyph~="bin"]:before { + content: "\e017" +} + +[class*="glyphicon"].music:before, [glyph~="music"]:before { + content: "\e018" +} + +[class*="glyphicon"].note:before, [glyph~="note"]:before { + content: "\e019" +} + +[class*="glyphicon"].heart-empty:before { + content: "\e020" +} + +[class*="glyphicon"].home:before, [glyph~="home"]:before { + content: "\e021" +} + +[class*="glyphicon"].snowflake:before, [glyph~="snowflake"]:before { + content: "\2744" +} + +[class*="glyphicon"].fire:before, [glyph~="fire"]:before { + content: "\1F525" +} + +[class*="glyphicon"].magnet:before, [glyph~="magnet"]:before { + content: "\e024" +} + +[class*="glyphicon"].parents:before, [glyph~="parents"]:before { + content: "\e025" +} + +[class*="glyphicon"].binoculars:before, [glyph~="binoculars"]:before { + content: "\e026" +} + +[class*="glyphicon"].road:before, [glyph~="road"]:before { + content: "\e027" +} + +[class*="glyphicon"].search:before, [glyph~="search"]:before { + content: "\e028" +} + +[class*="glyphicon"].cars:before, [glyph~="cars"]:before { + content: "\e029" +} + +[class*="glyphicon"].notes-2:before { + content: "\e030" +} + +[class*="glyphicon"].pencil:before, [glyph~="pencil"]:before { + content: "\270F" +} + +[class*="glyphicon"].bus:before, [glyph~="bus"]:before { + content: "\1F68C" +} + +[class*="glyphicon"].wifi-alt:before { + content: "\e033" +} + +[class*="glyphicon"].luggage:before, [glyph~="luggage"]:before { + content: "\e034" +} + +[class*="glyphicon"].old-man:before { + content: "\e035" +} + +[class*="glyphicon"].woman:before, [glyph~="woman"]:before { + content: "\1F469" +} + +[class*="glyphicon"].file:before, [glyph~="file"]:before { + content: "\e037" +} + +[class*="glyphicon"].coins:before, [glyph~="coins"]:before { + content: "\e038" +} + +[class*="glyphicon"].airplane:before, [glyph~="airplane"]:before { + content: "\2708" +} + +[class*="glyphicon"].notes:before, [glyph~="notes"]:before { + content: "\e040" +} + +[class*="glyphicon"].stats:before, [glyph~="stats"]:before { + content: "\e041" +} + +[class*="glyphicon"].charts:before, [glyph~="charts"]:before { + content: "\e042" +} + +[class*="glyphicon"].pie-chart:before { + content: "\e043" +} + +[class*="glyphicon"].group:before, [glyph~="group"]:before { + content: "\e044" +} + +[class*="glyphicon"].keys:before, [glyph~="keys"]:before { + content: "\e045" +} + +[class*="glyphicon"].calendar:before, [glyph~="calendar"]:before { + content: "\1F4C5" +} + +[class*="glyphicon"].router:before, [glyph~="router"]:before { + content: "\e047" +} + +[class*="glyphicon"].camera-small:before { + content: "\e048" +} + +[class*="glyphicon"].dislikes:before, [glyph~="dislikes"]:before { + content: "\e049" +} + +[class*="glyphicon"].star:before, [glyph~="star"]:before { + content: "\e050" +} + +[class*="glyphicon"].link:before, [glyph~="link"]:before { + content: "\e051" +} + +[class*="glyphicon"].eye-open:before { + content: "\e052" +} + +[class*="glyphicon"].eye-close:before { + content: "\e053" +} + +[class*="glyphicon"].alarm:before, [glyph~="alarm"]:before { + content: "\e054" +} + +[class*="glyphicon"].clock:before, [glyph~="clock"]:before { + content: "\e055" +} + +[class*="glyphicon"].stopwatch:before, [glyph~="stopwatch"]:before { + content: "\e056" +} + +[class*="glyphicon"].projector:before, [glyph~="projector"]:before { + content: "\e057" +} + +[class*="glyphicon"].history:before, [glyph~="history"]:before { + content: "\e058" +} + +[class*="glyphicon"].truck:before, [glyph~="truck"]:before { + content: "\e059" +} + +[class*="glyphicon"].cargo:before, [glyph~="cargo"]:before { + content: "\e060" +} + +[class*="glyphicon"].compass:before, [glyph~="compass"]:before { + content: "\e061" +} + +[class*="glyphicon"].keynote:before, [glyph~="keynote"]:before { + content: "\e062" +} + +[class*="glyphicon"].paperclip:before, [glyph~="paperclip"]:before { + content: "\1F4CE" +} + +[class*="glyphicon"].power:before, [glyph~="power"]:before { + content: "\e064" +} + +[class*="glyphicon"].lightbulb:before, [glyph~="lightbulb"]:before { + content: "\e065" +} + +[class*="glyphicon"].tag:before, [glyph~="tag"]:before { + content: "\e066" +} + +[class*="glyphicon"].tags:before, [glyph~="tags"]:before { + content: "\e067" +} + +[class*="glyphicon"].cleaning:before, [glyph~="cleaning"]:before { + content: "\e068" +} + +[class*="glyphicon"].ruller:before, [glyph~="ruller"]:before { + content: "\e069" +} + +[class*="glyphicon"].gift:before, [glyph~="gift"]:before { + content: "\e070" +} + +[class*="glyphicon"].umbrella:before, [glyph~="umbrella"]:before { + content: "\2602" +} + +[class*="glyphicon"].book:before, [glyph~="book"]:before { + content: "\e072" +} + +[class*="glyphicon"].bookmark:before, [glyph~="bookmark"]:before { + content: "\1F516" +} + +[class*="glyphicon"].wifi:before, [glyph~="wifi"]:before { + content: "\e074" +} + +[class*="glyphicon"].cup:before, [glyph~="cup"]:before { + content: "\e075" +} + +[class*="glyphicon"].stroller:before, [glyph~="stroller"]:before { + content: "\e076" +} + +[class*="glyphicon"].headphones:before, [glyph~="headphones"]:before { + content: "\e077" +} + +[class*="glyphicon"].headset:before, [glyph~="headset"]:before { + content: "\e078" +} + +[class*="glyphicon"].warning-sign:before { + content: "\e079" +} + +[class*="glyphicon"].signal:before, [glyph~="signal"]:before { + content: "\e080" +} + +[class*="glyphicon"].retweet:before, [glyph~="retweet"]:before { + content: "\e081" +} + +[class*="glyphicon"].refresh:before, [glyph~="refresh"]:before { + content: "\e082" +} + +[class*="glyphicon"].roundabout:before, [glyph~="roundabout"]:before { + content: "\e083" +} + +[class*="glyphicon"].random:before, [glyph~="random"]:before { + content: "\e084" +} + +[class*="glyphicon"].heat:before, [glyph~="heat"]:before { + content: "\e085" +} + +[class*="glyphicon"].repeat:before, [glyph~="repeat"]:before { + content: "\e086" +} + +[class*="glyphicon"].display:before, [glyph~="display"]:before { + content: "\e087" +} + +[class*="glyphicon"].log-book:before { + content: "\e088" +} + +[class*="glyphicon"].address-book:before { + content: "\e089" +} + +[class*="glyphicon"].building:before, [glyph~="building"]:before { + content: "\e090" +} + +[class*="glyphicon"].eyedropper:before, [glyph~="eyedropper"]:before { + content: "\e091" +} + +[class*="glyphicon"].adjust:before, [glyph~="adjust"]:before { + content: "\e092" +} + +[class*="glyphicon"].tint:before, [glyph~="tint"]:before { + content: "\e093" +} + +[class*="glyphicon"].crop:before, [glyph~="crop"]:before { + content: "\e094" +} + +[class*="glyphicon"].vector-path-square:before { + content: "\e095" +} + +[class*="glyphicon"].vector-path-circle:before { + content: "\e096" +} + +[class*="glyphicon"].vector-path-polygon:before { + content: "\e097" +} + +[class*="glyphicon"].vector-path-line:before { + content: "\e098" +} + +[class*="glyphicon"].vector-path-curve:before { + content: "\e099" +} + +[class*="glyphicon"].vector-path-all:before { + content: "\e100" +} + +[class*="glyphicon"].font:before, [glyph~="font"]:before { + content: "\e101" +} + +[class*="glyphicon"].italic:before, [glyph~="italic"]:before { + content: "\e102" +} + +[class*="glyphicon"].bold:before, [glyph~="bold"]:before { + content: "\e103" +} + +[class*="glyphicon"].text-underline:before { + content: "\e104" +} + +[class*="glyphicon"].text-strike:before { + content: "\e105" +} + +[class*="glyphicon"].text-height:before { + content: "\e106" +} + +[class*="glyphicon"].text-width:before { + content: "\e107" +} + +[class*="glyphicon"].text-resize:before { + content: "\e108" +} + +[class*="glyphicon"].left-indent:before { + content: "\e109" +} + +[class*="glyphicon"].right-indent:before { + content: "\e110" +} + +[class*="glyphicon"].align-left:before { + content: "\e111" +} + +[class*="glyphicon"].align-center:before { + content: "\e112" +} + +[class*="glyphicon"].align-right:before { + content: "\e113" +} + +[class*="glyphicon"].justify:before, [glyph~="justify"]:before { + content: "\e114" +} + +[class*="glyphicon"].list:before, [glyph~="list"]:before { + content: "\e115" +} + +[class*="glyphicon"].text-smaller:before { + content: "\e116" +} + +[class*="glyphicon"].text-bigger:before { + content: "\e117" +} + +[class*="glyphicon"].embed:before, [glyph~="embed"]:before { + content: "\e118" +} + +[class*="glyphicon"].embed-close:before { + content: "\e119" +} + +[class*="glyphicon"].table:before, [glyph~="table"]:before { + content: "\e120" +} + +[class*="glyphicon"].message-full:before { + content: "\e121" +} + +[class*="glyphicon"].message-empty:before { + content: "\e122" +} + +[class*="glyphicon"].message-in:before { + content: "\e123" +} + +[class*="glyphicon"].message-out:before { + content: "\e124" +} + +[class*="glyphicon"].message-plus:before { + content: "\e125" +} + +[class*="glyphicon"].message-minus:before { + content: "\e126" +} + +[class*="glyphicon"].message-ban:before { + content: "\e127" +} + +[class*="glyphicon"].message-flag:before { + content: "\e128" +} + +[class*="glyphicon"].message-lock:before { + content: "\e129" +} + +[class*="glyphicon"].message-new:before { + content: "\e130" +} + +[class*="glyphicon"].inbox:before, [glyph~="inbox"]:before { + content: "\e131" +} + +[class*="glyphicon"].inbox-plus:before { + content: "\e132" +} + +[class*="glyphicon"].inbox-minus:before { + content: "\e133" +} + +[class*="glyphicon"].inbox-lock:before { + content: "\e134" +} + +[class*="glyphicon"].inbox-in:before { + content: "\e135" +} + +[class*="glyphicon"].inbox-out:before { + content: "\e136" +} + +[class*="glyphicon"].cogwheel:before, [glyph~="cogwheel"]:before { + content: "\e137" +} + +[class*="glyphicon"].cogwheels:before, [glyph~="cogwheels"]:before { + content: "\e138" +} + +[class*="glyphicon"].picture:before, [glyph~="picture"]:before { + content: "\e139" +} + +[class*="glyphicon"].adjust-alt:before { + content: "\e140" +} + +[class*="glyphicon"].database-lock:before { + content: "\e141" +} + +[class*="glyphicon"].database-plus:before { + content: "\e142" +} + +[class*="glyphicon"].database-minus:before { + content: "\e143" +} + +[class*="glyphicon"].database-ban:before { + content: "\e144" +} + +[class*="glyphicon"].folder-open:before { + content: "\e145" +} + +[class*="glyphicon"].folder-plus:before { + content: "\e146" +} + +[class*="glyphicon"].folder-minus:before { + content: "\e147" +} + +[class*="glyphicon"].folder-lock:before { + content: "\e148" +} + +[class*="glyphicon"].folder-flag:before { + content: "\e149" +} + +[class*="glyphicon"].folder-new:before { + content: "\e150" +} + +[class*="glyphicon"].edit:before, [glyph~="edit"]:before { + content: "\e151" +} + +[class*="glyphicon"].new-window:before { + content: "\e152" +} + +[class*="glyphicon"].check:before, [glyph~="check"]:before { + content: "\e153" +} + +[class*="glyphicon"].unchecked:before, [glyph~="unchecked"]:before { + content: "\e154" +} + +[class*="glyphicon"].more-windows:before { + content: "\e155" +} + +[class*="glyphicon"].show-big-thumbnails:before { + content: "\e156" +} + +[class*="glyphicon"].show-thumbnails:before { + content: "\e157" +} + +[class*="glyphicon"].show-thumbnails-with-lines:before { + content: "\e158" +} + +[class*="glyphicon"].show-lines:before { + content: "\e159" +} + +[class*="glyphicon"].playlist:before, [glyph~="playlist"]:before { + content: "\e160" +} + +[class*="glyphicon"].imac:before, [glyph~="imac"]:before { + content: "\e161" +} + +[class*="glyphicon"].macbook:before, [glyph~="macbook"]:before { + content: "\e162" +} + +[class*="glyphicon"].ipad:before, [glyph~="ipad"]:before { + content: "\e163" +} + +[class*="glyphicon"].iphone:before, [glyph~="iphone"]:before { + content: "\e164" +} + +[class*="glyphicon"].iphone-transfer:before { + content: "\e165" +} + +[class*="glyphicon"].iphone-exchange:before { + content: "\e166" +} + +[class*="glyphicon"].ipod:before, [glyph~="ipod"]:before { + content: "\e167" +} + +[class*="glyphicon"].ipod-shuffle:before { + content: "\e168" +} + +[class*="glyphicon"].ear-plugs:before { + content: "\e169" +} + +[class*="glyphicon"].record:before, [glyph~="record"]:before { + content: "\e170" +} + +[class*="glyphicon"].step-backward:before { + content: "\e171" +} + +[class*="glyphicon"].fast-backward:before { + content: "\e172" +} + +[class*="glyphicon"].rewind:before, [glyph~="rewind"]:before { + content: "\e173" +} + +[class*="glyphicon"].play:before, [glyph~="play"]:before { + content: "\e174" +} + +[class*="glyphicon"].pause:before, [glyph~="pause"]:before { + content: "\e175" +} + +[class*="glyphicon"].stop:before, [glyph~="stop"]:before { + content: "\e176" +} + +[class*="glyphicon"].forward:before, [glyph~="forward"]:before { + content: "\e177" +} + +[class*="glyphicon"].fast-forward:before { + content: "\e178" +} + +[class*="glyphicon"].step-forward:before { + content: "\e179" +} + +[class*="glyphicon"].eject:before, [glyph~="eject"]:before { + content: "\e180" +} + +[class*="glyphicon"].facetime-video:before { + content: "\e181" +} + +[class*="glyphicon"].download-alt:before { + content: "\e182" +} + +[class*="glyphicon"].mute:before, [glyph~="mute"]:before { + content: "\e183" +} + +[class*="glyphicon"].volume-down:before { + content: "\e184" +} + +[class*="glyphicon"].volume-up:before { + content: "\e185" +} + +[class*="glyphicon"].screenshot:before, [glyph~="screenshot"]:before { + content: "\e186" +} + +[class*="glyphicon"].move:before, [glyph~="move"]:before { + content: "\e187" +} + +[class*="glyphicon"].more:before, [glyph~="more"]:before { + content: "\e188" +} + +[class*="glyphicon"].brightness-reduce:before { + content: "\e189" +} + +[class*="glyphicon"].brightness-increase:before { + content: "\e190" +} + +[class*="glyphicon"].circle-plus:before { + content: "\e191" +} + +[class*="glyphicon"].circle-minus:before { + content: "\e192" +} + +[class*="glyphicon"].circle-remove:before { + content: "\e193" +} + +[class*="glyphicon"].circle-ok:before { + content: "\e194" +} + +[class*="glyphicon"].circle-question-mark:before { + content: "\e195" +} + +[class*="glyphicon"].circle-info:before { + content: "\e196" +} + +[class*="glyphicon"].circle-exclamation-mark:before { + content: "\e197" +} + +[class*="glyphicon"].remove:before, [glyph~="remove"]:before { + content: "\e198" +} + +[class*="glyphicon"].ok:before, [glyph~="ok"]:before { + content: "\e199" +} + +[class*="glyphicon"].ban:before, [glyph~="ban"]:before { + content: "\e200" +} + +[class*="glyphicon"].download:before, [glyph~="download"]:before { + content: "\e201" +} + +[class*="glyphicon"].upload:before, [glyph~="upload"]:before { + content: "\e202" +} + +[class*="glyphicon"].shopping-cart:before { + content: "\e203" +} + +[class*="glyphicon"].lock:before, [glyph~="lock"]:before { + content: "\1F512" +} + +[class*="glyphicon"].unlock:before, [glyph~="unlock"]:before { + content: "\e205" +} + +[class*="glyphicon"].electricity:before, [glyph~="electricity"]:before { + content: "\e206" +} + +[class*="glyphicon"].ok-2:before { + content: "\e207" +} + +[class*="glyphicon"].remove-2:before { + content: "\e208" +} + +[class*="glyphicon"].cart-out:before { + content: "\e209" +} + +[class*="glyphicon"].cart-in:before { + content: "\e210" +} + +[class*="glyphicon"].left-arrow:before { + content: "\e211" +} + +[class*="glyphicon"].right-arrow:before { + content: "\e212" +} + +[class*="glyphicon"].down-arrow:before { + content: "\e213" +} + +[class*="glyphicon"].up-arrow:before { + content: "\e214" +} + +[class*="glyphicon"].resize-small:before { + content: "\e215" +} + +[class*="glyphicon"].resize-full:before { + content: "\e216" +} + +[class*="glyphicon"].circle-arrow-left:before { + content: "\e217" +} + +[class*="glyphicon"].circle-arrow-right:before { + content: "\e218" +} + +[class*="glyphicon"].circle-arrow-top:before { + content: "\e219" +} + +[class*="glyphicon"].circle-arrow-down:before { + content: "\e220" +} + +[class*="glyphicon"].play-button:before { + content: "\e221" +} + +[class*="glyphicon"].unshare:before, [glyph~="unshare"]:before { + content: "\e222" +} + +[class*="glyphicon"].share:before, [glyph~="share"]:before { + content: "\e223" +} + +[class*="glyphicon"].chevron-right:before { + content: "\e224" +} + +[class*="glyphicon"].chevron-left:before { + content: "\e225" +} + +[class*="glyphicon"].chevron-up:before { + content: "\e224" +} + +[class*="glyphicon"].chevron-up { + -webkit-transform: rotate(-90deg); + -ms-transform: rotate(-90deg); + transform: rotate(-90deg) +} + +[class*="glyphicon"].chevron-down:before { + content: "\e224" +} + +[class*="glyphicon"].chevron-down { + -webkit-transform: rotate(90deg); + -ms-transform: rotate(90deg); + transform: rotate(90deg) +} + +[class*="glyphicon"].bluetooth:before, [glyph~="bluetooth"]:before { + content: "\e226" +} + +[class*="glyphicon"].euro:before, [glyph~="euro"]:before { + content: "\20AC" +} + +[class*="glyphicon"].usd:before, [glyph~="usd"]:before { + content: "\e228" +} + +[class*="glyphicon"].gbp:before, [glyph~="gbp"]:before { + content: "\e229" +} + +[class*="glyphicon"].retweet-2:before { + content: "\e230" +} + +[class*="glyphicon"].moon:before, [glyph~="moon"]:before { + content: "\e231" +} + +[class*="glyphicon"].sun:before, [glyph~="sun"]:before { + content: "\2609" +} + +[class*="glyphicon"].cloud:before, [glyph~="cloud"]:before { + content: "\2601" +} + +[class*="glyphicon"].direction:before, [glyph~="direction"]:before { + content: "\e234" +} + +[class*="glyphicon"].brush:before, [glyph~="brush"]:before { + content: "\e235" +} + +[class*="glyphicon"].pen:before, [glyph~="pen"]:before { + content: "\e236" +} + +[class*="glyphicon"].zoom-in:before { + content: "\e237" +} + +[class*="glyphicon"].zoom-out:before { + content: "\e238" +} + +[class*="glyphicon"].pin:before, [glyph~="pin"]:before { + content: "\e239" +} + +[class*="glyphicon"].albums:before, [glyph~="albums"]:before { + content: "\e240" +} + +[class*="glyphicon"].rotation-lock:before { + content: "\e241" +} + +[class*="glyphicon"].flash:before, [glyph~="flash"]:before { + content: "\e242" +} + +[class*="glyphicon"].google-maps:before { + content: "\e243" +} + +[class*="glyphicon"].anchor:before, [glyph~="anchor"]:before { + content: "\2693" +} + +[class*="glyphicon"].conversation:before, [glyph~="conversation"]:before { + content: "\e245" +} + +[class*="glyphicon"].chat:before, [glyph~="chat"]:before { + content: "\e246" +} + +[class*="glyphicon"].male:before, [glyph~="male"]:before { + content: "\e247" +} + +[class*="glyphicon"].female:before, [glyph~="female"]:before { + content: "\e248" +} + +[class*="glyphicon"].asterisk:before, [glyph~="asterisk"]:before { + content: "\002A" +} + +[class*="glyphicon"].divide:before, [glyph~="divide"]:before { + content: "\00F7" +} + +[class*="glyphicon"].snorkel-diving:before { + content: "\e251" +} + +[class*="glyphicon"].scuba-diving:before { + content: "\e252" +} + +[class*="glyphicon"].oxygen-bottle:before { + content: "\e253" +} + +[class*="glyphicon"].fins:before, [glyph~="fins"]:before { + content: "\e254" +} + +[class*="glyphicon"].fishes:before, [glyph~="fishes"]:before { + content: "\e255" +} + +[class*="glyphicon"].boat:before, [glyph~="boat"]:before { + content: "\e256" +} + +[class*="glyphicon"].delete:before, [glyph~="delete"]:before { + content: "\e257" +} + +[class*="glyphicon"].sheriffs-star:before { + content: "\e258" +} + +[class*="glyphicon"].qrcode:before, [glyph~="qrcode"]:before { + content: "\e259" +} + +[class*="glyphicon"].barcode:before, [glyph~="barcode"]:before { + content: "\e260" +} + +[class*="glyphicon"].pool:before, [glyph~="pool"]:before { + content: "\e261" +} + +[class*="glyphicon"].buoy:before, [glyph~="buoy"]:before { + content: "\e262" +} + +[class*="glyphicon"].spade:before, [glyph~="spade"]:before { + content: "\e263" +} + +[class*="glyphicon"].bank:before, [glyph~="bank"]:before { + content: "\1F3E6" +} + +[class*="glyphicon"].vcard:before, [glyph~="vcard"]:before { + content: "\e265" +} + +[class*="glyphicon"].electrical-plug:before { + content: "\e266" +} + +[class*="glyphicon"].flag:before, [glyph~="flag"]:before { + content: "\e267" +} + +[class*="glyphicon"].credit-card:before { + content: "\e268" +} + +[class*="glyphicon"].keyboard-wireless:before { + content: "\e269" +} + +[class*="glyphicon"].keyboard-wired:before { + content: "\e270" +} + +[class*="glyphicon"].shield:before, [glyph~="shield"]:before { + content: "\e271" +} + +[class*="glyphicon"].ring:before, [glyph~="ring"]:before { + content: "\02DA" +} + +[class*="glyphicon"].cake:before, [glyph~="cake"]:before { + content: "\e273" +} + +[class*="glyphicon"].drink:before, [glyph~="drink"]:before { + content: "\e274" +} + +[class*="glyphicon"].beer:before, [glyph~="beer"]:before { + content: "\e275" +} + +[class*="glyphicon"].fast-food:before { + content: "\e276" +} + +[class*="glyphicon"].cutlery:before, [glyph~="cutlery"]:before { + content: "\e277" +} + +[class*="glyphicon"].pizza:before, [glyph~="pizza"]:before { + content: "\e278" +} + +[class*="glyphicon"].birthday-cake:before { + content: "\e279" +} + +[class*="glyphicon"].tablet:before, [glyph~="tablet"]:before { + content: "\e280" +} + +[class*="glyphicon"].settings:before, [glyph~="settings"]:before { + content: "\e281" +} + +[class*="glyphicon"].bullets:before, [glyph~="bullets"]:before { + content: "\e282" +} + +[class*="glyphicon"].cardio:before, [glyph~="cardio"]:before { + content: "\e283" +} + +[class*="glyphicon"].t-shirt:before { + content: "\e284" +} + +[class*="glyphicon"].pants:before, [glyph~="pants"]:before { + content: "\e285" +} + +[class*="glyphicon"].sweater:before, [glyph~="sweater"]:before { + content: "\e286" +} + +[class*="glyphicon"].fabric:before, [glyph~="fabric"]:before { + content: "\e287" +} + +[class*="glyphicon"].leather:before, [glyph~="leather"]:before { + content: "\e288" +} + +[class*="glyphicon"].scissors:before, [glyph~="scissors"]:before { + content: "\e289" +} + +[class*="glyphicon"].bomb:before, [glyph~="bomb"]:before { + content: "\1F4A3" +} + +[class*="glyphicon"].skull:before, [glyph~="skull"]:before { + content: "\1F480" +} + +[class*="glyphicon"].celebration:before, [glyph~="celebration"]:before { + content: "\e292" +} + +[class*="glyphicon"].tea-kettle:before { + content: "\e293" +} + +[class*="glyphicon"].french-press:before { + content: "\e294" +} + +[class*="glyphicon"].coffe-cup:before { + content: "\e295" +} + +[class*="glyphicon"].pot:before, [glyph~="pot"]:before { + content: "\e296" +} + +[class*="glyphicon"].grater:before, [glyph~="grater"]:before { + content: "\e297" +} + +[class*="glyphicon"].kettle:before, [glyph~="kettle"]:before { + content: "\e298" +} + +[class*="glyphicon"].hospital:before, [glyph~="hospital"]:before { + content: "\1F3E5" +} + +[class*="glyphicon"].hospital-h:before { + content: "\e300" +} + +[class*="glyphicon"].microphone:before, [glyph~="microphone"]:before { + content: "\1F3A4" +} + +[class*="glyphicon"].webcam:before, [glyph~="webcam"]:before { + content: "\e302" +} + +[class*="glyphicon"].temple-christianity-church:before { + content: "\e303" +} + +[class*="glyphicon"].temple-islam:before { + content: "\e304" +} + +[class*="glyphicon"].temple-hindu:before { + content: "\e305" +} + +[class*="glyphicon"].temple-buddhist:before { + content: "\e306" +} + +[class*="glyphicon"].bicycle:before, [glyph~="bicycle"]:before { + content: "\1F6B2" +} + +[class*="glyphicon"].life-preserver:before { + content: "\e308" +} + +[class*="glyphicon"].share-alt:before { + content: "\e309" +} + +[class*="glyphicon"].comments:before, [glyph~="comments"]:before { + content: "\e310" +} + +[class*="glyphicon"].flower:before, [glyph~="flower"]:before { + content: "\2698" +} + +[class*="glyphicon"].baseball:before, [glyph~="baseball"]:before { + content: "\26BE" +} + +[class*="glyphicon"].rugby:before, [glyph~="rugby"]:before { + content: "\e313" +} + +[class*="glyphicon"].ax:before, [glyph~="ax"]:before { + content: "\e314" +} + +[class*="glyphicon"].table-tennis:before { + content: "\e315" +} + +[class*="glyphicon"].bowling:before, [glyph~="bowling"]:before { + content: "\1F3B3" +} + +[class*="glyphicon"].tree-conifer:before { + content: "\e317" +} + +[class*="glyphicon"].tree-deciduous:before { + content: "\e318" +} + +[class*="glyphicon"].more-items:before { + content: "\e319" +} + +[class*="glyphicon"].sort:before, [glyph~="sort"]:before { + content: "\e320" +} + +[class*="glyphicon"].filter:before, [glyph~="filter"]:before { + content: "\e321" +} + +[class*="glyphicon"].gamepad:before, [glyph~="gamepad"]:before { + content: "\e322" +} + +[class*="glyphicon"].playing-dices:before { + content: "\e323" +} + +[class*="glyphicon"].calculator:before, [glyph~="calculator"]:before { + content: "\e324" +} + +[class*="glyphicon"].tie:before, [glyph~="tie"]:before { + content: "\e325" +} + +[class*="glyphicon"].wallet:before, [glyph~="wallet"]:before { + content: "\e326" +} + +[class*="glyphicon"].piano:before, [glyph~="piano"]:before { + content: "\e327" +} + +[class*="glyphicon"].sampler:before, [glyph~="sampler"]:before { + content: "\e328" +} + +[class*="glyphicon"].podium:before, [glyph~="podium"]:before { + content: "\e329" +} + +[class*="glyphicon"].soccer-ball:before { + content: "\e330" +} + +[class*="glyphicon"].blog:before, [glyph~="blog"]:before { + content: "\e331" +} + +[class*="glyphicon"].dashboard:before, [glyph~="dashboard"]:before { + content: "\e332" +} + +[class*="glyphicon"].certificate:before, [glyph~="certificate"]:before { + content: "\e333" +} + +[class*="glyphicon"].bell:before, [glyph~="bell"]:before { + content: "\1F514" +} + +[class*="glyphicon"].candle:before, [glyph~="candle"]:before { + content: "\e335" +} + +[class*="glyphicon"].pushpin:before, [glyph~="pushpin"]:before { + content: "\1F4CC" +} + +[class*="glyphicon"].iphone-shake:before { + content: "\e337" +} + +[class*="glyphicon"].pin-flag:before { + content: "\e338" +} + +[class*="glyphicon"].turtle:before, [glyph~="turtle"]:before { + content: "\1F422" +} + +[class*="glyphicon"].rabbit:before, [glyph~="rabbit"]:before { + content: "\1F407" +} + +[class*="glyphicon"].globe:before, [glyph~="globe"]:before { + content: "\e341" +} + +[class*="glyphicon"].briefcase:before, [glyph~="briefcase"]:before { + content: "\1F4BC" +} + +[class*="glyphicon"].hdd:before, [glyph~="hdd"]:before { + content: "\e343" +} + +[class*="glyphicon"].thumbs-up:before { + content: "\e344" +} + +[class*="glyphicon"].thumbs-down:before { + content: "\e345" +} + +[class*="glyphicon"].hand-right:before { + content: "\e346" +} + +[class*="glyphicon"].hand-left:before { + content: "\e347" +} + +[class*="glyphicon"].hand-up:before { + content: "\e348" +} + +[class*="glyphicon"].hand-down:before { + content: "\e349" +} + +[class*="glyphicon"].fullscreen:before, [glyph~="fullscreen"]:before { + content: "\e350" +} + +[class*="glyphicon"].shopping-bag:before { + content: "\e351" +} + +[class*="glyphicon"].book-open:before { + content: "\e352" +} + +[class*="glyphicon"].nameplate:before, [glyph~="nameplate"]:before { + content: "\e353" +} + +[class*="glyphicon"].nameplate-alt:before { + content: "\e354" +} + +[class*="glyphicon"].vases:before, [glyph~="vases"]:before { + content: "\e355" +} + +[class*="glyphicon"].bullhorn:before, [glyph~="bullhorn"]:before { + content: "\e356" +} + +[class*="glyphicon"].dumbbell:before, [glyph~="dumbbell"]:before { + content: "\e357" +} + +[class*="glyphicon"].suitcase:before, [glyph~="suitcase"]:before { + content: "\e358" +} + +[class*="glyphicon"].file-import:before { + content: "\e359" +} + +[class*="glyphicon"].file-export:before { + content: "\e360" +} + +[class*="glyphicon"].bug:before, [glyph~="bug"]:before { + content: "\1F41B" +} + +[class*="glyphicon"].crown:before, [glyph~="crown"]:before { + content: "\1F451" +} + +[class*="glyphicon"].smoking:before, [glyph~="smoking"]:before { + content: "\e363" +} + +[class*="glyphicon"].cloud-upload:before { + content: "\e364" +} + +[class*="glyphicon"].cloud-download:before { + content: "\e365" +} + +[class*="glyphicon"].restart:before, [glyph~="restart"]:before { + content: "\e366" +} + +[class*="glyphicon"].security-camera:before { + content: "\e367" +} + +[class*="glyphicon"].expand:before, [glyph~="expand"]:before { + content: "\e368" +} + +[class*="glyphicon"].collapse:before, [glyph~="collapse"]:before { + content: "\e369" +} + +[class*="glyphicon"].collapse-top:before { + content: "\e370" +} + +[class*="glyphicon"].globe-af:before { + content: "\e371" +} + +[class*="glyphicon"].global:before, [glyph~="global"]:before { + content: "\e372" +} + +[class*="glyphicon"].spray:before, [glyph~="spray"]:before { + content: "\e373" +} + +[class*="glyphicon"].nails:before, [glyph~="nails"]:before { + content: "\e374" +} + +[class*="glyphicon"].claw-hammer:before { + content: "\e375" +} + +[class*="glyphicon"].classic-hammer:before { + content: "\e376" +} + +[class*="glyphicon"].hand-saw:before { + content: "\e377" +} + +[class*="glyphicon"].riflescope:before, [glyph~="riflescope"]:before { + content: "\e378" +} + +[class*="glyphicon"].electrical-socket-eu:before { + content: "\e379" +} + +[class*="glyphicon"].electrical-socket-us:before { + content: "\e380" +} + +[class*="glyphicon"].message-forward:before { + content: "\e381" +} + +[class*="glyphicon"].coat-hanger:before { + content: "\e382" +} + +[class*="glyphicon"].dress:before, [glyph~="dress"]:before { + content: "\1F457" +} + +[class*="glyphicon"].bathrobe:before, [glyph~="bathrobe"]:before { + content: "\e384" +} + +[class*="glyphicon"].shirt:before, [glyph~="shirt"]:before { + content: "\e385" +} + +[class*="glyphicon"].underwear:before, [glyph~="underwear"]:before { + content: "\e386" +} + +[class*="glyphicon"].log-in:before { + content: "\e387" +} + +[class*="glyphicon"].log-out:before { + content: "\e388" +} + +[class*="glyphicon"].exit:before, [glyph~="exit"]:before { + content: "\e389" +} + +[class*="glyphicon"].new-window-alt:before { + content: "\e390" +} + +[class*="glyphicon"].video-sd:before { + content: "\e391" +} + +[class*="glyphicon"].video-hd:before { + content: "\e392" +} + +[class*="glyphicon"].subtitles:before, [glyph~="subtitles"]:before { + content: "\e393" +} + +[class*="glyphicon"].sound-stereo:before { + content: "\e394" +} + +[class*="glyphicon"].sound-dolby:before { + content: "\e395" +} + +[class*="glyphicon"].sound-5-1:before { + content: "\e396" +} + +[class*="glyphicon"].sound-6-1:before { + content: "\e397" +} + +[class*="glyphicon"].sound-7-1:before { + content: "\e398" +} + +[class*="glyphicon"].copyright-mark:before { + content: "\e399" +} + +[class*="glyphicon"].registration-mark:before { + content: "\e400" +} + +[class*="glyphicon"].radar:before, [glyph~="radar"]:before { + content: "\e401" +} + +[class*="glyphicon"].skateboard:before, [glyph~="skateboard"]:before { + content: "\e402" +} + +[class*="glyphicon"].golf-course:before { + content: "\e403" +} + +[class*="glyphicon"].sorting:before, [glyph~="sorting"]:before { + content: "\e404" +} + +[class*="glyphicon"].sort-by-alphabet:before { + content: "\e405" +} + +[class*="glyphicon"].sort-by-alphabet-alt:before { + content: "\e406" +} + +[class*="glyphicon"].sort-by-order:before { + content: "\e407" +} + +[class*="glyphicon"].sort-by-order-alt:before { + content: "\e408" +} + +[class*="glyphicon"].sort-by-attributes:before { + content: "\e409" +} + +[class*="glyphicon"].sort-by-attributes-alt:before { + content: "\e410" +} + +[class*="glyphicon"].compressed:before, [glyph~="compressed"]:before { + content: "\e411" +} + +[class*="glyphicon"].package:before, [glyph~="package"]:before { + content: "\1F4E6" +} + +[class*="glyphicon"].cloud-plus:before { + content: "\e413" +} + +[class*="glyphicon"].cloud-minus:before { + content: "\e414" +} + +[class*="glyphicon"].disk-save:before { + content: "\e415" +} + +[class*="glyphicon"].disk-open:before { + content: "\e416" +} + +[class*="glyphicon"].disk-saved:before { + content: "\e417" +} + +[class*="glyphicon"].disk-remove:before { + content: "\e418" +} + +[class*="glyphicon"].disk-import:before { + content: "\e419" +} + +[class*="glyphicon"].disk-export:before { + content: "\e420" +} + +[class*="glyphicon"].tower:before, [glyph~="tower"]:before { + content: "\e421" +} + +[class*="glyphicon"].send:before, [glyph~="send"]:before { + content: "\e422" +} + +[class*="glyphicon"].git-branch:before { + content: "\e423" +} + +[class*="glyphicon"].git-create:before { + content: "\e424" +} + +[class*="glyphicon"].git-private:before { + content: "\e425" +} + +[class*="glyphicon"].git-delete:before { + content: "\e426" +} + +[class*="glyphicon"].git-merge:before { + content: "\e427" +} + +[class*="glyphicon"].git-pull-request:before { + content: "\e428" +} + +[class*="glyphicon"].git-compare:before { + content: "\e429" +} + +[class*="glyphicon"].git-commit:before { + content: "\e430" +} + +[class*="glyphicon"].construction-cone:before { + content: "\e431" +} + +[class*="glyphicon"].shoe-steps:before { + content: "\e432" +} + +[class*="glyphicon"].plus:before, [glyph~="plus"]:before { + content: "\002B" +} + +[class*="glyphicon"].minus:before, [glyph~="minus"]:before { + content: "\2212" +} + +[class*="glyphicon"].redo:before, [glyph~="redo"]:before { + content: "\e435" +} + +[class*="glyphicon"].undo:before, [glyph~="undo"]:before { + content: "\e436" +} + +[class*="glyphicon"].golf:before, [glyph~="golf"]:before { + content: "\e437" +} + +[class*="glyphicon"].hockey:before, [glyph~="hockey"]:before { + content: "\e438" +} + +[class*="glyphicon"].pipe:before, [glyph~="pipe"]:before { + content: "\e439" +} + +[class*="glyphicon"].wrench:before, [glyph~="wrench"]:before { + content: "\1F527" +} + +[class*="glyphicon"].folder-closed:before { + content: "\e441" +} + +[class*="glyphicon"].phone-alt:before { + content: "\e442" +} + +[class*="glyphicon"].earphone:before, [glyph~="earphone"]:before { + content: "\e443" +} + +[class*="glyphicon"].floppy-disk:before { + content: "\e444" +} + +[class*="glyphicon"].floppy-saved:before { + content: "\e445" +} + +[class*="glyphicon"].floppy-remove:before { + content: "\e446" +} + +[class*="glyphicon"].floppy-save:before { + content: "\e447" +} + +[class*="glyphicon"].floppy-open:before { + content: "\e448" +} + +[class*="glyphicon"].translate:before, [glyph~="translate"]:before { + content: "\e449" +} + +[class*="glyphicon"].fax:before, [glyph~="fax"]:before { + content: "\e450" +} + +[class*="glyphicon"].factory:before, [glyph~="factory"]:before { + content: "\1F3ED" +} + +[class*="glyphicon"].shop-window:before { + content: "\e452" +} + +[class*="glyphicon"].shop:before, [glyph~="shop"]:before { + content: "\e453" +} + +[class*="glyphicon"].kiosk:before, [glyph~="kiosk"]:before { + content: "\e454" +} + +[class*="glyphicon"].kiosk-wheels:before { + content: "\e455" +} + +[class*="glyphicon"].kiosk-light:before { + content: "\e456" +} + +[class*="glyphicon"].kiosk-food:before { + content: "\e457" +} + +[class*="glyphicon"].transfer:before, [glyph~="transfer"]:before { + content: "\e458" +} + +[class*="glyphicon"].money:before, [glyph~="money"]:before { + content: "\e459" +} + +[class*="glyphicon"].header:before, [glyph~="header"]:before { + content: "\e460" +} + +[class*="glyphicon"].blacksmith:before, [glyph~="blacksmith"]:before { + content: "\e461" +} + +[class*="glyphicon"].saw-blade:before { + content: "\e462" +} + +[class*="glyphicon"].basketball:before, [glyph~="basketball"]:before { + content: "\e463" +} + +[class*="glyphicon"].server:before, [glyph~="server"]:before { + content: "\e464" +} + +[class*="glyphicon"].server-plus:before { + content: "\e465" +} + +[class*="glyphicon"].server-minus:before { + content: "\e466" +} + +[class*="glyphicon"].server-ban:before { + content: "\e467" +} + +[class*="glyphicon"].server-flag:before { + content: "\e468" +} + +[class*="glyphicon"].server-lock:before { + content: "\e469" +} + +[class*="glyphicon"].server-new:before { + content: "\e470" +} + +.glyphicon-social.pinterest:before, [glyph~="social pinterest"]:before { + content: "\e001" +} + +.glyphicon-social.dropbox:before, [glyph~="social dropbox"]:before { + content: "\e002" +} + +.glyphicon-social.google_plus:before, [glyph~="social google_plus"]:before { + content: "\e003" +} + +.glyphicon-social.jolicloud:before, [glyph~="social jolicloud"]:before { + content: "\e004" +} + +.glyphicon-social.yahoo:before, [glyph~="social yahoo"]:before { + content: "\e005" +} + +.glyphicon-social.blogger:before, [glyph~="social blogger"]:before { + content: "\e006" +} + +.glyphicon-social.picasa:before, [glyph~="social picasa"]:before { + content: "\e007" +} + +.glyphicon-social.amazon:before, [glyph~="social amazon"]:before { + content: "\e008" +} + +.glyphicon-social.tumblr:before, [glyph~="social tumblr"]:before { + content: "\e009" +} + +.glyphicon-social.wordpress:before, [glyph~="social wordpress"]:before { + content: "\e010" +} + +.glyphicon-social.instapaper:before, [glyph~="social instapaper"]:before { + content: "\e011" +} + +.glyphicon-social.evernote:before, [glyph~="social evernote"]:before { + content: "\e012" +} + +.glyphicon-social.xing:before, [glyph~="social xing"]:before { + content: "\e013" +} + +.glyphicon-social.zootool:before, [glyph~="social zootool"]:before { + content: "\e014" +} + +.glyphicon-social.dribbble:before, [glyph~="social dribbble"]:before { + content: "\e015" +} + +.glyphicon-social.deviantart:before, [glyph~="social deviantart"]:before { + content: "\e016" +} + +.glyphicon-social.read_it_later:before, [glyph~="social read_it_later"]:before { + content: "\e017" +} + +.glyphicon-social.linked_in:before, [glyph~="social linked_in"]:before { + content: "\e018" +} + +.glyphicon-social.forrst:before, [glyph~="social forrst"]:before { + content: "\e019" +} + +.glyphicon-social.pinboard:before, [glyph~="social pinboard"]:before { + content: "\e020" +} + +.glyphicon-social.behance:before, [glyph~="social behance"]:before { + content: "\e021" +} + +.glyphicon-social.github:before, [glyph~="social github"]:before { + content: "\e022" +} + +.glyphicon-social.youtube:before, [glyph~="social youtube"]:before { + content: "\e023" +} + +.glyphicon-social.skitch:before, [glyph~="social skitch"]:before { + content: "\e024" +} + +.glyphicon-social.foursquare:before, [glyph~="social foursquare"]:before { + content: "\e025" +} + +.glyphicon-social.quora:before, [glyph~="social quora"]:before { + content: "\e026" +} + +.glyphicon-social.badoo:before, [glyph~="social badoo"]:before { + content: "\e027" +} + +.glyphicon-social.spotify:before, [glyph~="social spotify"]:before { + content: "\e028" +} + +.glyphicon-social.stumbleupon:before, [glyph~="social stumbleupon"]:before { + content: "\e029" +} + +.glyphicon-social.readability:before, [glyph~="social readability"]:before { + content: "\e030" +} + +.glyphicon-social.facebook:before, [glyph~="social facebook"]:before { + content: "\e031" +} + +.glyphicon-social.twitter:before, [glyph~="social twitter"]:before { + content: "\e032" +} + +.glyphicon-social.instagram:before, [glyph~="social instagram"]:before { + content: "\e033" +} + +.glyphicon-social.posterous_spaces:before, [glyph~="social posterous_spaces"]:before { + content: "\e034" +} + +.glyphicon-social.vimeo:before, [glyph~="social vimeo"]:before { + content: "\e035" +} + +.glyphicon-social.flickr:before, [glyph~="social flickr"]:before { + content: "\e036" +} + +.glyphicon-social.last_fm:before, [glyph~="social last_fm"]:before { + content: "\e037" +} + +.glyphicon-social.rss:before, [glyph~="social rss"]:before { + content: "\e038" +} + +.glyphicon-social.skype:before, [glyph~="social skype"]:before { + content: "\e039" +} + +.glyphicon-social.e-mail:before { + content: "\e040" +} + +.glyphicon-social.vine:before, [glyph~="social vine"]:before { + content: "\e041" +} + +.glyphicon-social.myspace:before, [glyph~="social myspace"]:before { + content: "\e042" +} + +.glyphicon-social.goodreads:before, [glyph~="social goodreads"]:before { + content: "\e043" +} + +.glyphicon-social.apple:before, [glyph~="social apple"]:before { + content: "\F8FF" +} + +.glyphicon-social.windows:before, [glyph~="social windows"]:before { + content: "\e045" +} + +.glyphicon-social.yelp:before, [glyph~="social yelp"]:before { + content: "\e046" +} + +.glyphicon-social.playstation:before, [glyph~="social playstation"]:before { + content: "\e047" +} + +.glyphicon-social.xbox:before, [glyph~="social xbox"]:before { + content: "\e048" +} + +.glyphicon-social.android:before, [glyph~="social android"]:before { + content: "\e049" +} + +.glyphicon-social.ios:before, [glyph~="social ios"]:before { + content: "\e050" +} diff --git a/moz/google.css b/moz/google.css new file mode 100644 index 0000000..b800ac7 --- /dev/null +++ b/moz/google.css @@ -0,0 +1,68 @@ +.wtp-w { + display: inline-block; + margin: 8px 0 0 4px; + transition: all 0.2s!important; +} + +.wtp-w, .web-to-plex-minion { + color: #ffffff!important; +} + +.web-to-plex-minion { + background: rgba(0,0,0,0)!important; +} + +.wtp-b { + background-color: #e5a00d!important; + line-height: 36px; + border-radius: 4px; + border: 1px; + font-size: 14px; + height: 36px; + padding: 0 20px; + box-shadow: 0 1px 0 rgba(0,0,0,0.05); + box-sizing: border-box; + cursor: pointer; + display: inline-block; + font-style: normal; + font-weight: 500; + min-width: 40px; + position: relative; + text-align: center; + text-decoration: none; + white-space: no-wrap; + vertical-align: middle; +} + +.wtp-b:hover { + background-color: #fbc022!important; + box-shadow: inset 0 -2px 0 rgba(0,0,0,0.27); +} + +.wtp--download .wtp-b { + background-color: var(--blue)!important; +} + +.wtp--download .wtp-b:hover { + background-color: var(--light-blue)!important; +} + +.wtp--found .wtp-b { + background-color: var(--orange)!important; +} + +.wtp--found .wtp-b:hover { + background-color: var(--light-orange)!important; +} + +.wtp-b:empty { + display: none!important; +} + +#tt--0-0 { + color: #666!important; +} + +#tt--0-0:hover { + color: #888!important; +} diff --git a/moz/google.play.js b/moz/google.play.js index 603e989..1e2c0b8 100644 --- a/moz/google.play.js +++ b/moz/google.play.js @@ -21,4 +21,46 @@ let script = { 'movie': 'show' ), + + "minions": () => { + let type = script.getType(); + + let actions = $('wishlist-add, wishlist-added'); + + if(actions.empty) + return; + + + actions.forEach(element => { + while(/c-wiz/i.test(element.parentElement.tagName)) + element = element.parentElement; + element = element.parentElement; + + let next, first, second; + + if(type == 'movie') { + next = element.nextElementSibling.firstElementChild; + first = next.firstChild; + second = first.firstChild; + } else { + next = furnish('div', {}, + first = furnish('span.wtp-w', {}, + second = furnish('button.wtp-b') + ) + ); + + element.appendChild(next); + } + + let minion; + let parent = furnish(`span.${['wtp-w', ...first.classList].join('.')}`, {}, + furnish(`button.${['wtp-b', ...second.classList].join('.')}`, {}, + minion = furnish('a.web-to-plex-minion', {}, 'Web to Plex') + ) + ); + + addMinions(parent, minion); + next.insertBefore(parent, first); + }); + }, }; diff --git a/moz/helpers.js b/moz/helpers.js index 033212b..df07bf6 100644 --- a/moz/helpers.js +++ b/moz/helpers.js @@ -1,8 +1,10 @@ +function $(selector, container = document) { + return queryBy(selector, container); +} + async function load(name = '') { if(!name) return; - let HELPERS_STORAGE = chrome.storage.sync || chrome.storage.local; - name = '~/cache/' + (name.toLowerCase().replace(/\s+/g, '_')); return new Promise((resolve, reject) => { @@ -12,21 +14,13 @@ async function load(name = '') { return resolve(data); } - HELPERS_STORAGE.get(null, DISK => { - if(chrome.runtime.lastError) - chrome.runtime.lastError.message || - chrome.storage.local.get(null, LOAD); - else - LOAD(DISK); - }); + HELPERS_STORAGE.get(null, DISK => LOAD(DISK)); }); } async function save(name = '', data) { if(!name) return; - let HELPERS_STORAGE = chrome.storage.sync || chrome.storage.local; - name = '~/cache/' + (name.toLowerCase().replace(/\s+/g, '_')); data = JSON.stringify(data); @@ -36,8 +30,6 @@ async function save(name = '', data) { } async function kill(name) { - let HELPERS_STORAGE = chrome.storage.sync || chrome.storage.local; - return HELPERS_STORAGE.remove(['~/cache/' + (name.toLowerCase().replace(/\s+/g, '_'))]); } diff --git a/moz/hulu.css b/moz/hulu.css new file mode 100644 index 0000000..f75c93d --- /dev/null +++ b/moz/hulu.css @@ -0,0 +1,37 @@ +.web-to-plex-minion { + background: 0 !important; + border: 0 !important; + display: inline-block !important; + color: #e6e6e6 !important; + font-size: 12px !important; + height: 20px !important; + text-decoration: none !important; + user-select: none !important; + vertical-align: top !important; + margin-top: 35%!important; + outline: #0000!important; +} + +.web-to-plex-minion.wtp--download { + color: #f45a26 !important; +} + +.web-to-plex-minion.wtp--download:hover { + color: #f67e56 !important; +} + +.web-to-plex-minion.wtp--found { + color: #e5a00d !important; +} + +.web-to-plex-minion.wtp--found:hover { + color: #f9be03 !important; +} + +#tt--0-0 { + color: #666 !important; +} + +#tt--0-0:hover { + color: #888 !important; +} diff --git a/moz/hulu.js b/moz/hulu.js index 89a8081..99ad143 100644 --- a/moz/hulu.js +++ b/moz/hulu.js @@ -45,4 +45,25 @@ let script = { 'show'; } }, + + "minions": () => { + let actions = $('.Details > .SimpleModalNav'); + + if(actions.empty) + return; + + actions.forEach(element => { + let minion, + sibling = $('.Nav__spacer ~ .Nav__item', element).last; + + let parent = furnish('div.Nav__item', {}, + minion = furnish('button.web-to-plex-minion', {}, + furnish('img', { src: IMAGES.icon_32 }) + ) + ); + + addMinions(minion); + element.insertBefore(parent, sibling); + }); + }, }; diff --git a/moz/imdb.css b/moz/imdb.css new file mode 100644 index 0000000..48c3858 --- /dev/null +++ b/moz/imdb.css @@ -0,0 +1,78 @@ +.web-to-plex-minion { + background: #727272; + border-radius: 1000px; + color: #fff!important; + display: inline-block; + text-decoration: none!important; + text-transform: uppercase; + + margin-top: 10px; + padding: 5px 10px; +} + +.web-to-plex-minion.wtp--queued { + background: var(--light-blue)!important; +} + +.web-to-plex-minion.wtp--queued:hover { + background: var(--blue)!important; +} + +.web-to-plex-minion.wtp--download { + background: var(--dark-blue)!important; +} + +.web-to-plex-minion.wtp--download:hover { + background: var(--blue)!important; +} + +.web-to-plex-minion.wtp--found { + background: var(--orange)!important; +} + +.web-to-plex-minion.wtp--found:hover { + background: var(--light-orange)!important; +} + +.lister-list .web-to-plex-minion { + background: 0!important; + border: 0!important; + border-bottom: 5px solid #727272!important; + border-radius: 0!important; + font-size: 10px; + + margin-top: 0; + padding: 4px 6px; +} + +.lister-list .web-to-plex-minion.wtp--queued { + border-color: var(--light-blue)!important; +} + +.lister-list .web-to-plex-minion.wtp--queued:hover { + border-color: var(--blue)!important; +} + +.lister-list .web-to-plex-minion.wtp--download { + border-color: var(--dark-blue)!important; +} + +.lister-list .web-to-plex-minion.wtp--download:hover { + border-color: var(--blue)!important; +} + +.lister-list .web-to-plex-minion.wtp--found { + border-color: var(--orange)!important; +} + +.lister-list .web-to-plex-minion.wtp--found:hover { + border-color: var(--light-orange)!important; +} + +#tt--0-0 { + background: var(--grey)!important; +} + +#tt--0-0:hover { + background: var(--light-grey)!important; +} diff --git a/moz/imdb.js b/moz/imdb.js index dc60999..a187f60 100644 --- a/moz/imdb.js +++ b/moz/imdb.js @@ -116,4 +116,40 @@ let script = { }, "clean": year => (year + '').replace(/^\(|\)$/g, '').trim(), + + "minions": () => { + let type = script.getType(), + actions; + + if(type == 'list') + actions = $('.lister-list .lister-col-wrapper'); + else + actions = $('.plot_summary'); + + if(actions.empty) + return; + + actions.forEach(element => { + let minion; + + if(type == 'list') { + let processed = script.process(element.parentElement.parentElement); + + if(!processed) + return; + + minion = furnish('a.web-to-plex-minion', { imdb: processed.IMDbID }, + furnish('img', { src: IMAGES.icon_32 }) + ); + + addMinions(minion).stayUnique(true); + } else { + minion = furnish('a.web-to-plex-minion', {}, 'Web to Plex'); + + addMinions(minion); + } + + element.appendChild(minion); + }); + }, }; diff --git a/moz/itunes.css b/moz/itunes.css new file mode 100644 index 0000000..1e33870 --- /dev/null +++ b/moz/itunes.css @@ -0,0 +1,9 @@ +.web-to-plex-minion { + border-color: var(--orange) !important; + color: var(--orange) !important; +} + +.web-to-plex-minion:hover { + border-color: var(--light-orange) !important; + color: var(--light-orange) !important; +} diff --git a/moz/itunes.js b/moz/itunes.js index c764b89..29ef14f 100644 --- a/moz/itunes.js +++ b/moz/itunes.js @@ -1,5 +1,7 @@ let script = { - "url": "", + "url": "*://itunes.apple.com/\\w{2,4}/(movie|tv(-season)?)/*", + + "ready": () => (!$('.section').empty && top.__NewCSP__), "init": (ready) => { let _title, _year, _image, R = RegExp; @@ -8,18 +10,18 @@ let script = { switch(type) { case 'movie': - title = $('[class*="movie-header__title"i]').first.textContent; - year = +$('[datetime]').first.textContent; - image = ($('[class*="product"] ~ * picture img').first || {}).src; + title = $('[class~="movie-header__title"i]').first.textContent; + year = +$('time, [datetime]').first.textContent; + image = ($('picture img, [class*="product"] ~ * picture img').first || {}).src; title = title.replace(RegExp(`\\s*\\(${ year }|\\d{4}\\)`), ''); year = year || +R.$1; break; case 'tv': - title = $('h1[itemprop="name"], h1').first.textContent.replace(/\s*\((\d+)\)\s*/, '').trim(); - year = +$('.release-date > *:last-child').first.textContent.replace(/[^]*(\d{4})[^]*?$/g, '$1').trim(); - image = $('[class*="product"] ~ * picture img').first.src; + title = $('[class~="show-header__title"i], h1[itemprop="name"], h1').first.textContent.replace(/\s*\((\d+)\)\s*/, '').trim(); + year = +$('time, .release-date > *:last-child').first.textContent.replace(/[^]*(\d{4})[^]*?$/g, '$1').trim(); + image = $('picture img, [class*="product"] ~ * picture img').first.src; title = title.replace(RegExp(`\\s*\\(${ year }\\)`), ''); break; @@ -35,7 +37,7 @@ let script = { }, "getType": () => { - return /(\/\w+)?\/tv-season\//.test(top.location.pathname)? + return /(\/\w+)?\/tv(-season)?\//.test(top.location.pathname)? 'tv': 'movie' }, @@ -45,4 +47,49 @@ let script = { button.attributes.style.value += '; box-sizing: border-box !important; font-size: 16px !important; line-height: normal !important;'; }, + + "minions": () => { + let actions = $('.product-header > *:last-child > *:first-child'); + + if(actions.empty) + return; + + actions.forEach(element => { + let minion = furnish('a.web-to-plex-minion.we-button.we-button--outlined.we-button-external.icon.icon-external', {}, 'Web to Plex '); + + addMinions(minion); + element.appendChild(minion); + }); + }, }; + + +top.__NewCSP__ = top.__NewCSP__ || false; +(() => { + if(!top.__NewCSP__) { + /* Add 'data:' to the CSP */ + let ContentSecurityPolicies = $('meta[http-equiv="Content-Security-Policy"]'); + + if(ContentSecurityPolicies.empty) + return; + + ContentSecurityPolicies.forEach(ContentSecurityPolicy => + ContentSecurityPolicy.content = ContentSecurityPolicy.content + .split(';') + .map(src => { + let type; + + src = src.trim().split(' '); + type = src[0]; + + if(type == 'font-src') + src.push('http://webtoplex.github.io', 'https://webtoplex.github.io'); + + return src.join(' '); + }) + .join(';') + ); + + top.__NewCSP__ = true; + } +})(); diff --git a/moz/letterboxd.css b/moz/letterboxd.css new file mode 100644 index 0000000..bfe692d --- /dev/null +++ b/moz/letterboxd.css @@ -0,0 +1,10 @@ +.web-to-plex-minion { + display: inline-block; + vertical-align: middle; + font-size: 12px; + line-height: 1.66666667; +} + +.web-to-plex-minion:hover { + color: #f67e56!important; +} diff --git a/moz/letterboxd.js b/moz/letterboxd.js index d8c1ac7..73b15dc 100644 --- a/moz/letterboxd.js +++ b/moz/letterboxd.js @@ -1,5 +1,5 @@ let script = { - "url": "*://*.letterboxd.com/(film|list)/", + "url": "*://*.letterboxd.com/(?:\\w+/)?(film|list)/*", "ready": () => (script.getType('list')? true: !$('.js-watch-panel').empty), @@ -10,16 +10,16 @@ let script = { switch(type) { case 'movie': - title = $('.headline-1[itemprop="name"]').first.textContent.trim(); - year = +$('small[itemprop="datePublished"]').first.textContent.trim(); - image = ($('.image').first || {}).src; + title = $('#featured-film-header .headline-1, .headline-1[itemprop="name"]').first.textContent.trim(); + year = +$('#featured-film-header [href*="/year/"], small[itemprop="datePublished"]').first.textContent.trim(); + image = ($('.film-poster img, .image').first || {}).src; IMDbID = script.getIMDbID(type); return { type, title, year, image, IMDbID }; break; case 'list': - let items = $('.poster-list .poster-container'), + let items = $('.poster-list .poster-container, .poster-list .film-detail'), options = []; items.forEach((element, index, array) => { @@ -73,4 +73,51 @@ let script = { return { type, title, year, image }; }, + + "minions": () => { + let actions = $('.actions-panel ul, .js-watch-panel .services, #watch').first, + type = script.getType(), + featured = (actions.id == 'watch'); + + if(!actions) + return; + + let minion, parent; + + if(type == 'list') { + parent = furnish('li', {}, + furnish('span', {}, + furnish('span.has-icon.icon-16', {}, + furnish('img.web-to-plex-icon.icon', { style: 'background: none !important', src: IMAGES.icon_16, height: 16, width: 16 }), + minion = furnish('a.web-to-plex-minion', {}, 'Web to Plex') + ) + ), + ); + + addMinions(minion); + actions.appendChild(parent); + } else if(featured) { + parent = furnish('div.other', {}, + furnish('img.web-to-plex-icon', { src: IMAGES.icon_16, height: 16, width: 16 }), + minion = furnish('a.web-to-plex-minion.label.more', {}, 'Web to Plex') + ); + + addMinions(minion); + actions.appendChild(parent); + } else { + parent = furnish('p.service', { style: 'display: flex !important' }, + minion = furnish('a.web-to-plex-minion.label.tooltip', {}, + furnish('span.brand', {}, + furnish('img', { src: IMAGES.icon_32, height: 24, width: 24 }) + ), + furnish('span.title', {}, + furnish('span.name', {}, 'Web to Plex') + ) + ) + ); + + addMinions(minion); + actions.appendChild(parent); + } + }, }; diff --git a/moz/manifest.json b/moz/manifest.json index c31853a..c65903b 100644 --- a/moz/manifest.json +++ b/moz/manifest.json @@ -4,7 +4,7 @@ "homepage_url": "https://webtoplex.github.io/", "manifest_version": 2, - "version": "4.1.1.11", + "version": "4.1.2.0", "browser_specific_settings": { "gecko": { "id": "mink.cbos@gmail.com", @@ -161,8 +161,7 @@ "js": ["utils.js", "plex$.js"] },{ "matches": ["*://*/*"], - "js": ["utils.js", "common.js"], - "css": ["common.css", "theme.css"] + "js": ["utils.js", "common.js"] } ], @@ -194,5 +193,5 @@ "contextMenus", "" ], - "web_accessible_resources": ["*.png", "options.*"] + "web_accessible_resources": ["*.woff", "*.png", "options.*", "*.css"] } diff --git a/moz/movieo.css b/moz/movieo.css new file mode 100644 index 0000000..7638814 --- /dev/null +++ b/moz/movieo.css @@ -0,0 +1,35 @@ +.mid-top-actions .share-box { + padding-left: 20px!important; +} + +.web-to-plex-minion { + margin-right: 8px; + color: #566273!important; +} + +.web-to-plex-minion.wtp--download { + border-color: #f45a26!important; + color: #acb4bf!important; +} + +.web-to-plex-minion.wtp--download:hover { + border-color: #f67e56!important; +} + +.web-to-plex-minion.wtp--found { + border-color: #ca7c1f!important; + color: #acb4bf!important; +} + +.web-to-plex-minion.wtp--found:hover { + border-color: #f8c022!important; +} + +#tt--0-0 { + background: #666!important; +} + +#tt--0-0:hover { + background: #888!important; + border-color: #888!important; +} diff --git a/moz/movieo.js b/moz/movieo.js index bca58ba..b052634 100644 --- a/moz/movieo.js +++ b/moz/movieo.js @@ -72,4 +72,25 @@ let script = { return { type, title, year, image }; }, + + "minions": () => { + // The button text in the "Comments" button takes up too much space, so we hide it + // It's very clear that it's about comments event without the text + let comments = $('.mid-top-actions .comments-link .txt'); + + if(!comments.empty) + comments.forEach(comment => comment.remove()); + + let actions = $('.mid-top-actions'); + + if(actions.empty) + return; + + actions.forEach(element => { + let minion = furnish('a.web-to-plex-minion.button.comments-link', {}, 'Web to Plex'); + + addMinions(minion); + element.appendChild(minion); + }); + }, }; diff --git a/moz/options.css b/moz/options.css index 90b37c1..11e2bbc 100644 --- a/moz/options.css +++ b/moz/options.css @@ -1,3 +1,25 @@ +@charset "UTF-8"; +@import url(glyphs.css); +@import url(colors.css); + +@font-face { + font-family: "Plex"; + src: local(Plex), + url(Plex.woff2) format('woff2'), + url(Plex.woff) format('woff'); + font-weight: 400; + font-style: normal; +} + +@font-face { + font-family: "Plex-bold"; + src: local(Plex-bold), + url(Plex.bold.woff2) format('woff2'), + url(Plex.bold.woff) format('woff'); + font-weight: 400; + font-style: normal; +} + * { outline: #0000 !important; } @@ -9,7 +31,7 @@ html, body { body { background: url(noise.png) fixed, url(background.png) no-repeat fixed center/cover, #3f4245 !important; color: #999 !important; - font-family: Open Sans Regular, Helvetica Neue, Helvetica, Arial, sans-serif, system; + font-family: "Plex", "Open Sans Regular", "Helvetica Neue", Helvetica, Arial, sans-serif, system; flex-grow: 1; overflow-x: hidden; overflow-y: auto; @@ -81,12 +103,26 @@ a { text-decoration: none !important; } +a:hover { + color: #e59029 !important; +} + [target="_blank"]:not(#version)::after { - content: " [\2197]"; + content: "\e223" !important; + font-family: Glyphicons Regular !important; + display: inline-block; font-size: 70%; - vertical-align: super; -}ertical-align: super; + height: 12px; + width: 12px; + + margin-left: 0.4em; + /* vertical-align: super; */ +} + +button[target="_blank"]::after { + content: "\e223" !important; + font-family: Glyphicons Regular !important; } hr { @@ -172,62 +208,109 @@ div:not(body > div) { z-index: 18 !important; } +.checkbox input:checked + label:hover, input[type="range"]::-moz-range-thumb:hover { + background: #e59029 !important; + transition: all 0.2s; +} + +.checkbox input:not(:checked) + label:hover { + background: #999; +} + button, input[type="button"i], .button { - padding: 10px 18px !important; - font-size: 16px !important; - line-height: 1.33 !important; + background: #fff2 !important; + border: 0.1em solid #cc7b19 !important; + border-bottom-width: 0.4em !important; border-radius: 3px; - font-family: inherit; - text-transform: uppercase; - border: 0; box-shadow: none !important; - position: relative; - overflow: hidden; color: #fff !important; - background: #cc7b19 !important; - margin-bottom: 0; + cursor: pointer !important; + font-family: inherit; + font-size: 16px !important; font-weight: 400 !important; + line-height: 1.33 !important; + overflow: hidden; + text-transform: uppercase; + user-select: none; vertical-align: middle; - cursor: pointer !important; white-space: nowrap; - user-select: none; + + margin-bottom: 0; + padding: 10px 18px !important; + position: relative; + transition: all 0.1s; } -button:hover, input[type="button"i]:hover, .button:hover, .checkbox input:checked + label:hover, input[type="range"]::-webkit-slider-thumb:hover { - background: #e59029 !important; - transition: all 0.1s; +[danger], .danger { + border-color: #f33 !important; + color: #f33; + outline: #f33; } -.checkbox input:not(:checked) + label:hover { - background: #999; +button:hover, input[type="button"i]:hover, .button:hover { + background: #fff4 !important; + border-color: #e59029 !important; transition: all 0.1s; } -[id$="_test"] { - background: #cc7b19 !important; +[danger]:hover, .danger:hover { + border-color: #f44 !important; + color: #f44; + outline: #f44; +} + +[id$="_test"], [id^="enable-"] { margin-bottom: 2px; padding: 10px 8px 10px 10px !important; } [id$="_status"] { - padding: 0 6px !important; - font-size: 16px !important; - border-radius: 3px; - font-family: monospace, sans-serif, sans, arial; + /* background: #6668 !important; */ border: 0; + /* border-radius: 4px; */ box-shadow: none !important; color: #fff !important; - background: #666 !important; - border-radius: 4px; + font-size: 16px !important; + font-family: monospace, sans-serif, sans, arial; + + padding: 0 3px !important; +} + +[notice]:after { + color: #9998 !important; + content: attr(notice); + font-family: Plex; + text-transform: capitalize; + + padding-left: 6px; +} + +svg:not(:root) { + overflow: hidden; +} + +.no-container { + margin: 0 10px !important; +} + +.no-container + .no-container { + margin-left: 0 !important; } -[id$="_status"].false { - background: #cc1b19 !important; +code ~ .no-container { + vertical-align: bottom !important; } -[id$="_status"].true { - background: #7bcc19 !important; +.icon { + position: relative; + top: -2px; + display: inline-block; + width: 1em; + height: 1em; + vertical-align: middle; + line-height: 1; + fill: currentColor; } [id$="token"], [data-option$="Token"], [data-option$="API"] { @@ -239,21 +322,29 @@ button:hover, input[type="button"i]:hover, .button:hover, .checkbox input:checke } em { - color: #cc7b19 !important; + color: #cc7b19; } strike, st, k { text-decoration: line-through !important; } +[href^="#!/NaCl+"], [href^="#!/NaCl+"] *{ + font-family: system-ui, Plex !important; + text-transform: none !important; +} + [href="#!/NaCl+Iw"], [href="#!/NaCl+Iw"] * { cursor: not-allowed !important; opacity: 0.5 !important; } select { + border: 0.1em solid #cc7b19; + border-left-width: 0.4em; + cursor: pointer; margin-left: 10px !important; - font-size: 16px !important; + font-size: 14px !important; line-height: inherit; text-transform: none; } @@ -342,6 +433,16 @@ display summary > .checkbox { display: block; } +summary.button, summary.button:hover { + border-color: #197bcc !important; + width: -moz-fit-content; +} + +details[open] > summary.button, summary.button:hover { + background: #fff4 !important; + border-color: #298bdc !important; +} + select:not([multiple]) > option { background: url(noise.png), #3f4245 !important; } @@ -350,9 +451,9 @@ details:last-child > summary { margin-bottom: 0 !important; } -.bar > article > details > summary { - display: list-item; - list-style-type: disc; +.bar > article > details > summary, summary.button { + display: list-item !important; + list-style-type: none !important; } .bar > article > details[open] > *:not(summary) { @@ -363,14 +464,29 @@ details:last-child > summary { color: #0000; } -.bar > article > details[open] > summary::marker { +.bar > article > details[open] > summary::before { + content: '\2022'; color: inherit !important; + font-size: 16px; + + margin-left: -10px; + margin-top: -4px; + position: absolute; } #save { margin: 6px 12px; } +#save > svg { + position: absolute; + + margin: inherit; + padding: inherit; + + padding-left: 25%; +} + footer { bottom: 0; position: fixed; @@ -432,12 +548,12 @@ footer { } .test { - background: #197bcc !important; + border-color: #197bcc !important; font-family: monospace; } .test:hover { - background: #298bdc !important; + border-color: #298bdc !important; } #sidebar .checkbox:not([special]) { @@ -545,8 +661,9 @@ span.checkbox { background: #cc7b19; } -.checkbox[disabled] { +[disabled] { opacity: 0.25 !important; + transition: opacity 0.5s !important; } .checkbox[disabled] + [using] { @@ -592,7 +709,7 @@ input[type="range"] { -moz-appearance: none; background: #0004; - outline: #0000; + outline: none; height: 5px !important; width: 83% !important; @@ -625,6 +742,9 @@ input[type="range"] + output::after { } input[type="range"]::-moz-range-thumb { + appearance: none; + -moz-appearance: none; + background: #cc7b19; border: 1px solid #cc7b19; border-radius: 100%; @@ -681,6 +801,15 @@ code[new], [code][new], [type^="code"i][new] { color: #efc371 !important; } +#ip-address, #ip-address ~ * { + color: var(--green) !important; +} + +#ip-address:empty::before, #ip-address:empty ~ * { + color: var(--light-red) !important; + content: 'Unknown' !important; +} + #version { background: #0000 !important; border: 1px solid #666 !important; @@ -693,6 +822,10 @@ code[new], [code][new], [type^="code"i][new] { transition: border 0.15s, color 0.15s; } +#version::before { + margin-right: 4px; +} + /* Release is higher than GitHub */ #version[status="high"] { /* Blue */ @@ -700,6 +833,10 @@ code[new], [code][new], [type^="code"i][new] { color: #197bcc !important; } +#version[status="high"]::before { + /* content: '\25b4'; */ +} + /* Release is same as GitHub */ #version[status="same"] { /* Green */ @@ -707,6 +844,10 @@ code[new], [code][new], [type^="code"i][new] { color: #6cc644 !important; } +#version[status="same"]::before { + /* content: '\25b8'; */ +} + /* Release is lower than GitHub */ #version[status="low"] { /* Orange/Red (#f3582c) */ @@ -717,6 +858,28 @@ code[new], [code][new], [type^="code"i][new] { color: #949494 !important; */ } +#version[status="low"]::before { + /* content: '\25be'; */ +} + +.maybe, [disabled].maybe, [disabled] .maybe, [disabled] .maybe path { + border-color: #888 !important; + color: #888 !important; + outline: #888 !important; +} + +.false, [disabled].false, [disabled] .false, [disabled] .false path { + border-color: #f53 !important; + color: #f53 !important; + outline: #f53 !important; +} + +.true, [disabled].true, [disabled] .true, [disabled] .true path { + border-color: #6b5 !important; + color: #6b5 !important; + outline: #6b5 !important; +} + /* notifications */ .notification { background: #F45A26; @@ -738,7 +901,6 @@ code[new], [code][new], [type^="code"i][new] { z-index: 999999; } - /* Web to Plex general information notifications */ .notification.info { background: #666 !important; @@ -908,17 +1070,28 @@ code[new], [code][new], [type^="code"i][new] { background: #ffffff4d !important; } -* { - scrollbar-width: thin; - scrollbar-color: rgba(255, 255, 255, 0.15); +*::-webkit-scrollbar { + width: 10px; +} + +*::-webkit-scrollbar-thumb { + min-height: 50px; + background: rgba(255, 255, 255, 0.15); + border: 2px solid rgba(0, 0, 0, 0); + border-radius: 8px; + background-clip: padding-box; +} + +*::-webkit-scrollbar-track { + /* background: url(noise.png) fixed, #3f4245 !important; */ } *::placeholder { color: #999 !important; } -*::-moz-placeholder, *:-moz-placeholder { - color: #999; +*::-webkit-input-placeholder { + color: #999 !important; } @keyframes spin { diff --git a/moz/options.html b/moz/options.html index 8b49001..ece70ab 100644 --- a/moz/options.html +++ b/moz/options.html @@ -41,61 +41,62 @@

Login Settings

- Use your Plex token - -
+ Use your Plex token + +
How to find your Plex token. -
+

— OR —

- Login with Plex - - + Login with Plex + +
Your Plex username and password are never stored, only your Plex token. -
- Your username and password are used in order to get a token from Plex itself. +
+
+ Your username and password are used in order to get a token from Plex.

— OR —

- Attach to Ombi* - - + Attach to Ombi* + +
-

Fill in Manager Settings with Ombi?

+

Would you like to use Ombi to fill in your Manager Settings?

- +
- Your Ombi Plex token will be shared by this extension. + Ombi's Plex token will be shared by this extension.


- +
- +
- Advance + Advanced

Plex Server Options

- +
Use this to communicate directly with your Plex server.
Such as http://localhost:32400/ or http://192.168.1.100:32400/ @@ -111,14 +112,14 @@

Plex Server Options

Ombi (Movies/TV Shows) - +

Connection Settings

- +
Such as https://example.com/ombi or http://192.168.1.100:5000
@@ -126,35 +127,38 @@

Connection Settings

- +
- 1. Go to Ombi | Settings | Ombi
- 2. Copy/Paste the "API Key."
- Such as aa756d33242f6g8ffbca2b3963586f21 + 1. Go to OmbiSettingsOmbi
+ 2. Copy/Paste the API Key
+ — Such as aa756d33242f6a8ffbca2b3963586f21
- +


- +
@@ -163,14 +167,14 @@

Login (saved)

Watcher (Movies) - +

Connection Settings

- +
Such as https://example.com/watcher or http://192.168.1.100:9090
@@ -178,63 +182,68 @@

Connection Settings

- +
- 1. Go to Watcher | Settings | Server
- 2. Copy/Paste the "API Key."
- Such as aa756d33242f6g8ffbca2b3963586f21 + 1. Go to WatcherSettingsServer
+ 2. Copy/Paste the API Key
+ — Such as aa756d33242f6a8ffbca2b3963586f21
-

Login (saved)

+

Login (saved)

The default username is watcher. The default password is rehctaw.

- +
Only use this if you setup a Watcher username.
- +
Only use this if you setup a Watcher password.
Your password will be hidden once saved.

-
This information will be used for Basic Access Authentication only. This will allow the extension to ask Watcher for your list of films, or to add to your list of films.
+
+ This information will be used for Basic Access Authentication only.
+ This will allow the extension to ask Watcher for your list of films, or to add to your list of films. +
- +
- +
- +


- +
Radarr (Movies) - +

Connection Settings

- +
Such as https://example.com/radarr or http://192.168.1.100:7878
@@ -242,37 +251,40 @@

Connection Settings

- +
- 1. Go to Radarr | Settings | General
- 2. Click on "Show advance," then copy/paste the "API Key."
- Such as aa756d33242f6g8ffbca2b3963586f21 + 1. Go to RadarrSettingsGeneral
+ 2. Click on Show Advanced then copy/paste the API Key
+ — Such as aa756d33242f6a8ffbca2b3963586f21
-

Login (saved)

+

Login (saved)

- +
Only use this if you setup a Radarr username.
- +
Only use this if you setup a Radarr password.
Your password will be hidden once saved.

-
This information will be used for Basic Access Authentication only. This will allow the extension to ask Radarr for your list of films, or to add to your list of films.
+
+ This information will be used for Basic Access Authentication only.
+ This will allow the extension to ask Radarr for your list of films, or to add to your list of films. +
- +
- +
This should be the same path (verbatim) used in Radarr. @@ -280,27 +292,27 @@

Login (saved)

- +


- +
CouchPotato (Movies) - +

Connection Settings

- +
Such as https://example.com/couchpotato or http://192.168.1.100:5050
@@ -308,38 +320,41 @@

Connection Settings

- +
- 1. Go to CouchPotato | Settings
- 2. Copy/Paste the "API Key."
- Such as aa756d33242f6g8ffbca2b3963586f21 + 1. Go to CouchPotatoSettings
+ 2. Copy/Paste the API Key
+ — Such as aa756d33242f6a8ffbca2b3963586f21
-

Login (saved)

+

Login (saved)

- +
Only use this if you setup a CouchPotato username.
- +
Only use this if you setup a CouchPotato password.
Your password will be hidden once saved.
-
This information will be used for Basic Access Authentication only. This will allow the extension to ask Couchpotato for your list of films, or to add to your list of films.
+
+ This information will be used for Basic Access Authentication only.
+ This will allow the extension to ask Couchpotato for your list of films, or to add to your list of films. +
@@ -348,7 +363,7 @@

Login (saved)



- +
@@ -357,14 +372,14 @@

Login (saved)

Medusa (TV Shows) - +

Connection Settings

- +
Such as https://example.com/medusa or http://192.168.1.100:8081
@@ -372,35 +387,38 @@

Connection Settings

- +
- 1. Go to Medusa | Settings | General | Interface | Web Interface
- 2. Copy/Paste the "API key."
- Such as aa756d33242f6g8ffbca2b3963586f21 + 1. Go to MedusaSettingsGeneralInterfaceWeb Interface
+ 2. Copy/Paste the API key
+ — Such as aa756d33242f6a8ffbca2b3963586f21
-

Login (saved)

+

Login (saved)

- +
- +
Your password will be hidden once saved.

-
This information will be used for Basic Access Authentication only. This will allow the extension to ask Medusa for your list of TV shows, or to add to your list of TV shows.
+
+ This information will be used for Basic Access Authentication only.
+ This will allow the extension to ask Medusa for your list of TV shows, or to add to your list of TV shows. +
- +
- +
This should be the same path (verbatim) used in Medusa. @@ -408,33 +426,33 @@

Login (saved)

- - +


- +
Sonarr (TV Shows) - +

Connection Settings

- +
Such as https://example.com/sonarr or http://192.168.1.100:8989
@@ -442,37 +460,40 @@

Connection Settings

- +
- 1. Go to Sonarr | Settings | General
- 2. Click on "Show advance," then copy/paste the "API Key."
- Such as aa756d33242f6g8ffbca2b3963586f21 + 1. Go to SonarrSettingsGeneral
+ 2. Click on Show Advanced then copy/paste the API Key
+ — Such as aa756d33242f6a8ffbca2b3963586f21
-

Login (saved)

+

Login (saved)

- +
Only use this if you setup a Sonarr username.
- +
Only use this if you setup a Sonarr password.
Your password will be hidden once saved.

-
This information will be used for Basic Access Authentication only. This will allow the extension to ask Sonarr for your list of TV shows, or to add to your list of TV shows.
+
+ This information will be used for Basic Access Authentication only.
+ This will allow the extension to ask Sonarr for your list of TV shows, or to add to your list of TV shows. +
- +
- +
This should be the same path (verbatim) used in Sonarr. @@ -480,27 +501,27 @@

Login (saved)

- +


- +
Sick Beard (TV Shows) - +

Connection Settings

- +
Such as https://example.com/sickBeard or http://192.168.1.100:8081
@@ -508,37 +529,40 @@

Connection Settings

- +
- 1. Go to Sick Beard | Config | General | API
- — a. Ensure the checkbox "Enable API" is enabled
- — b. Press the "Generate" button
- 2. Copy/Paste the "API Key."
- Such as aa756d33242f6g8ffbca2b3963586f21 + 1. Go to Sick BeardConfigGeneralAPI
+ — a. Ensure the checkbox Enable API is checked
+ — b. Press the Generate button
+ 2. Copy/Paste the API Key
+ — Such as aa756d33242f6a8ffbca2b3963586f21
-

Login (saved)

+

Login (saved)

- +
- +
Your password will be hidden once saved.

-
This information will be used for Basic Access Authentication only. This will allow the extension to ask Sick Beard for your list of TV shows, or to add to your list of TV shows.
+
+ This information will be used for Basic Access Authentication only.
+ This will allow the extension to ask Sick Beard for your list of TV shows, or to add to your list of TV shows. +
- +
- +
This should be the same path (verbatim) used in Sick Beard. @@ -546,13 +570,13 @@

Login (saved)

- - + - + + +

+
+ — a minimalist, rectangular button (top center of page) +
+
+ — a normal, circular button (bottom left or right of page) +
- Which direction should the button open (◯ only)? + Which direction should the master button open (◯ only)?
- - + +
- How transparent should the button be when hidden? + How transparent should the master button be when hidden? +
+ + +
+
+

The Minion Button(s)

+
+ Allow the use of custom (minion) buttons?
- - + + + + +
@@ -607,14 +649,14 @@

The Button

Experimental Sites -
+


Default Sites -
+

@@ -633,18 +675,18 @@

Proxy Settings

Force Secure Connections - +

- If enabled, all insecure (HTTP) requests will be through an HTTPS proxy. + If enabled, all requests sent by Web to Plex will be proxied.

Proxy URL & Syntax

- +
Please provide the URL of your proxy.
@@ -660,9 +702,9 @@

Proxy URL & Syntax

Proxy Headers

-
+
- If your proxy requires special headers, enter that information in here. + If your proxy requires special headers, enter that information in here.
  • @{raw-url} OR @{url} — the raw, uneditied URL
  • @@ -673,11 +715,23 @@

    Proxy Headers

+
+ +
    +
  • + IP Address + + + +
  • +
+
+

Native Proxy Settings

@@ -691,20 +745,20 @@

Native Proxy Settings

Auto Grab - +

- When the user presses the Grab button, the extension should: + When you press the Grab button, the extension should:
  • Grab ALL — Find items not on Plex, and grab them
  • -
  • ASK user — Find items not on Plex, and grab what the user approves
  • +
  • ASK you — Find items not on Plex, and grab what you approve

Maximum Auto Grabs

- - + +
How many items can be automatically handled before requiring permission to continue?
@@ -714,12 +768,12 @@

Maximum Auto Grabs

Prompt for Save Location - +

- When the user presses the Grab button should the save location be asked for? + When you press the Grab button, should the save location be asked for?
Only supports Medusa, Radarr, and Sonarr.
@@ -729,12 +783,12 @@

Prompt for Quality - +

- When the user presses the Grab button should the quality be asked for? + When you press the Grab button, should the quality be asked for?
Only supports Medusa, Radarr, and Sonarr.
@@ -748,23 +802,23 @@

Ignore Found Items - +

- When the user presses the Grab button and an item already exists, should the notification be ignored or not? + When you press the Grab button and the item already exists, should the notification be ignored or not?

Ignore Repetitive Notifications - +

- When the user presses the Grab button and there are several incoming notifications of similar information, should the notifications be ignored (after the first one) or not? + When you press the Grab button and there are several incoming notifications of similar information, should the notifications be ignored (after the first one) or not?
@@ -776,14 +830,14 @@

Loose Searching - +


- - + +
Allows the extension to search for non-English titles using pattern matching (as a last resort).
@@ -795,13 +849,13 @@

Manager Searching - +

Allows the extension to use your manager(s) to find media.
- Currently supports: Medusa, Ombi, Radarr, and Sonarr. + Currently supports Medusa, Ombi, Radarr, and Sonarr.

@@ -809,7 +863,7 @@

ID Fetching Mode - +

@@ -823,13 +877,13 @@

- Advance Settings + Advanced Settings BETA

API Keys

- +
You can sign up for an API key, or have OMDb used as a last resort.
@@ -837,17 +891,29 @@

API Keys

- +
You can learn more on how to obtain an API key.

Data Handling

+
+

Settings' Data

+ +
+ +
+ WARNING — This will remove all of your data. +
+ If you don't press Save, this action will be ignored. +
+
+

Data Compression

- +
@@ -886,13 +952,13 @@

Developer Options

Developer Mode - +

WARNING — may cause data loss.
- Enables developer (debugging) mode, showing advance errors and logging to the console when possible. + Enables developer (debugging) mode, showing Advanced errors and logging to the console when possible.
@@ -905,12 +971,17 @@

External Links

- +
Browse the Web to Plex Wiki for help setting up
+
Browse issues if you're having trouble
+
+ +
+ Download Plex Media Server. +
+
+
@@ -994,9 +1065,10 @@

External Links

+
- ... + ... diff --git a/moz/options.js b/moz/options.js index 26b3a84..0ae997e 100644 --- a/moz/options.js +++ b/moz/options.js @@ -8,13 +8,13 @@ let DEVELOPER_MODE; -if(chrome.runtime.lastError) +if(browser.runtime.lastError) /* Always causes errors on *nix machines, so just "poke" the errors here */ - chrome.runtime.lastError.message; + browser.runtime.lastError.message; // FireFox doesn't support sync storage. -const storage = (chrome.storage.sync || chrome.storage.local), - $ = top.$ = (selector, all) => (all? [...document.querySelectorAll(selector)]: document.querySelector(selector)), +const storage = (browser.storage.sync || browser.storage.local), + $ = top.$ = (selector, all = false, container = document) => (all? [...container.querySelectorAll(selector)]: container.querySelector(selector)), $$ = top.$$ = (selector, all) => (all? [...$('display').querySelectorAll(selector)]: $('display').querySelector(selector)), __servers__ = $('[data-option="preferredServer"]'), __sickBeard_qualityProfile__ = $(`[data-option="sickBeardQualityProfileId"]`), @@ -117,7 +117,7 @@ const storage = (chrome.storage.sync || chrome.storage.local), 'ManagerSearch', 'UseLowCache', - // Advance Settings + // Advanced Settings 'OMDbAPI', 'TMDbAPI', 'UseLZW', @@ -179,7 +179,6 @@ const storage = (chrome.storage.sync || chrome.storage.local), 'builtin_gostream', 'builtin_tubi', 'builtin_webtoplex', - 'builtin_shanaproject', // Plugins - End of file, "let plugins =" 'plugin_toloka', @@ -196,19 +195,23 @@ const storage = (chrome.storage.sync || chrome.storage.local), 'plugin_metacritic', // Theme Settings - ...(() => [...$('[data-option^="theme:"i]', true)].map(e => e.dataset.option))() + 'UseMinions', + ...(() => [...$('[data-option^="theme:"i]', true)].map(e => e.dataset.option))(), + + // Other Settings + '__defaults', ]; let PlexServers = [], ServerID = null, ClientID = null, - manifest = chrome.runtime.getManifest(), + manifest = browser.runtime.getManifest(), terminal = // See #3 (DEVELOPER_MODE = $('[data-option="DeveloperMode"]').checked)? console: { error: m => m, info: m => m, log: m => m, warn: m => m, group: m => m, groupEnd: m => m }; -chrome.manifest = manifest; +browser.manifest = manifest; // Not really important variables // The "caught" IDs (already asked for in managers) @@ -218,7 +221,23 @@ let __caught = { tvdb: [], }, // The theme classes - __theme = []; + __theme = {}; + +// Icon Markers +let MARKERS = [ + // yes + ``, + // no + ``, + // maybe + `` +]; + +MARKERS.yes = MARKERS[0]; +MARKERS.no = MARKERS[1]; +MARKERS.maybe = MARKERS[2]; + +let RESETTING_SETTINGS = false; // create and/or queue a notification // state = "error" - red @@ -517,6 +536,7 @@ function performPlexLogin({ event }) { s.title = ''; __servers__.innerHTML = ''; __save__.disabled = true; + __save__.innerHTML = 'Save ' + MARKERS.maybe; LoadingAnimation(true); tryPlexLogin(u, p) @@ -533,7 +553,8 @@ function performPlexLogin({ event }) { return performPlexTest({}); } - }); + }) + .catch(error => { LoadingAnimation(); new Notification('error', error); __save__.innerHTML = 'Save ' + MARKERS.no; }); } @@ -543,8 +564,9 @@ function performPlexTest({ ServerID, event }) { inusestatus = [...$('[using="plex"]', true)]; __save__.disabled = true; + __save__.innerHTML = 'Save ' + MARKERS.maybe; __servers__.innerHTML = ''; - teststatus.textContent = '?'; + teststatus.innerHTML = MARKERS.maybe; inusestatus.map(e => e.setAttribute('in-use', false)); LoadingAnimation(true); @@ -552,7 +574,7 @@ function performPlexTest({ ServerID, event }) { LoadingAnimation(); PlexServers = servers; - teststatus.textContent = '!'; + teststatus.innerHTML = MARKERS[+!servers]; inusestatus.map(e => e.setAttribute('in-use', false)); if(!servers) @@ -560,6 +582,7 @@ function performPlexTest({ ServerID, event }) { inusestatus.map(e => e.setAttribute('in-use', true)); __save__.disabled = false; + __save__.innerHTML = 'Save ' + MARKERS.yes; teststatus.classList = true; (servers = [{ sourceTitle: 'GitHub', clientIdentifier: '', name: 'No Server', notice: 'This will not connect to any Plex servers' }, ...servers]).forEach(server => { @@ -575,7 +598,8 @@ function performPlexTest({ ServerID, event }) { if(ServerID) { __servers__.value = ServerID; } - }); + }) + .catch(error => { LoadingAnimation(); new Notification('error', error); teststatus.innerHTML = MARKERS.no; __save__.innerHTML = 'Save ' + MARKERS.no; }); } function getPlexConnections(server) { @@ -607,12 +631,16 @@ function getOptionValues() { } }); - let COM = options.UseLZW; + let COM = options.UseLZW, + DEF = options.__defaults == 'true'; for(let key in __caught) __caught[key] = __caught[key].filter(id => id).slice(0, (COM? 200: 100)).sort(); - __theme = __theme.filter(v => v); + // if(options.__theme) + // __theme = JSON.parse(options.__theme); + // + // __theme = __theme.filter(v => v); let _c = JSON.stringify(__caught), _t = JSON.stringify(__theme); @@ -635,6 +663,7 @@ function performOmbiLogin({ event }) { s.title = ''; __servers__.innerHTML = ''; __save__.disabled = true; + __save__.innerHTML = 'Save ' + MARKERS.maybe; LoadingAnimation(true); let APIURL = `${ l }api/v1/`, @@ -743,12 +772,14 @@ function performOmbiLogin({ event }) { } __save__.disabled = false; + __save__.innerHTML = 'Save ' + MARKERS.yes; } else { /* Plex either doesn't exist, or is disabled */ new Notification('error', 'Error getting Plex details from Ombi'); + __save__.innerHTML = 'Save ' + MARKERS.no; } } ) - .catch( error => { new Notification('error', error); throw error } ); + .catch(error => { LoadingAnimation(); new Notification('error', error); __save__.innerHTML = 'Save ' + MARKERS.no; }); } function performOmbiTest({ refreshing = false, event }) { @@ -760,7 +791,7 @@ function performOmbiTest({ refreshing = false, event }) { enabled = refreshing? $('#using-ombi'): $$('#using-ombi'), inusestatus = [...$('[using="ombi"]', true)]; - teststatus.textContent = '?'; + teststatus.innerHTML = MARKERS.maybe; options.ombiURLRoot = url = path.value = options.ombiURLRoot.replace(/^(\:\d+)/, 'localhost$1').replace(/^(?!^http(s)?:)/, 'http$1://').replace(/\/+$/, ''); inusestatus.map(e => e.setAttribute('in-use', false)); LoadingAnimation(true); @@ -774,7 +805,8 @@ function performOmbiTest({ refreshing = false, event }) { __caught.imdb.push(item.imdbId); __caught.tmdb.push(item.theMovieDbId); }); - }); + }) + .catch(error => { LoadingAnimation(); new Notification('error', error); teststatus.innerHTML = MARKERS.no; }); fetch(`${ url }/api/v1/Request/tv`) .then(r => r.json()) @@ -783,29 +815,30 @@ function performOmbiTest({ refreshing = false, event }) { __caught.imdb.push(item.imdbId); __caught.tvdb.push(item.tvDbId); }); - }); - fetch(`${ url }/api/v1/Status`, headers) - .then( response => response.text() ) - .then( status => { - LoadingAnimation(); - if (!status || !status.length) throw new Error('Unable to communicate with Ombi'); - - if ((status = +status) >= 200 && status < 400) { - teststatus.textContent = '!'; - enabled.checked = teststatus.classList = true; - enabled.parentElement.removeAttribute('disabled'); - inusestatus.map(e => e.setAttribute('in-use', true)); - } else { - teststatus.textContent = '!'; - enabled.checked = teststatus.classList = false; - enabled.parentElement.setAttribute('disabled'); - inusestatus.map(e => e.setAttribute('in-use', false)); - - throw new Error(`Ombi error [${ status }]`); - } - } ) - .catch( error => { new Notification('error', error) } ); + fetch(`${ url }/api/v1/Status`, headers) + .then( response => response.text() ) + .then( status => { + LoadingAnimation(); + if (!status || !status.length) throw new Error('Unable to communicate with Ombi'); + + if ((status = +status) >= 200 && status < 400) { + teststatus.innerHTML = MARKERS.yes; + enabled.checked = teststatus.classList = true; + enabled.parentElement.removeAttribute('disabled'); + inusestatus.map(e => e.setAttribute('in-use', true)); + } else { + teststatus.innerHTML = MARKERS.no; + enabled.checked = teststatus.classList = false; + enabled.parentElement.setAttribute('disabled'); + inusestatus.map(e => e.setAttribute('in-use', false)); + + throw new Error(`Ombi error [${ status }]`); + } + } ) + .catch(error => { LoadingAnimation(); new Notification('error', error); teststatus.innerHTML = MARKERS.no; }); + }) + .catch(error => { LoadingAnimation(); new Notification('error', error); teststatus.innerHTML = MARKERS.no; }); } catch(error) { LoadingAnimation(); @@ -855,7 +888,7 @@ function performWatcherTest({ QualityProfileID = 'Default', refreshing = false, inusestatus = [...$('[using="watcher"]', true)]; quality.innerHTML = ''; - teststatus.textContent = '?'; + teststatus.innerHTML = MARKERS.maybe; storagepath.value = '[Empty]'; options.watcherURLRoot = url = path.value = options.watcherURLRoot.replace(/^(\:\d+)/, 'localhost$1').replace(/^(?!^http(s)?:)/, 'http$1://').replace(/\/+$/, ''); inusestatus.map(e => e.setAttribute('in-use', false)); @@ -868,7 +901,8 @@ function performWatcherTest({ QualityProfileID = 'Default', refreshing = false, __caught.imdb.push(item.movies.imdbid); __caught.tmdb.push(item.movies.tmdbid); }); - }); + }) + .catch(error => { LoadingAnimation(); new Notification('error', error); teststatus.innerHTML = MARKERS.no }); getWatcher(options, 'getconfig').then(configuration => { LoadingAnimation(); @@ -887,8 +921,7 @@ function performWatcherTest({ QualityProfileID = 'Default', refreshing = false, name }); - teststatus.textContent = '!'; - teststatus.classList = enabled.checked = !!profiles.length; + teststatus.innerHTML = MARKERS[+!(teststatus.classList = enabled.checked = !!profiles.length)]; inusestatus.map(e => e.setAttribute('in-use', enabled.checked)); if(!profiles.length) @@ -915,11 +948,11 @@ function performWatcherTest({ QualityProfileID = 'Default', refreshing = false, storagepath.value = path || '[Default Location]'; $('[data-option="watcherStoragePaths"i]').value = JSON.stringify(path || { path: '[Default Location]', id: 0 }); - }); + }) + .catch(error => { LoadingAnimation(); new Notification('error', error); teststatus.innerHTML = MARKERS.no }); } catch(error) { LoadingAnimation(); - - throw error; + new Notification('error', error); } } @@ -965,7 +998,7 @@ function performRadarrTest({ QualityProfileID, StoragePath, refreshing = false, inusestatus = [...$('[using="radarr"]', true)]; quality.innerHTML = ''; - teststatus.textContent = '?'; + teststatus.innerHTML = MARKERS.maybe; storagepath.textContent = ''; options.radarrURLRoot = url = path.value = options.radarrURLRoot.replace(/^(\:\d+)/, 'localhost$1').replace(/^(?!^http(s)?:)/, 'http$1://').replace(/\/+$/, ''); inusestatus.map(e => e.setAttribute('in-use', false)); @@ -978,14 +1011,14 @@ function performRadarrTest({ QualityProfileID, StoragePath, refreshing = false, __caught.imdb.push(movie.imdbId); __caught.tmdb.push(movie.tmdbId); }); - }); + }) + .catch(error => { LoadingAnimation(); new Notification('error', error); teststatus.innerHTML = MARKERS.no }); getRadarr(options, 'profile').then(profiles => { LoadingAnimation(); if(!profiles) return new Notification('error', 'Failed to get Radarr configuration'); - teststatus.textContent = '!'; - teststatus.classList = enabled.checked = !!profiles.length; + teststatus.innerHTML = MARKERS[+!(teststatus.classList = enabled.checked = !!profiles.length)]; inusestatus.map(e => e.setAttribute('in-use', enabled.checked)); if(!profiles.length) @@ -1008,7 +1041,8 @@ function performRadarrTest({ QualityProfileID, StoragePath, refreshing = false, // Because the was reset, the original value is lost. if(QualityProfileID) $('[data-option="__sonarrQuality"i]').value = quality.value = QualityProfileID; - }); + }) + .catch(error => { LoadingAnimation(); new Notification('error', error); teststatus.innerHTML = MARKERS.no }); let StoragePaths = []; getSonarr(options, 'rootfolder').then(storagepaths => { @@ -1136,11 +1171,11 @@ function performSonarrTest({ QualityProfileID, StoragePath, refreshing = false, storagepath.value = StoragePath; $('[data-option="__sonarrStoragePath"i]').value = StoragePaths.indexOf(StoragePath.replace(/\\/g, '/')) + 1; } - }); + }) + .catch(error => { LoadingAnimation(); new Notification('error', error); teststatus.innerHTML = MARKERS.no }); } catch(error) { LoadingAnimation(); - - throw error; + new Notification('error', error); } }; @@ -1186,7 +1221,7 @@ function performMedusaTest({ QualityProfileID, StoragePath, refreshing = false, inusestatus = [...$('[using="medusa"]', true)]; quality.innerHTML = ''; - teststatus.textContent = '?'; + teststatus.innerHTML = MARKERS.maybe; storagepath.textContent = ''; options.medusaURLRoot = url = path.value = options.medusaURLRoot.replace(/^(\:\d+)/, 'localhost$1').replace(/^(?!^http(s)?:)/, 'http$1://').replace(/\/+$/, ''); inusestatus.map(e => e.setAttribute('in-use', false)); @@ -1199,7 +1234,8 @@ function performMedusaTest({ QualityProfileID, StoragePath, refreshing = false, __caught.imdb.push(show.id.imdb) __caught.tvdb.push(show.id.tvdb); }); - }); + }) + .catch(error => { LoadingAnimation(); new Notification('error', error); teststatus.innerHTML = MARKERS.no }); getMedusa(options, 'config').then(configuration => { LoadingAnimation(); @@ -1211,8 +1247,7 @@ function performMedusaTest({ QualityProfileID, StoragePath, refreshing = false, profiles = qualities[profiles]; - teststatus.textContent = '!'; - teststatus.classList = enabled.checked = !!profiles.length; + teststatus.innerHTML = MARKERS[+!(teststatus.classList = enabled.checked = !!profiles.length)]; inusestatus.map(e => e.setAttribute('in-use', enabled.checked)); if(!profiles.length) @@ -1233,7 +1268,8 @@ function performMedusaTest({ QualityProfileID, StoragePath, refreshing = false, // Because the was reset, the original value is lost. if(QualityProfileID) $('[data-option="__sickBeardQuality"i]').value = quality.value = QualityProfileID; - }); + }) + .catch(error => { LoadingAnimation(); new Notification('error', error); teststatus.innerHTML = MARKERS.no }); let StoragePaths = []; getSickBeard(options, 'sb.getrootdirs').then(configuration => { @@ -1383,11 +1420,11 @@ function performSickBeardTest({ QualityProfileID, StoragePath, refreshing = fals $('[data-option="__sickBeardStoragePath"i]').value = storagepath.selectedIndex = StoragePaths.indexOf(StoragePath.replace(/\\/g, '/').replace(/\/+$/, '')); } - }); + }) + .catch(error => { LoadingAnimation(); new Notification('error', error); teststatus.innerHTML = MARKERS.no }); } catch(error) { LoadingAnimation(); - - throw error; + new Notification('error', error); } }; @@ -1402,10 +1439,8 @@ function performSickBeardTest({ QualityProfileID, StoragePath, refreshing = fals } function enableCouchPotato() { - let inusestatus = [...$('[using="couchpotato"]', true)]; - - inusestatus.map(e => e.setAttribute('in-use', true)); - $('#use-couchpotato').parentElement.removeAttribute('disabled'); + $('#enable-couchpotato', true).forEach(e => e.setAttribute('disabled', '')); + $('#using-couchpotato', true).forEach(e => e.parentElement.removeAttribute('disabled')); } function HandleProxySettings(data) { @@ -1423,7 +1458,42 @@ function HandleProxySettings(data) { }; } +function HandleProxyHeaders(Headers = "", URL = "") { + let headers = {}, + R = RegExp; + + Headers.replace(/^[ \t]*([^\=\s]+)[ \t]*=[ \t]*((["'`])(?:[^\\\3]*|\\.)\3|[^\f\n\r\v]*)/gm, ($0, $1, $2, $3, $$, $_) => { + let string = !!$3; + + if(string) { + headers[$1] = $2.replace(RegExp(`^${ $3 }|${ $3 }$`, 'g'), ''); + } else { + $2 = $2.replace(/@([\w\.]+)/g, (_0, _1, _$, __) => { + let property = top; + + for(let path of _1.split('.')) + if(/^(\w+)\(\s*\)$/.test(path)) + property = property[R.$1](); + else + property = property[path]; + + headers[$1] = property; + }) + .replace(/@\{b(ase-?)?64-url\}/gi, btoa(URL)) + .replace(/@\{enc(ode)?-url\}/gi, encodeURIComponent(URL)) + .replace(/@\{(raw-)?url\}/gi, URL); + + headers[$1] = $2; + } + }); + + return headers; +} + function saveOptions() { + if(RESETTING_SETTINGS) + return saveOptionsWhileResetting(); + ServerID = [...__servers__.selectedOptions][0]; if(!ServerID || !ServerID.value) { @@ -1497,6 +1567,8 @@ function saveOptions() { ClientID = window.crypto.getRandomValues(new Uint32Array(5)) .join('-'); } + new Notification('update', 'Saving...', 1500); + LoadingAnimation(true); storage.set({ ClientID }); options.plexURL = options.plexURLRoot = (options.plexURL || "https://app.plex.tv/") @@ -1560,8 +1632,6 @@ function saveOptions() { // Update status to let the user know the options were saved new Notification('update', 'Saved', 1500); } - new Notification('update', 'Saving...', 1500); - LoadingAnimation(true); let data = { ...options, @@ -1586,8 +1656,8 @@ function saveOptions() { storage.set(data, () => { LoadingAnimation(); - if(chrome.runtime.lastError) { - new Notification('error', 'Error with saving: ' + chrome.runtime.lastError.message); + if(browser.runtime.lastError) { + new Notification('error', 'Error with saving: ' + browser.runtime.lastError.message); storage.set(data, OptionsSavedMessage); } else { terminal.log('Saved Options: ', options); @@ -1608,6 +1678,9 @@ function saveOptions() { } function saveOptionsWithoutPlex() { + if(RESETTING_SETTINGS) + return saveOptionsWhileResetting(); + // See #4 let options = getOptionValues(), endingSlash = ($0, $1, $$, $_) => ($1 + (/\\/.test($_)? '\\': '/')); @@ -1650,6 +1723,7 @@ function saveOptionsWithoutPlex() { ClientID = 'web-to-plex:client'; storage.set({ ClientID }); } + new Notification('update', 'Saving...', 1500); // Still need to set this options.plexURL = options.plexURLRoot = "https://webtoplex.github.io/web/no.server/"; @@ -1710,7 +1784,6 @@ function saveOptionsWithoutPlex() { // Update status to let the user know the options were saved new Notification('update', 'Saved', 1500); } - new Notification('update', 'Saving...', 1500); let data = options; @@ -1728,8 +1801,37 @@ function saveOptionsWithoutPlex() { storage.set(data, () => { LoadingAnimation(); - if(chrome.runtime.lastError) { - new Notification('error', 'Error with saving: ' + chrome.runtime.lastError.message); + if(browser.runtime.lastError) { + new Notification('error', 'Error with saving: ' + browser.runtime.lastError.message); + storage.set(data, OptionsSavedMessage); + } else { + terminal.log('Saved Options: ', options); + OptionsSavedMessage(); + } + + browser.runtime.sendMessage({ + type: 'UPDATE_CONFIGURATION', + options, + }).then(response => { + if(response === undefined) { + console.warn(`Update response (UPDATE_CONFIGURATION): Invalid response...`, { response, options }); + } else { + console.log(`Update response (UPDATE_CONFIGURATION):`, { response, options }); + } + }); + }); +} + +function saveOptionsWhileResetting() { + LoadingAnimation(true); + + let options = getOptionValues(); + + storage.set(options, () => { + LoadingAnimation(); + + if(browser.runtime.lastError) { + new Notification('error', 'Error with saving: ' + browser.runtime.lastError.message); storage.set(data, OptionsSavedMessage); } else { terminal.log('Saved Options: ', options); @@ -1768,14 +1870,14 @@ function requestURLPermissions(url, callback) { return; // TODO: Firefox doesn't have support for the chrome.permissions API. - if(chrome.permissions) { + if(browser.permissions) { // When asking permissions the URL needs to have a trailing slash. - chrome.permissions.request({ origins: [`${ url }`] }, callback); + browser.permissions.request({ origins: [`${ url }`] }, callback); } } // Restores select box and checkbox state using the preferences -// stored in chrome.storage.* +// stored in browser.storage.* function restoreOptions(OPTIONS) { function setOptions(items) { if(!items) return; @@ -1837,6 +1939,7 @@ function restoreOptions(OPTIONS) { })({ ...builtin_sites, ...plugin_sites }); $('[data-option="__domains"i]').value = __domains; + $('[data-option="__defaults"i]').value = 'false'; } if (OPTIONS && typeof OPTIONS == 'string') { @@ -1847,7 +1950,7 @@ function restoreOptions(OPTIONS) { storage.get(null, items => { // Sigh... This is a workaround for Firefox; newer versions have support for the `chrome.storage.sync` API, // BUT, it will throw an error if you haven't enabled it... - if(chrome.runtime.lastError) + if(browser.runtime.lastError) storage.get(null, setOptions); else setOptions(items); @@ -1924,7 +2027,6 @@ let builtins = { "Netflix": "https://netflix.com/", "Verizon": "https://tv.verizon.com/", "Trakt": "https://trakt.tv/", - "Shana Project": "https://shanaproject.com/", "YouTube": "https://youtube.com/", "Rotten Tomatoes": "https://rottentomatoes.com/", "Fandango": "https://www.fandango.com/", @@ -1980,7 +2082,7 @@ for(let index = 0, length = builtin_array.length; builtinElement && index < leng `

${ title }

- +
@@ -2004,7 +2106,7 @@ for(let index = 0, length = builtin_array.length; builtinElement && index < leng `

${ title }

- +
@@ -2095,7 +2197,7 @@ for(let index = 0, length = plugin_array.length; pluginElement && index < length `

${ title }

- +
@@ -2119,7 +2221,7 @@ for(let index = 0, length = plugin_array.length; pluginElement && index < length `

${ title }

- +
@@ -2233,6 +2335,29 @@ addListener($('#erase_cache'), 'mouseup', event => { saveOptions(); }); +/* Erase ALL Settings */ +addListener($('#reset_settings'), 'mouseup', event => { + let options = JSON.stringify(getOptionValues()); + + $('[default]', true) + .forEach(el => { + let de = el.getAttribute('default'); + + if(el.type == 'checkbox') + el.checked = de === 'true'; + else if('contenteditable' in el.attributes) + el.innerHTML = de || ''; + else + el.value = de || ''; + }); + + Recall.CountEnabledSites(); + + new Notification('update', 'All settings have been reset'); + + RESETTING_SETTINGS = true; +}); + $('#version') .innerHTML = `v${ manifest.version }`; @@ -2250,7 +2375,7 @@ $('.checkbox', true) .forEach((element, index, array) => { addListener(element, 'mouseup', event => { let self = traverse(path(event), element => (element && element.type == 'checkbox'), true), - isChecked = e => e.setAttribute('in-use', self.checked); + isChecked = e => e.setAttribute('in-use', !self.checked); if(!self) return; @@ -2263,13 +2388,17 @@ $('.checkbox', true) switch(self.id.toLowerCase()) { /* Update the database when the option is toggled */ case 'use-lzw': - if(!self.checked) + let eneabled; + + if(enabled = !self.checked) new Notification('update', 'Compressing data...', 3000, () => new Notification('update', 'Compressed', 3000), false); else new Notification('update', 'Decompressing data...', 3000, () => new Notification('update', 'Decompressed', 3000), false); let options = getOptionValues(); + Recall.ToggleConfigurationAvailability(enabled); + for(let name in options) if(/^__/.test(name)) { if(!self.checked) @@ -2312,26 +2441,27 @@ $('.test', true) $('[data-option^="theme:"i], [data-option^="theme:"i] + label', true) .forEach((element, index, array) => { - addListener(element, 'mouseup', async event => { + let UpdateTheme; + + addListener(element, 'mouseup', UpdateTheme = async event => { let self = traverse(event.target, element => /^theme:/i.test(element.dataset.option), true), R = RegExp; - let [a, b] = self.getAttribute('theme').split(/\s*:\s*/).filter(v => v), + let [a, b] = self.getAttribute('theme').split(/^([^]+):([^]+?)$/).filter(v => v), value = `${self.dataset.option.replace(/^theme:/i, '')}-${b}`; if(/^(get|read|for)$/i.test(a)) - __theme.push(`${ value }=${ self.value }`) + __theme[value] = (self.value == 'true'? true: self.value == 'false'? false: self.value); else if(/^(checkbox)$/i.test(self.type) && (self.checked + '') != a) // backwards; fires late - __theme.push(value); - else if(/^(text|input|button|\B)$/i.test(self.type) && R(self.value + '', 'i').test(a)) - __theme.push(value); + __theme[value] = JSON.parse(a); + else if(/^(text|input|button|\B)$/i.test(self.type) && R(a, 'i').test(self.value)) + __theme[value] = self.value; else - __theme = __theme.filter(v => v != value); - - /* Get rid of repeats */ - // __theme = __theme.join('\u0000').replace(/([\w\-]+\=)([^\u0000]+?)\u0000\1[^\u0000]+?/g, ($0, $1, $2, $$, $_) => $1 + $2); + delete __theme[value]; }); + + setTimeout(() => UpdateTheme({ target: element }), 1000); }); let hold = document.createElement('summary'), @@ -2474,6 +2604,11 @@ $('[href^="#!/"]', true) }; }); +$('[id$="_test_status"]', true) + .forEach(element => { + element.innerHTML = MARKERS.maybe; + }); + // CORS exception: SecurityError // MUST be { window }, never { top } let { hash } = window.location; @@ -2494,7 +2629,7 @@ if(hash.length > 1) /* #!/SETTING[/SUB-SETTING] * #!/radarr - * #!/advance-settings/api-keys + * #!/advanced-settings/api-keys */ case '': break; @@ -2506,7 +2641,7 @@ if(hash.length > 1) /* Functions that require some time */ let Recall = { - '@auto': {}, // run at 100ms, and be recallable + '@auto': {}, // run at 100ms '@0sec': {}, // run at 1ms '@1sec': {}, // run at 1000ms }; @@ -2575,15 +2710,125 @@ Recall['@0sec'].SetVersionInfo = async() => { } if(DM) - await fetch('https://api.github.com/repos/SpaceK33z/web-to-plex/releases') + await fetch('https://api.github.com/repos/webtoplex/browser-extension/releases') .then(response => response.json()) .then(versions => useVer(versions[0])); else - await fetch('https://api.github.com/repos/SpaceK33z/web-to-plex/releases/latest') + await fetch('https://api.github.com/repos/webtoplex/browser-extension/releases/latest') .then(response => response.json()) .then(version => useVer(version)); }; +/* Get the user's IP address */ +Recall['@auto'].GetIPAddress = async() => { + let self = $('#ip-address'), + teststatus = $('#proxy_test_status'), + options = getOptionValues(); + + + self.innerHTML = 'Loading...'; + teststatus.innerHTML = MARKERS.maybe; + + let proxy = HandleProxySettings(options); + + if(proxy.enabled) { + let { url, headers } = proxy, + tor = 'https://check.torproject.org'; + + headers = HandleProxyHeaders(headers, tor); + + if(/(^https?:\/\/)(?!localhost|127\.0\.0\.1(?:\/8)?|::1(?:\/128)?|:\d+)\b/i.test(url)) { + url = url + .replace(/\{b(ase-?)?64-url\}/gi, btoa(tor)) + .replace(/\{enc(ode)?-url\}/gi, encodeURIComponent(tor)) + .replace(/\{(raw-)?url\}/gi, tor); + } else { + self.innerHTML = ''; + teststatus.innerHTML = MARKERS.no; + + throw new SyntaxError('Unable to proxy: The URL must be a public (HTTPS/HTTP) address'); + } + + await fetch(url, { mode: 'cors', headers }) + .then(results => results.text()) + .then(text => { + let DOM = new DOMParser, + html = DOM.parseFromString(text, 'text/html'), + strong = $('.content strong', false, html), + IPAddress; + + if(strong) + IPAddress = strong.textContent; + else if(/([\d\.]{7,15})/.test(html.body.textContent)) + IPAddress = RegExp.$1; + else + IPAddress = ''; + + self.innerHTML = IPAddress; + teststatus.innerHTML = MARKERS[+!IPAddress]; + }) + .catch(error => { + self.innerHTML = ''; + teststatus.innerHTML = MARKERS.no; + + new Notification('error', error); + }); + } else { + await fetch('https://check.torproject.org', { mode: 'cors' }) + .then(results => results.text()) + .then(text => { + let DOM = new DOMParser, + html = DOM.parseFromString(text, 'text/html'), + strong = $('.content strong', false, html), + IPAddress; + + if(strong) + IPAddress = strong.textContent; + else if(/([\d\.]{7,15})/.test(html.body.textContent)) + IPAddress = RegExp.$1; + else + IPAddress = ''; + + self.innerHTML = IPAddress; + teststatus.innerHTML = MARKERS[+!IPAddress]; + }) + .catch(error => { + self.innerHTML = ''; + teststatus.innerHTML = MARKERS.no; + + new Notification('error', error); + }); + } + + self.setAttribute('notice', `Public${proxy.enabled?' (Proxy)':''}`); +}; + +let ToggleConfigurationAvailabilityListener = false; +/* Setting the Configuration Data disabled state */ +Recall['@1sec'].ToggleConfigurationAvailability = (enabled = null) => { + if(enabled === null) + enabled = $('#use-lzw').checked; + + if(enabled) { + let parent = $('#json_data').parentElement; + + parent.setAttribute('disabled', ''); + + if(!ToggleConfigurationAvailabilityListener) + $('*:not([data-option])', true, parent).forEach(element => { + addListener(element, 'mousedown', event => { + let disabled = traverse(element, () => 'disabled' in element.attributes, false); + + if(disabled) + return event.preventDefault(); + }); + }); + ToggleConfigurationAvailabilityListener = true; + } else { + $('#json_data').parentElement.removeAttribute('disabled'); + } +}; + for(let func in Recall) { if(/^@/.test(func)) { let f; @@ -2602,15 +2847,21 @@ for(let func in Recall) { for(let fn in Recall[func]) { f = Recall[func][fn]; + Recall[fn] = f; setTimeout(f, 1); } break; - case '@1sec': + default: + /^@(\d+)sec$/i.test(func); + + let time = +RegExp.$1; + for(let fn in Recall[func]) { f = Recall[func][fn]; - setTimeout(f, 1000); + Recall[fn] = f; + setTimeout(f, time * 1000); } break; } @@ -2950,3 +3201,29 @@ function restoreSelection({ anchor, focus }, editor, key) { selection.setBaseAndExtent(nodes.anchor, index.anchor, nodes.focus, index.focus); } + +addListener($('#test-proxy-settings'), 'click', Recall.GetIPAddress); + +addListener($('#ip-address'), 'mouseup', async event => { + let self = event.target; + + self.innerHTML = 'Loading...'; + + await fetch('https://check.torproject.org', { mode: 'cors' }) + .then(results => results.text()) + .then(text => { + let DOM = new DOMParser, + html = DOM.parseFromString(text, 'text/html'), + strong = $('.content strong', false, html), + IPAddress; + + if(strong) + IPAddress = strong.textContent; + else if(/([\d\.]{7,15})/.test(html.body.textContent)) + IPAddress = RegExp.$1; + else + IPAddress = 'unknown'; + + self.innerHTML = IPAddress; + }); +}); diff --git a/moz/plugn.js b/moz/plugn.js index 99aa936..26d85b8 100644 --- a/moz/plugn.js +++ b/moz/plugn.js @@ -257,7 +257,15 @@ async function prepare({ code, alias, type, allowed, url }) { Type = type.replace(/^\w/, ($0, $$, $_) => $0.toUpperCase()); let org = url.origin, - ali = TLDHost(url.host); + ali = TLDHost(url.host), + runOnInit = ['(null)']; + + let funcs = { + minions: PLUGN_CONFIGURATION.UseMinions, + }; + for(let func in funcs) + runOnInit.push(`(${ funcs[func] } && ${ type }.${ func } instanceof Function? ${ type }.${ func }(): null)`); + runOnInit = runOnInit.join(','); let { authorized, ...A } = await GetAuthorization(alias); @@ -294,7 +302,7 @@ ${ ${ code .replace(/\/\/+\s*"([^\"\n\f\r\v]+?)"\s*requires?\:?\s*(.+)/i, ($0, $1, $2, $$, $_) => - `;(async() => await Require("${ $2 }", "${ alias }", "${ $1 }", "${ instance }"))();` + `;(async() => await Require("cache,${ $2 }", "${ alias }", "${ $1 }", "${ instance }"))();` ) .replace(/\b(chrome|browser)\.storage\.(sync|local|managed)\.?/g, ($0, $1, $2, $$, $_) => `;console.warn("This ${ type } attempted to access <${ $1 }.storage.${ $2 }>; use , , and instead.");` @@ -334,11 +342,17 @@ return ( /* "ready" is a sync (normal) function */ ${ type }.ready() )? - ${ type }.init( ${ Type }ReadyState ): + ( + ${ runOnInit }, + ${ type }.init( ${ Type }ReadyState ) + ): /* Injected ${ type } isn't ready */ (${ type }.timeout || 1000): /* Injected ${ type } doesn't have the "ready" property */ - ${ type }.init(): + ( + ${ runOnInit }, + ${ type }.init() + ): /* Injected ${ type } isn't properly structured */ (console.warn("The ${ type } (${ alias }) is incorrectly structured. Could not find required function ${ type }.init"), -1): /* URL doesn't match pattern */ @@ -358,8 +372,13 @@ let handle = async(results, tabID, instance, script, type) => { results = await results; + let extURL = url => browser.extension.getURL(url); + /* Always display a pretty button */ - browser.tabs.insertCSS(tabID, { file: 'common.css' }); + browser.tabs.insertCSS(tabID, { cssOrigin: 'user', file: 'common.css' }); + browser.tabs.insertCSS(tabID, { cssOrigin: 'user', file: 'theme.css' }); + browser.tabs.insertCSS(tabID, { cssOrigin: 'user', file: 'glyphs.css' }); + browser.tabs.insertCSS(tabID, { cssOrigin: 'user', file: 'colors.css' }); if((!results || !results[0] || !instance) && !FOUND[instance]) try { @@ -424,18 +443,9 @@ let handle = async(results, tabID, instance, script, type) => { data = { ...data, type, title, year }; - browser.tabs.sendMessage(tabID, { data, instance, [InstanceType.toLowerCase()]: script, instance_type: InstanceType, type: 'POPULATE' }) - .then(response => { - if(browser.runtime.lastError) - browser.runtime.lastError.message; - - PLUGN_TERMINAL.warn('Response [plugn]: ' + JSON.stringify(response)); - - if(!response) - PLUGN_TERMINAL.warn(`Terminated execution, response: ${ JSON.stringify(response) }`); - }); + browser.tabs.sendMessage(tabID, { data, instance, [InstanceType.toLowerCase()]: script, instance_type: InstanceType, type: 'POPULATE' }); } catch(error) { - throw new Error(`${ InstanceWarning } - ${ String(error) }`); + throw new Error(InstanceWarning + ' - ' + String(error)); } }; @@ -477,8 +487,6 @@ let tabchange = async tabs => { if(!allowed || !js) return; - let { authorized, ...A } = await GetAuthorization(js); - if(code) { browser.tabs.executeScript(id, { file: 'helpers.js' }, () => { // Sorry, but the instance needs to be callable multiple times @@ -494,18 +502,26 @@ let tabchange = async tabs => { browser.runtime.getURL(`plugin.${ js }.js`): `https://webtoplex.github.io/web/${ type }s/${ js }.js`; + let style = (PLUGN_DEVELOPER)? + browser.runtime.getURL(`${ js }.css`): + `https://webtoplex.github.io/web/styles/${ js }.css`; + await fetch(file, { mode: 'cors' }) .then(response => response.text()) .then(async code => { await browser.tabs.executeScript(id, { file: 'helpers.js' }, async() => { // Sorry, but the instance needs to be callable multiple times - await browser.tabs.executeScript(id, { + browser.tabs.executeScript(id, { code: (LAST = cache[ali] = await prepare({ code, alias: js, type, allowed, url })), - }, results => handle(results, LAST_ID = id, LAST_INSTANCE = instance, LAST_JS = js, LAST_TYPE = type)) + }).then(results => handle(results, LAST_ID = id, LAST_INSTANCE = instance, LAST_JS = js, LAST_TYPE = type)) }) }) .then(() => running.push(id, instance)) .catch(error => { throw error }); + + await fetch(style, { mode: 'cors' }) + .then(response => response.text()) + .then(async code => browser.tabs.insertCSS({ code })); }; // listen for message event @@ -551,6 +567,10 @@ browser.runtime.onMessage.addListener(processMessage = async(request = {}, sende browser.runtime.getURL(`plugin.${ plugin }.js`): `https://webtoplex.github.io/web/${ _type }s/${ options[_type] }.js`; + let style = (PLUGN_DEVELOPER)? + browser.runtime.getURL(`${ options[_type] }.css`): + `https://webtoplex.github.io/web/styles/${ options[_type] }.css`; + let { authorized, ...A } = await GetAuthorization(options[_type]); try { @@ -563,9 +583,9 @@ browser.runtime.onMessage.addListener(processMessage = async(request = {}, sende .then(async code => { await browser.tabs.executeScript(id, { file: 'helpers.js' }, async() => { // Sorry, but the instance needs to be callable multiple times - await browser.tabs.executeScript(id, { + browser.tabs.executeScript(id, { code: (LAST = cache[plugin] = await prepare({ code, alias: plugin, type: 'plugin', allowed, url })) - }, results => handle(results, LAST_ID = id, LAST_INSTANCE = instance, LAST_JS = plugin, LAST_TYPE = type)) + }).then(results => handle(results, LAST_ID = id, LAST_INSTANCE = instance, LAST_JS = plugin, LAST_TYPE = type)) }) }) .then(() => running.push(id, instance)) @@ -580,9 +600,9 @@ browser.runtime.onMessage.addListener(processMessage = async(request = {}, sende .then(async code => { await browser.tabs.executeScript(id, { file: 'helpers.js' }, async() => { // Sorry, but the instance needs to be callable multiple times - await browser.tabs.executeScript(id, { - code: (LAST = cache[plugin] = await prepare({ code, alias: plugin, type: 'script', allowed, url })) - }, results => handle(results, LAST_ID = id, LAST_INSTANCE = instance, LAST_JS = script, LAST_TYPE = type)) + browser.tabs.executeScript(id, { + code: (LAST = cache[script] = await prepare({ code, alias: script, type: 'script', allowed, url })) + }).then(results => handle(results, LAST_ID = id, LAST_INSTANCE = instance, LAST_JS = script, LAST_TYPE = type)) }) }) .then(() => running.push(id, instance)) @@ -591,7 +611,7 @@ browser.runtime.onMessage.addListener(processMessage = async(request = {}, sende // Soft reset (button reset) case '_INIT_': - browser.tabs.executeScript(id, { code: LAST }, results => handle(results, LAST_ID, LAST_INSTANCE, LAST_JS, LAST_TYPE)); + browser.tabs.executeScript(id, { code: LAST }).then(results => handle(results, LAST_ID, LAST_INSTANCE, LAST_JS, LAST_TYPE)); break; // Hard reset (program reset) @@ -609,10 +629,13 @@ browser.runtime.onMessage.addListener(processMessage = async(request = {}, sende case 'FOUND': FOUND[request.instance] = request.found; - // chrome.tabs.sendMessage(tab.id, { data: {}, instance, found: request.found, instance_type: type.toLowerCase(), type: 'POSTED' }); + // browser.tabs.sendMessage(tab.id, { data: {}, instance, found: request.found, instance_type: type.toLowerCase(), type: 'POSTED' }); break; case 'GRANT_PERMISSION': + if(!options[_type]) + return false; + await Save(`has/${ options[_type] }`, options.allowed); await Save(`get/${ options[_type] }`, options.permissions); break; @@ -640,6 +663,10 @@ browser.runtime.onMessage.addListener(processMessage = async(request = {}, sende return false; } + // await fetch(style, { mode: 'cors' }) + // .then(response => response.text()) + // .then(async code => browser.tabs.insertCSS({ code })); + return true; } catch(error) { PLUGN_TERMINAL.error(error); diff --git a/moz/rottentomatoes.css b/moz/rottentomatoes.css new file mode 100644 index 0000000..a757498 --- /dev/null +++ b/moz/rottentomatoes.css @@ -0,0 +1,43 @@ +.mid-top-actions .share-box { + padding-left: 20px!important; +} + +.web-to-plex-minion { + border: 1px solid #f3f3f3; + border-radius: 22px; + color: #fff!important; + font: inherit; + font-family: 'Franklin Gothic FS Med', Arila, Helvetica, Tahoma, Century, Verdana, sans-serif; + font-size: 16px; + font-weight: 400; + visibility: visible; + + margin-right: 8px; + + height: 44px; +} + +.web-to-plex-minion.wtp--download { + background-color: #f45a26!important; +} + +.web-to-plex-minion.wtp--download:hover { + background-color: #f67e56!important; +} + +.web-to-plex-minion.wtp--found { + background-color: #ca7c1f!important; +} + +.web-to-plex-minion.wtp--found:hover { + background-color: #f8c022!important; +} + +#tt--0-0 { + background: #666!important; +} + +#tt--0-0:hover { + background: #888!important; + border-color: #888!important; +} diff --git a/moz/rottentomatoes.js b/moz/rottentomatoes.js index f06644b..ce71ccd 100644 --- a/moz/rottentomatoes.js +++ b/moz/rottentomatoes.js @@ -1,11 +1,7 @@ let script = { "url": "*://*.rottentomatoes.com/([mt]|browse)/*", - "ready": () => { - let element = $('#reviews').first; - - return !!element; - }, + "ready": () => !$('#reviews').empty, "init": (ready) => { let _title, _year, _image, R = RegExp; @@ -83,4 +79,37 @@ let script = { return { type, title, image }; }, + + "minions": () => { + let actions = $('.franchiseLink, #topSection > *'), + type = script.getType(); + + if(actions.empty || type == 'error') + return; + let element = actions.first; + + if(type == 'movie') { + let minion; + + let parent = furnish('div', { style: 'box-shadow: none' }, + furnish('div.wts-button__container', {}, + minion = furnish('button.web-to-plex-minion.button--wts', { style: 'margin-bottom: 25px' }, + ' Web to Plex' + ) + ) + ); + + addMinions(minion); + element.appendChild(minion); + } else { + let minion; + + let parent = furnish('div.poster_button.hidden-xs', {}, + minion = furnish('a.web-to-plex-minion.fullWidth', {}, 'Web to Plex') + ); + + addMinions(minion); + element.appendChild(parent); + } + }, }; diff --git a/moz/theme.css b/moz/theme.css index 75130c8..806e25f 100644 --- a/moz/theme.css +++ b/moz/theme.css @@ -23,11 +23,11 @@ BUTTON [style].web-to-plex-button... LI [tooltip]#wtp-list-name.list-name // The actionable anchor A [tooltip][href][`id`].list-action - IMG [src=/img/48.png] // Web to Plex logo + IMG [src=48.png] // Web to Plex logo // The Plex It! button LI [tooltip]#wtp-plexit.list-item - IMG [src=/img/plexit.48.png] // Alarm bell + IMG [src=plexit.48.png] // Alarm bell // The Add to Plex It! button (normally hidden, until Plex It! is visible) LI [tooltip][data]#plexit.list-item @@ -35,22 +35,22 @@ BUTTON [style].web-to-plex-button... // The hide button LI [tooltip]#wtp-hide.list-item - IMG [src=/img/hide.48.png] // Eye icon + IMG [src=hide.48.png] // Eye icon // The refresh button LI [tooltip]#wtp-refresh.list-item - IMG [src=/img/reload.48.png] // Refresh + IMG [src=reload.48.png] // Refresh // The settings button LI [tooltip]#wtp-options.list-item - IMG [src=/img/settings.48.png] // Gear icon + IMG [src=settings.48.png] // Gear icon // The small, circular, colored indicator ::after */ -/* Button location */ -.web-to-plex-button.button-location-right { +/* Button location (Circular) */ +.web-to-plex-button.button-location-right:not(.button-shape-box) { left: unset !important; right: 5px !important; } @@ -68,7 +68,72 @@ BUTTON [style].web-to-plex-button... opacity: 0.10 !important; } -/* Button shape */ -.web-to-plex-button.button-shape-box { - /* ... */ +/* Button shape (Box) */ +.web-to-plex-button.button-shape-box.animate::before { + border-radius: 75px !important; +} + +.web-to-plex-button.button-shape-box, #plexit-bookmarklet-frame ~ .web-to-plex-button.button-shape-box { + border-radius: 0 !important; + border-bottom-left-radius: 2px !important; + border-bottom-right-radius: 2px !important; + + bottom: 0 !important; + margin-left: calc(50% - 34.5px) !important; + margin-top: -3px !important; + padding: 0 !important; + top: 0 !important; + + height: 20px !important; + width: 75px !important; +} + +.web-to-plex-button.button-shape-box .list-action img { + height: 16px !important; + width: 16px !important; +} + +.web-to-plex-button.button-shape-box:not(:hover) { + margin-top: -20px !important; +} + +.web-to-plex-button.button-shape-box::after { + border: 0 !important; + border-radius: 0 !important; + border-bottom: inherit !important; + + top: 100% !important; + + height: 2px !important; + width: 100% !important; +} + +.web-to-plex-button.button-shape-box:not(:hover)::after { + height: 6px !important; +} + +.web-to-plex-button.button-shape-box #wtp-list-name { + padding: 0 !important; + margin-top: 0 !important; + + width: 75px !important; +} + +.web-to-plex-button.button-shape-box #wtp-list-name img[src$="16.png"] { + height: 12px !important; +} + +.web-to-plex-button.button-shape-box:not(:hover) #wtp-list-name ~ * { + display: none !important; +} + +.web-to-plex-button.button-shape-box.open li:hover [tooltip]::before, .web-to-plex-button.button-shape-box.open [tooltip]::before { + background: #000c !important; + + left: -115px !important; + top: 30px !important; + z-index: 999999999 !important; + + height: -moz-fit-content !important; + width: 300px !important; } diff --git a/moz/tmdb.css b/moz/tmdb.css new file mode 100644 index 0000000..f6e5cef --- /dev/null +++ b/moz/tmdb.css @@ -0,0 +1,34 @@ +.web-to-plex-minion:hover, .web-to-plex-minion.wtp--downloader:hover, .web-to-plex-minion.wtp--found:hover { + cursor: pointer!important; + transition: background 0.2s, border 0.2s; +} + +.web-to-plex-minion, .web-to-plex-minion.wtp--downloader { + border-color: #f45a26!important; + background: #f45a26!important; + color: #fff!important; +} + +.web-to-plex-minion.wtp--downloader:hover { + border-color: #f67e56!important; + background: #f67e56!important; +} + +.web-to-plex-minion.wtp--found { + background: #e5a00d!important; + border-color: #e5a00d!important; + color: #fff!important; +} + +.web-to-plex-minion.wtp--found:hover { + background: #f9be03!important; +} + +#tt--0-0 { + background: #666!important; +} + +#tt--0-0:hover { + background: #888!important; + border-color: #888!important; +} diff --git a/moz/tmdb.js b/moz/tmdb.js index 5a98c24..27414d6 100644 --- a/moz/tmdb.js +++ b/moz/tmdb.js @@ -76,4 +76,22 @@ let script = { return { type, title, year, image, TMDbID }; }, + + "minions": () => { + let actions = $('.header .actions'); + + if(actions.empty) + return; + + actions.forEach(element => { + let minion; + + let parent = furnish('li.tooltip.use_tooltip', { title: 'Web to Plex' }, + minion = furnish('a.web-to-plex-minion', { style: `background: url("${ IMAGES.icon_32 }") center/50% no-repeat !important` }) + ); + + addMinions(minion); + element.insertBefore(parent, element.lastElementChild); + }); + }, }; diff --git a/moz/trakt.css b/moz/trakt.css new file mode 100644 index 0000000..5c11d0f --- /dev/null +++ b/moz/trakt.css @@ -0,0 +1,22 @@ +.web-to-plex-minion { + background-color: #f45a26!important; + border-color: #f45a26!important; + color: #fff!important; +} + +.web-to-plex-minion:hover { + background-color: #d43a06!important; +} + +.wtp-min.under-info[title]::after { + content: attr(title); +} + +#tt--0-0 { + color: #666!important; + text-decoration: line-through!important; +} + +#tt--0-0:hover { + color: #888!important; +} diff --git a/moz/trakt.js b/moz/trakt.js index 77c531d..71cb111 100644 --- a/moz/trakt.js +++ b/moz/trakt.js @@ -101,4 +101,42 @@ let script = { return { type, title, year }; }, + + "minions": () => { + let actions = $('#info-wrapper .action-buttons'); + + if(actions.empty) + return; + + actions.forEach(element => { + let subtitle; + + let minion = furnish('a.web-to-plex-minion.btn.btn-block.btn-summary.selected', {}, + furnish('i.fa.fa-fw.fa-download'), + furnish('div.text', {}, + furnish('div.main-info', {}, 'Web to Plex'), + subtitle = furnish('div.wtp-min.under-info', { + title: 'Loading...', + onmouseenter: event => { + let self = event.target, + title = self.getAttribute('title'); + + self.setAttribute('title', ''); + self.innerHTML = title; + }, + onmouseleave: event => { + let self = event.target, + title = self.innerHTML; + + self.setAttribute('title', title); + self.innerHTML = ''; + }, + }) + ) + ); + + addMinions(minion, subtitle); + element.insertBefore(minion, element.childNodes[3]); + }); + }, }; diff --git a/moz/tvmaze.css b/moz/tvmaze.css new file mode 100644 index 0000000..d503832 --- /dev/null +++ b/moz/tvmaze.css @@ -0,0 +1,27 @@ +.web-to-plex-minion { + background: #727272; + color: #fff!important; + text-decoration: none!important; + text-transform: none!important; +} + +.web-to-plex-minion.wtp--download { + background: #f45a26!important; +} + +.web-to-plex-wrapper:hover > .web-to-plex-minion.wtp--download, .web-to-plex-minion.wtp--download:hover { + background-color: #f67e56!important; + color: #ffffff!important; +} + +.web-to-plex-wrapper:hover > .web-to-plex-minion.wtp--found, .web-to-plex-minion.wtp--found:hover { + background-color: #f8c022!important; +} + +#tt--0-0 { + background: #666!important; +} + +#tt--0-0:hover { + background: #888!important; +} diff --git a/moz/tvmaze.js b/moz/tvmaze.js index 3332237..8a690d4 100644 --- a/moz/tvmaze.js +++ b/moz/tvmaze.js @@ -24,4 +24,28 @@ let script = { return pathname.replace(/\/shows\/(\d+).*/, '$1'); }, + + "minions": () => { + let actions = $('nav.page-subnav > ul'); + + if(actions.empty) + return; + + actions.forEach(element => { + let li = /^[ou]l$/i.test(element.tagName); + + let parent = furnish('li.web-to-plex-wrapper', {}), + minion = furnish(`a.web-to-plex-minion.${ li? 'flatButton': 'roundButton' }`, {}, 'Web to Plex'); + + if(li) { + parent.appendChild(minion); + element.appendChild(parent); + } else { + element.appendChild(minion); + } + + addMinions(minion); + }); + + }, }; diff --git a/moz/utils.js b/moz/utils.js index a44f68b..7e418ec 100644 --- a/moz/utils.js +++ b/moz/utils.js @@ -1,9 +1,105 @@ /* eslint-disable no-unused-vars */ /* global configuration, init, Update, "Helpers" */ -let configuration, init, Update; +var configuration, init, Update, IMAGES, Glyphs = {}, + HELPERS_STORAGE = { + get(keys, callback = () => {}) { + let results; + + if(keys === null) { + return callback(configuration); + } else if(keys instanceof String) { + return callback(configuration[keys]); + } else if(keys instanceof Array) { + results = [...keys]; + + for(let key of keys) + result.push(configuration[key]); + return callback(results); + } else if(keys instanceof Object) { + results = { ...keys }; + + for(let key in keys) + results[key] = configuration[key]; + return callback(results); + } + }, + + set(keys, callback = () => {}) { + let results = {}; + + for(let key in keys) + configuration[key] = results[key] = keys[key]; + return callback(results); + }, + + remove(keys, callback = () => {}) { + if(keys === null) + for(let key in configuration) + delete configuration[key]; + else if(keys instanceof String) + delete configuration[key]; + else if(keys instanceof Array) + for(let key of keys) + delete configuration[key]; + else if(keys instanceof Object) + for(let key in keys) + delete configuration[key]; + + callback(); + } + }, + MINIONS = [], + addMinions = (...minions) => { + MINIONS = [...minions, ...MINIONS]; + + return { + stayUnique: status => { + MINIONS = MINIONS.map(minion => ((!!~minions.indexOf(minion)? minion.setAttribute('ignore-web-to-plex-updates', status): null), minion)) + } + } + }; + +var UUID = UUID || class UUID { + constructor(length = 16, symbol = '-') { + let values = []; + + window.crypto.getRandomValues(new Uint32Array(length)).forEach(value => values.push(value.toString(36))); -(async date => { + return values.join(symbol).replace(/^[^a-z]+/i, ''); + } + + static from(object, seed = 64, symbol = '-') { + let id = []; + + for(let key in object) { + let o = object[key]; + + if(o instanceof Array) + o = o.join(symbol); + else if(o instanceof Object) + o = Object.values(o).join(symbol); + else + o = o + ''; + + if(typeof(o) == 'string') + o = o + .toLowerCase() + .split('') + .reduce((a, b) => ((typeof(a) == 'number'? a: a.charCodeAt(0)) + b.charCodeAt(0)), seed) + .toString(36); + else + return + /* Error occurred */; + + id.push(o); + } + + return id.join('').replace(/(\w{1,8})(\w{4})?(\w{4})?(\w{4})?(\w{12})?(\w+)?/, '$1-$2-$3-$4-$5:$6').replace(/\-+\:/g, ''); + } +}; + +var INITIALIZE = INITIALIZE || (async date => { // default date items let YEAR = date.getFullYear(), @@ -46,6 +142,15 @@ let configuration, init, Update; 'settings_icon_48': extURL('settings.48.png'), }; + for(let glyph of "adjust,airplane,alarm,albums,amazon,anchor,android,apple,asterisk,ax,badoo,ban,bank,barcode,baseball,basketball,bathrobe,beer,behance,bell,bicycle,bin,binoculars,blacksmith,blog,blogger,bluetooth,boat,bold,bomb,book,bookmark,bowling,briefcase,brush,bug,building,bullets,bullhorn,buoy,bus,cake,calculator,calendar,camera,candle,car,cardio,cargo,cars,celebration,certificate,charts,chat,check,cleaning,clock,cloud,cogwheel,cogwheels,coins,collapse,comments,compass,compressed,conversation,crop,crown,cup,cutlery,dashboard,delete,deviantart,direction,dislikes,display,divide,dog,download,dress,dribbble,drink,dropbox,dumbbell,earphone,edit,eject,electricity,embed,envelope,euro,evernote,exit,expand,eyedropper,fabric,facebook,factory,fax,female,file,film,filter,fins,fire,fishes,flag,flash,flickr,flower,font,forrst,forward,foursquare,fullscreen,gamepad,gbp,gift,girl,github,glass,global,globe,golf,goodreads,google_plus,grater,group,hdd,header,headphones,headset,heart,heat,history,hockey,home,hospital,imac,inbox,instagram,instapaper,ios,ipad,iphone,ipod,italic,jolicloud,justify,kettle,keynote,keys,kiosk,last_fm,leaf,leather,lightbulb,link,linked_in,list,lock,luggage,macbook,magic,magnet,male,microphone,minus,money,moon,more,move,music,mute,myspace,nails,nameplate,note,notes,ok,package,pants,paperclip,parents,pause,pen,pencil,piano,picasa,picture,pin,pinboard,pinterest,pipe,pizza,play,playlist,playstation,plus,podium,pool,posterous_spaces,pot,power,print,projector,pushpin,qrcode,quora,rabbit,radar,random,read_it_later,readability,record,redo,refresh,remove,repeat,restart,retweet,rewind,riflescope,ring,road,roundabout,router,rss,rugby,ruller,sampler,scissors,screenshot,search,send,server,settings,share,shield,shirt,shop,signal,skateboard,skitch,skull,skype,smoking,snowflake,sort,sorting,spade,spotify,spray,star,stats,stop,stopwatch,stroller,stumbleupon,subtitles,suitcase,sun,sweater,table,tablet,tag,tags,tie,tint,tower,train,transfer,translate,truck,tumblr,turtle,twitter,umbrella,unchecked,underwear,undo,unlock,unshare,upload,usd,user,vases,vcard,vimeo,vine,wallet,webcam,wifi,windows,woman,wordpress,wrench,xbox,xing,yahoo,yelp,youtube,zootool".split(',')) + Object.defineProperty(Glyphs, glyph, { + get() { return document.furnish('i', { glyph }) }, + set(value) { return document.furnish('i', { glyph: value }) }, + + configurable: true, + enumerable: true, + }); + // the storage - priority to sync const UTILS_STORAGE = browser.storage.sync || browser.storage.local; @@ -65,7 +170,7 @@ let configuration, init, Update; UTILS_STORAGE.get(null, DISK => { if(browser.runtime.lastError) browser.runtime.lastError.message || - browser.storage.local.get(null, LOAD); + UTILS_STORAGE.get(null, LOAD); else LOAD(DISK); }); @@ -302,15 +407,15 @@ let configuration, init, Update; .toLowerCase() ); - let preX = document.queryBy('.web-to-plex-prompt').first, - movie = /^(m(?:ovies?)?|f(?:ilms?)?|c(?:inemas?)?)?$/i; + let preX = document.queryBy('.web-to-plex-prompt').first, + movie = /^(m(?:ovies?)?|f(?:ilms?)?|c(?:inemas?)?|theat[re]{2})?$/i; if(preX) return /* Ignore while another prompt is open, prevents double prompts */; - options.imdb = options.IMDbID; - options.tmdb = options.TMDbID; - options.tvdb = options.TVDbID; + options.imdb = options.IMDbID; + options.tmdb = options.TMDbID; + options.tvdb = options.TVDbID; switch(prompt_type) { /* Allows the user to add and remove items from a list */ @@ -330,7 +435,7 @@ let configuration, init, Update; header.innerText = 'Approve ' + counter.children.length + (counter.children.length == 1?' item': ' items'); }; - prompt = furnish('div.web-to-plex-prompt', {}, + prompt = furnish('div.web-to-plex-prompt', { type: prompt_type }, furnish('div.web-to-plex-prompt-body', { style: `background-image: url(${ IMG_URL.noise_background }), url(${ IMG_URL.background }); background-size: auto, cover;` }, // The prompt's title furnish('h1.web-to-plex-prompt-header', {}, 'Approve ' + array.length + (array.length == 1? ' item': ' items')), @@ -347,14 +452,14 @@ let configuration, init, Update; elements.push( furnish('li.web-to-plex-prompt-option.mutable', { value: index, innerHTML: `

${ index + 1 }. ${ title }${ year? ` (${ year })`: '' } \u2014 ${ type }

` }, - furnish('button.remove', { title: `Remove "${ title }"`, onmouseup: event => { remove(event.target.parentElement); event.target.remove() } }), + furnish('i[glyph=remove]', { title: `Remove "${ title }"`, onmouseup: event => { remove(event.target.parentElement); event.target.remove() } }), ( __CONFIG__.PromptQuality? - P_QUA = furnish('select.quality', { index, onchange: event => data[event.target.getAttribute('index')].quality = event.target.value }, ...profiles[/(movie|film|cinema)/i.test(type)?'movie':'show'].map(Q => furnish('option', { value: Q.id }, Q.name))): + P_QUA = furnish('select.quality', { index, onchange: event => data[event.target.getAttribute('index')].quality = event.target.value }, ...profiles[movie.test(type)?'movie':'show'].map(Q => furnish('option', { value: Q.id }, Q.name))): '' ),( __CONFIG__.PromptLocation? - P_LOC = furnish('select.location', { index, onchange: event => data[event.target.getAttribute('index')].location = event.target.value }, ...locations[/(movie|film|cinema)/i.test(type)?'movie':'show'].map(Q => furnish('option', { value: Q.id }, Q.path))): + P_LOC = furnish('select.location', { index, onchange: event => data[event.target.getAttribute('index')].location = event.target.value }, ...locations[movie.test(type)?'movie':'show'].map(Q => furnish('option', { value: Q.id }, Q.path))): '' ) ) @@ -375,7 +480,7 @@ let configuration, init, Update; furnish('input.web-to-plex-prompt-input[type=text]', { placeholder: 'Add an item (enter to add): Title (Year) Type / ID Type', title: 'Solo: A Star Wars Story (2018) movie / tt3778644 m', onkeydown: async event => { if(event.keyCode == 13) { let title, year, type, self = event.target, R = RegExp, - movie = /^(m(?:ovies?)?|f(?:ilms?)?|c(?:inemas?)?)/i, + movie = /^(m(?:ovies?)?|f(?:ilms?)?|c(?:inemas?)?|theat[re]{2})/i, Db, IMDbID, TMDbID, TVDbID, value = self.value; self.setAttribute('disabled', self.disabled = true); @@ -421,9 +526,9 @@ let configuration, init, Update; } } } }), - furnish('button.web-to-plex-prompt-decline', { onmouseup: event => { remove(true); callback([]) }, title: 'Close' }, '\u2718'), - furnish('button.web-to-plex-prompt-accept', { onmouseup: event => { remove(true); new Prompt(prompt_type, options, callback, container) }, title: 'Reset' }, '\u21BA'), - furnish('button.web-to-plex-prompt-accept', { onmouseup: event => { remove(true); callback(data.filter(value => value !== null && value !== undefined)) }, title: 'Continue' }, '\u2714') + furnish('button.web-to-plex-prompt-decline', { onmouseup: event => { remove(true); callback([]) }, title: 'Close' }, Glyphs.exit), + furnish('button.web-to-plex-prompt-accept', { onmouseup: event => { remove(true); new Prompt(prompt_type, options, callback, container) }, title: 'Reset' }, Glyphs.restart), + furnish('button.web-to-plex-prompt-accept', { onmouseup: event => { remove(true); callback(data.filter(value => value !== null && value !== undefined)) }, title: 'Continue' }, Glyphs.ok) ) ) ); @@ -449,7 +554,7 @@ let configuration, init, Update; header.innerText = `Correction ready...`; }; - prompt = furnish('div.web-to-plex-prompt', {}, + prompt = furnish('div.web-to-plex-prompt', { type: prompt_type }, furnish('div.web-to-plex-prompt-body', { style: `background-image: url(${ IMG_URL.noise_background }), url(${ IMG_URL.background }); background-size: auto, cover;` }, // The prompt's title furnish('h1.web-to-plex-prompt-header', {}, (array.length == 1? 'Correction ready...': `Choose a correction from ${array.length} items`)), @@ -478,16 +583,21 @@ let configuration, init, Update; captured.tvdb.push(v); elements.push( - furnish('li.web-to-plex-prompt-option.mutable', { value: index, innerHTML: `

${ index + 1 }. ${ title }${ year? ` (${ year })`: '' } \u2014 ${ type }

${ i? `${i}`: '/' } \u2014 ${ t? `${t}`: '/' } \u2014 ${ v? `${v}`: '/' }` }, - furnish('button.choose', { title: `Use "${ title } (${ year })"`, onmouseup: event => { - let element = event.target.parentElement, - children = [...element.parentElement.children].filter(e => e != element); + furnish('li.web-to-plex-prompt-option.mutable.choose', { + value: index, + innerHTML: `

${ index + 1 }. ${ title }${ year? ` (${ year })`: '' } \u2014 ${ type }

${ i? `${i}`: '/' } \u2014 ${ t? `${t}`: '/' } \u2014 ${ v? `${v}`: '/' }`, + onmouseup: event => { + let self = traverse(event.target, element => element.classList.contains('mutable')), + children = [...self.parentElement.children].filter(e => e != self); children.forEach(child => { + child.classList.remove('chosen'); remove(child); - element.parentElement.appendChild(child); + self.parentElement.appendChild(child); }); - element.classList.add('chosen'); - } }) + self.classList.add('chosen'); + } + }, + furnish('i[glyph=ok]', { title: `Use "${ title } (${ year })"` }) ) ); } @@ -546,9 +656,9 @@ let configuration, init, Update; } } } }), - furnish('button.web-to-plex-prompt-decline', { onmouseup: event => { remove(true); prompt.done = true; callback([]) }, title: 'Close' }, '\u2718'), - furnish('button.web-to-plex-prompt-accept', { onmouseup: event => { remove(true); prompt.done = false; new Prompt(prompt_type, options, callback, container) }, title: 'Reset' }, '\u21BA'), - furnish('button.web-to-plex-prompt-accept', { onmouseup: event => { remove(true); prompt.done = true; callback(data.filter(value => value !== null && value !== undefined)) }, title: 'Continue' }, '\u2714') + furnish('button.web-to-plex-prompt-decline', { onmouseup: event => { remove(true); prompt.done = true; callback([]) }, title: 'Close' }, Glyphs.exit), + furnish('button.web-to-plex-prompt-accept', { onmouseup: event => { remove(true); prompt.done = false; new Prompt(prompt_type, options, callback, container) }, title: 'Reset' }, Glyphs.restart), + furnish('button.web-to-plex-prompt-accept', { onmouseup: event => { remove(true); prompt.done = true; callback(data.filter(value => value !== null && value !== undefined)) }, title: 'Continue' }, Glyphs.ok) ) ) ); @@ -570,7 +680,7 @@ let configuration, init, Update; header.innerText = 'Approve ' + counter.children.length + (counter.children.length == 1?' item': ' items'); }; - prompt = furnish('div.web-to-plex-prompt', {}, + prompt = furnish('div.web-to-plex-prompt', { type: prompt_type }, furnish('div.web-to-plex-prompt-body', { style: `background-image: url(${ IMG_URL.noise_background }), url(${ IMG_URL.background }); background-size: auto, cover;` }, // The prompt's title furnish('h1.web-to-plex-prompt-header', {}, 'Approve ' + array.length + (array.length == 1? ' item': ' items')), @@ -587,21 +697,21 @@ let configuration, init, Update; elements.push( furnish('li.web-to-plex-prompt-option.mutable', { value: index, innerHTML: `

${ index + 1 } \u00b7 ${ title }${ year? ` (${ year })`: '' } \u2014 ${ type }

` }, - furnish('button.remove', { title: `Remove "${ title }"`, onmouseup: event => { remove(event.target.parentElement); event.target.remove() } }), + furnish('i[glyph=remove]', { title: `Remove "${ title }"`, onmouseup: event => { remove(event.target.parentElement); event.target.remove() } }), ( __CONFIG__.PromptQuality? - P_QUA = furnish('select.quality', { index, onchange: event => data[event.target.getAttribute('index')].quality = event.target.value }, ...profiles[/(movie|film|cinema)/i.test(type)?'movie':'show'].map(Q => furnish('option', { value: Q.id }, Q.name))): + P_QUA = furnish('select.quality', { index, onchange: event => data[+event.target.getAttribute('index')].quality = event.target.value }, ...profiles[movie.test(type)?'movie':'show'].map(Q => furnish('option', { value: Q.id }, Q.name))): '' ),( __CONFIG__.PromptLocation? - P_LOC = furnish('select.location', { index, onchange: event => data[event.target.getAttribute('index')].location = event.target.value }, ...locations[/(movie|film|cinema)/i.test(type)?'movie':'show'].map(Q => furnish('option', { value: Q.id }, Q.path))): + P_LOC = furnish('select.location', { index, onchange: event => data[+event.target.getAttribute('index')].location = event.target.value }, ...locations[movie.test(type)?'movie':'show'].map(Q => furnish('option', { value: Q.id }, Q.path))): '' ) ) ); - if(P_QUA) P_QUA.value = defaults[type].quality; - if(P_LOC) P_LOC.value = defaults[type].location; + if(P_QUA) P_QUA.value = data[index].quality = defaults[type].quality; + if(P_LOC) P_LOC.value = data[index].location = defaults[type].location; P_QUA = P_LOC = null; } @@ -612,9 +722,9 @@ let configuration, init, Update; // The engagers furnish('div.web-to-plex-prompt-footer', {}, - furnish('button.web-to-plex-prompt-decline', { onmouseup: event => { remove(true); callback([]) }, title: 'Close' }, '\u2718'), - furnish('button.web-to-plex-prompt-accept', { onmouseup: event => { remove(true); new Prompt(prompt_type, options, callback, container) }, title: 'Reset' }, '\u21BA'), - furnish('button.web-to-plex-prompt-accept', { onmouseup: event => { remove(true); callback(data.filter(value => value !== null && value !== undefined)) }, title: 'Continue' }, '\u2714') + furnish('button.web-to-plex-prompt-decline', { onmouseup: event => { remove(true); callback([]) }, title: 'Close' }, Glyphs.exit), + furnish('button.web-to-plex-prompt-accept', { onmouseup: event => { remove(true); new Prompt(prompt_type, options, callback, container) }, title: 'Reset' }, Glyphs.restart), + furnish('button.web-to-plex-prompt-accept', { onmouseup: event => { remove(true); callback(data.filter(value => value)) }, title: 'Continue' }, Glyphs.ok) ) ) ); @@ -623,8 +733,15 @@ let configuration, init, Update; /* Allows the user to modify a single item (before being pushed) */ case 'modify': let { title, year, type, IMDbID, TMDbID, TVDbID } = options, + refined = { ...defaults[type], ...options }, + uuid = UUID.from({ type, title, IMDbID, TMDbID, TVDbID }), P_QUA, P_LOC; + if(REFINED[uuid]) + refined = REFINED[uuid]; + else + REFINED[uuid] = refined; + let i = IMDbID, t = TMDbID, v = TVDbID, @@ -645,34 +762,34 @@ let configuration, init, Update; element.remove(); }; - type = /(movie|film|cinema)/i.test(type)?'movie':'show'; + type = /(movie|film|cinema|theat[re]{2})s?/i.test(type)?'movie':'show'; - prompt = furnish('div.web-to-plex-prompt', {}, + prompt = furnish('div.web-to-plex-prompt', { type: prompt_type }, furnish('div.web-to-plex-prompt-body', { style: `background-image: url(${ IMG_URL.noise_background }), url(${ IMG_URL.background }); background-size: auto, cover;` }, // The prompt's title - furnish('h1.web-to-plex-prompt-header', { innerHTML: `${ title }${ year? ` (${ year })`: '' }` }), + furnish('h1.web-to-plex-prompt-header', { innerHTML: `${ title.length < 40? title: title.slice(0, 37) + '...' }${ parseInt(year)? ` (${ year })`: '' } \u2014 ${ movie.test(type)? 'Movie': 'TV Show' }` }), // The prompt's items furnish('div.web-to-plex-prompt-options', {}, - furnish('div.web-to-plex-prompt-option', { innerHTML: `${ type } \u2014 ${ i? `${i}`: '/' } \u2014 ${ t? `${t}`: '/' } \u2014 ${ v? `${v}`: '/' }` }), + furnish('div.web-to-plex-prompt-option', { innerHTML: `${ i? `${i}`: '?' } \u2014 ${ t? `${t}`: '?' } \u2014 ${ v? `${v}`: '?' }` }), ( __CONFIG__.PromptQuality? - P_QUA = furnish('select.quality', { onchange: event => options.quality = event.target.value }, ...profiles[type].map(Q => furnish('option', { value: Q.id }, Q.name))): + P_QUA = furnish('select.quality', { onchange: event => REFINED[uuid].quality = event.target.value }, ...profiles[type].map(Q => furnish('option', { value: Q.id }, Q.name))): '' ), furnish('br'), ( __CONFIG__.PromptLocation? - P_LOC = furnish('select.location', { onchange: event => options.location = event.target.value }, ...locations[type].map(Q => furnish('option', { value: Q.id }, Q.path))): + P_LOC = furnish('select.location', { onchange: event => REFINED[uuid].location = event.target.value }, ...locations[type].map(Q => furnish('option', { value: Q.id }, Q.path))): '' ) ), // The engagers furnish('div.web-to-plex-prompt-footer', {}, - furnish('button.web-to-plex-prompt-decline', { onmouseup: event => { remove(true); callback([]) }, title: 'Close' }, '\u2718'), - furnish('button.web-to-plex-prompt-accept', { onmouseup: event => { remove(true); new Prompt(prompt_type, options, callback, container) }, title: 'Reset' }, '\u21BA'), - furnish('button.web-to-plex-prompt-accept', { onmouseup: event => { remove(true); callback(options) }, title: 'Continue' }, '\u2714'), + furnish('button.web-to-plex-prompt-decline', { onmouseup: event => { remove(true); callback([]) }, title: 'Close' }, Glyphs.exit), + furnish('button.web-to-plex-prompt-accept', { onmouseup: event => { remove(true); new Prompt(prompt_type, REFINED[uuid], callback, container) }, title: 'Reset' }, Glyphs.restart), + furnish('button.web-to-plex-prompt-accept', { onmouseup: event => { remove(true); callback(REFINED[uuid]) }, title: 'Continue' }, Glyphs.ok), ( (!__CONFIG__.UseLowCache || (__CONFIG__.UseLowCache && CAUGHT.has({ imdb: i, tmdb: t, tvdb: v })))? furnish('button.web-to-plex-prompt-accept', { onmouseup: event => { let self = event.target; open(self.getAttribute('href'), '_blank') }, href: slugify(type), title: `Open on ${ manager[type] }` }, manager[type]): @@ -723,14 +840,14 @@ let configuration, init, Update; (init && !RUNNING? (init(), RUNNING = true): RUNNING = false); }; - prompt = furnish('div.web-to-plex-prompt', {}, + prompt = furnish('div.web-to-plex-prompt', { type: prompt_type }, furnish('div.web-to-plex-prompt-body', { style: `background-image: url(${ IMG_URL.noise_background }), url(${ IMG_URL.background }); background-size: auto, cover;` }, // The prompt's title furnish('h1.web-to-plex-prompt-header', { innerHTML: `"${ alias || name }" would like:` }), // The prompt's items furnish('div.web-to-plex-prompt-options', {}, - ...((permissions = permission.split(/\s*,\s*/).filter(v=>v&&v.length)).map( + ...((permissions = permission.split(/\s*,\s*/).filter((v,i,a)=>v&&v.length&&a.indexOf(v)==i)).map( __permission => furnish('div.web-to-plex-prompt-option.web-to-plex-permission', { innerHTML: `Access to your ${ __permission.replace(/(y)?s?$/, ($0, $1, $$, $_) => ($1? 'ies': 's')) } — ` + (p => { let R = RegExp, @@ -774,8 +891,8 @@ let configuration, init, Update; // The engagers furnish('div.web-to-plex-prompt-footer', {}, - furnish('button.web-to-plex-prompt-decline', { onmouseup: event => { if(!event.isTrusted) throw alert('The script for this site is trying to decline its own permissions!'), 'Malicious script. Decline permissions'; remove(true); callback(false, {}) }, title: 'Deny all permissions' }, '\u2718'), - furnish('button.web-to-plex-prompt-accept', { onmouseup: async event => { if(!event.isTrusted) throw alert('The script for this site is trying to grant its own permissions!'), 'Malicious script. Grant permissions'; remove(true); await callback(true, permissions); top.open(top.location.href, '_top'); }, title: 'Allow all permissions' }, '\u2714') + furnish('button.web-to-plex-prompt-decline', { onmouseup: event => { if(!event.isTrusted) throw alert('The script for this site is trying to decline its own permissions!'), 'Malicious script. Decline permissions'; remove(true); callback(false, {}) }, title: 'Deny all permissions' }, Glyphs.ban), + furnish('button.web-to-plex-prompt-accept', { onmouseup: async event => { if(!event.isTrusted) throw alert('The script for this site is trying to grant its own permissions!'), 'Malicious script. Grant permissions'; remove(true); await callback(true, permissions); top.open(top.location.href, '_top'); }, title: 'Allow all permissions' }, Glyphs.ok) ) ) ); @@ -825,6 +942,8 @@ let configuration, init, Update; // Send an update query to background.js Update = (type, options = {}, postToo) => { + Update.running = options.script || options.plugin || null; + if(configuration) console.log(`Requesting update (${ type } [post-to-top=${ !!postToo }])`, options); else if(!Update.retry) @@ -1008,7 +1127,7 @@ let configuration, init, Update; UTILS_STORAGE.get(null, options => { if(browser.runtime.lastError) browser.runtime.lastError.message || - browser.storage.local.get(null, handleOptions); + UTILS_STORAGE.get(null, handleOptions); else handleOptions(options); }); @@ -1019,13 +1138,17 @@ let configuration, init, Update; function ParsedOptions() { return options() .then( - options => { + async options => { configuration = {}; + let { running } = Update, + allowed = await load(`has/${ running }`), + permiss = await load(`get/${ running }`); + /* Don't expose the user's authentication information to sites */ for(let key in options) if(/username|password|token|api|server|url|storage|cache|proxy|client|builtin|plugin|qualit/i.test(key)) - if(ALLOWED && RegExp(PERMISS.join('|'),'i').test(key)) + if(allowed && RegExp(permiss.join('|'),'i').test(key)) configuration[key] = options[key]; else /* Do nothing */; @@ -1441,6 +1564,8 @@ let configuration, init, Update; .replace(/@\{b(ase-?)?64-url\}/gi, btoa(URL)) .replace(/@\{enc(ode)?-url\}/gi, encodeURIComponent(URL)) .replace(/@\{(raw-)?url\}/gi, URL); + + headers[$1] = $2; } }); @@ -1466,7 +1591,7 @@ let configuration, init, Update; rqut = apit, // request type: tmdb, imdb, or tvdb manable = __CONFIG__.ManagerSearch && !(rerun & 0b1000), // is the user's "Manager Searches" option enabled? UTF_16 = /[^0\u0020-\u007e, 1\u00a1\u00bf-\u00ff, 2\u0100-\u017f, 3\u0180-\u024f, 4\u0300-\u036f, 5\u0370-\u03ff, 6\u0400-\u04ff, 7\u0500-\u052f, 8\u20a0-\u20bf]+/g, - MV = /^(movies?|films?|cinemas?)$/i.test(apit), + MV = /^(movies?|films?|cinemas?|theat[re]{2}s?)$/i.test(apit), TV = /^(tv[\s\-]*(?:shows?|series)?)$/i.test(apit); iid = iid == 'tt'? null: iid; @@ -1475,7 +1600,7 @@ let configuration, init, Update; rqut = /(tv|show|series)/i.test(rqut)? 'tvdb': - /(movie|film|cinema)s?/i.test(rqut)? + /(movie|film|cinema|theat[re]{2})s?/i.test(rqut)? 'tmdb': rqut || '*'; manable = manable && (__CONFIG__.usingOmbi || (__CONFIG__.usingRadarr && rqut == 'tmdb') || ((__CONFIG__.usingSonarr || __CONFIG__.usingMedusa /*|| __CONFIG__.usingSickBeard*/) && rqut == 'tvdb')); @@ -1572,7 +1697,7 @@ let configuration, init, Update; cors = proxy.url, // if cors is requried and not uspported, proxy through this URL headers = HandleProxyHeaders(proxy.headers, url); - if(proxy.enabled && /(^http:\/\/)(?!localhost|127\.0\.0\.1(?:\/8)?|::1(?:\/128)?|:\d+)\b/i.test(url)) { + if(proxy.enabled && /(^https?:\/\/)(?!localhost|127\.0\.0\.1(?:\/8)?|::1(?:\/128)?|:\d+)\b/i.test(url)) { url = cors .replace(/\{b(ase-?)?64-url\}/gi, btoa(url)) .replace(/\{enc(ode)?-url\}/gi, encodeURIComponent(url)) @@ -2031,7 +2156,7 @@ let configuration, init, Update; ); } - let contentType = (/movies?|film/i.test(options.type)? 'movie': 'tv'); + let contentType = (/(movie|film|cinema|theat[re]{2})s?/i.test(options.type)? 'movie': 'tv'); browser.runtime.sendMessage({ type: 'PUSH_OMBI', @@ -2424,28 +2549,24 @@ let configuration, init, Update; let { __theme } = __CONFIG__; - let ThemeClasses = JSON.parse(COMPRESS? iBWT(unzip(decompress(__theme))): __theme), + __theme = JSON.parse(COMPRESS? iBWT(unzip(decompress(__theme))): __theme); + + let ThemeClasses = [], HeaderClasses = [], ParsedAttributes = {}; // Theme(s) + for(let theme in __theme) + if((typeof __theme[theme]) == 'boolean') + ThemeClasses.push(theme); + else + ParsedAttributes[theme] = __theme[theme]; + if(!ThemeClasses.length) ThemeClasses = ''; else ThemeClasses = '.' + ThemeClasses.join('.'); - ThemeClasses = ThemeClasses.split('.').filter(v => { - let R = RegExp; - - if(/([^=]+?)=([^.]+?)/.test(v)) { - ParsedAttributes[R.$1] = R.$2; - - return false; - } - - return true; - }).join('.'); - // Header(s) for(let header in headers) if(headers[header]) @@ -2472,7 +2593,7 @@ let configuration, init, Update; self.classList.remove('open', 'animate'); self.classList.add('closed'); }, - style: `background-image: url(${ IMG_URL.noise_background }), url(${ IMG_URL.background }); background-size: auto, cover;` + style: `display: none; background-image: url(${ IMG_URL.noise_background }), url(${ IMG_URL.background }); background-size: auto, cover;` }, //
    furnish('ul', {}, @@ -2489,33 +2610,32 @@ let configuration, init, Update; (d=>{let s=d.createElement('script'),h=d.querySelector('head');s.type='text/javascript';s.src='//webtoplex.github.io/plex.it.js';h.appendChild(s)})(document); } }, - furnish('img[alt=Favorite]', { src: IMG_URL.plexit_icon_48, onmouseup: event => event.target.parentElement.click() }) // + furnish('i[red][gradient=lighten]', { glyph: 'fire 3x', onmouseup: event => event.target.parentElement.click() }) // ), furnish('li#wtp-hide.list-item', { tooltip: 'Hide Web to Plex', onmouseup: event => { - let self = $('#wtp-hide').first, state = self.getAttribute('state') || 'show'; + let self = $('#wtp-hide').first, + state = (self.getAttribute('state') || 'show'), + visual = (state == 'show'? 'sun': 'cloud'); button.classList.remove(state); - self.setAttribute('tooltip', state.toCaps() + ' Web to Plex'); - let img = self.querySelector('img'); - - img && (img.src = state == 'show'? IMG_URL.show_icon_48: IMG_URL.hide_icon_48); + state = state == 'show'? 'hide': 'show'; - if(state == 'show') { - state = 'hide'; - } else { - state = 'show'; - } + let g = $('[glyph]', self).first; + g.setAttribute('glyph', `${ visual } 3x`); + g.removeAttribute('blue'); + g.removeAttribute('orange'); + g.setAttribute((visual == 'sun'? 'orange': 'blue'), ''); button.classList.add(state); self.setAttribute('state', state); } }, - furnish('img[alt=Hide]', { src: IMG_URL.hide_icon_48, onmouseup: event => event.target.parentElement.click() }) // + furnish('i[blue][gradient=lighten]', { glyph: 'cloud 3x', onmouseup: event => event.target.parentElement.click() }) // ), furnish('li#wtp-refresh.list-item', { @@ -2529,7 +2649,7 @@ let configuration, init, Update; new Notification('warning', "Couldn't reload. Please refresh the page."); } }, - furnish('img[alt=Reload]', { src: IMG_URL.reload_icon_48, onmouseup: event => event.target.parentElement.click() }) // + furnish('i[orange][gradient=lighten]', { glyph: 'restart 3x', onmouseup: event => event.target.parentElement.click() }) // ), furnish('li#wtp-options.list-item', { @@ -2540,7 +2660,7 @@ let configuration, init, Update; return Options(); } }, - furnish('img[alt=Settings]', { src: IMG_URL.settings_icon_48, onmouseup: event => event.target.parentElement.click() }) // + furnish('i[grey][gradient=lighten]', { glyph: 'cogwheel 3x', onmouseup: event => event.target.parentElement.click() }) // ) // ) @@ -2870,7 +2990,7 @@ let configuration, init, Update; results = results.filter(v => v.status == 'downloader'); - let img = furnish('img', { title: 'Add to Plex It!', src: IMG_URL.plexit_icon_48, onmouseup: event => {let frame = document.querySelector('#plexit-bookmarklet-frame'); frame.src = frame.src.replace(/(#plexit:.*)?$/, '#plexit:' + event.target.parentElement.getAttribute('data'))} }), + let img = furnish('img', { title: 'Add to Plex It!', onmouseup: event => {let frame = document.querySelector('#plexit-bookmarklet-frame'); frame.src = frame.src.replace(/(#plexit:.*)?$/, '#plexit:' + event.target.parentElement.getAttribute('data'))} }), po, pi = furnish('li#plexit.list-item', { data: encode(JSON.stringify(results)) }, img), op = document.querySelector('#wtp-plexit'); @@ -3085,7 +3205,8 @@ let configuration, init, Update; case 'INITIALIZE': UTILS_TERMINAL.LOG('Told to reinitialize...'); - init && init(); + document.queryBy('.web-to-plex-button').map(e => e.remove()); + INITIALIZE(new Date); return true; case 'NO_RENDER': @@ -3155,7 +3276,7 @@ let configuration, init, Update; // create the sleeping button wait(() => document.readyState === 'complete', () => RenderButton(false, { sleeper: true })); -})(new Date); +}); /* Helpers */ /* Zipping Algorithm */ @@ -3290,6 +3411,49 @@ function wait(on, then) { setTimeout(() => wait(on, then), 50); } +function addListener(element, eventName, callback = event => {}) { + eventName = eventName.replace(/^(on)?/, 'on'); + callback = callback.toString().replace(/;+$/g, ''); + + let event = element.getAttribute(eventName); + + if(event && event.length) + event = `${ event }; ${ callback }`; + else + event = callback; + + element[eventName] = eval(event); +} + +function traverse(element, until, siblings = false) { + let elements; + + if(siblings) { + if(element instanceof Array || element instanceof NodeList) { + for(elements = [...element], element = elements[0]; element && until(element) === false;) + if(element.previousElementSibling) + element = element.previousElementSibling; + else + elements.splice(0, 1), + element = elements[0]; + } else { + while(until(element) === false && element) + element = element.previousElementSibling || element.parentElement; + } + } + + if(element instanceof Array || element instanceof NodeList) { + for(element = [...element]; element.length && until(element[0]) === false; element.splice(0, 1)) + continue; + element = element[0]; + } else { + while(until(element) === false && element) + element = element.parentElement; + } + + return element; +} + // the custom "on location change" event function watchlocationchange(subject) { let locationchangecallbacks = watchlocationchange.locationchangecallbacks; @@ -3579,7 +3743,7 @@ Object.filter = Object.filter || function filter(object, prejudice) { } })(document); -let PRIMITIVE = Symbol.toPrimitive, +var PRIMITIVE = Symbol.toPrimitive, queryBy = document.queryBy, furnish = document.furnish; @@ -3587,3 +3751,5 @@ queryBy[PRIMITIVE] = furnish[PRIMITIVE] = String.prototype.toCaps[PRIMITIVE] = ( if(browser.runtime.lastError) browser.runtime.lastError.message; + +INITIALIZE(new Date); diff --git a/moz/verizon.css b/moz/verizon.css new file mode 100644 index 0000000..a789420 --- /dev/null +++ b/moz/verizon.css @@ -0,0 +1,34 @@ +.web-to-plex-minion { + border-color: #f45a26!important; + background: #f45a26!important; + color: #fff; +} + +.web-to-plex-minion.wtp--download { + border-color: #f45a26!important; + background: #f45a26!important; + color: #fff!important; +} + +.web-to-plex-minion.wtp--download:hover { + background: #f67e56!important; +} + +.web-to-plex-minion.wtp--found { + background: #e5a00d!important; + border-color: #e5a00d!important; + color: #fff!important; +} + +.web-to-plex-minion.wtp--found:hover { + background: #f9be03!important; +} + +#tt--0-0 { + background: #666!important; +} + +#tt--0-0:hover { + background: #888!important; + border-color: #888!important; +} diff --git a/moz/verizon.js b/moz/verizon.js index 8a89d99..f7ca257 100644 --- a/moz/verizon.js +++ b/moz/verizon.js @@ -1,7 +1,7 @@ let script = { "url": "*://*.verizon.com/*/(movie|show)s?/*", - "ready": () => !$('.container .btn-with-play, .moredetails, .more-like').empty, + "ready": () => !$('.container .btn-with-play, .moredetails, .more-like, #more_like_this').empty, "init": (ready) => { let _title, _year, _image, R = RegExp; @@ -52,6 +52,20 @@ let script = { 'error' }, + "minions": () => { + let actions = $('.container .content-holder, .detail .fl, [class^="primaryButtons"]'); + + if(actions.empty) + return; + + actions.forEach(element => { + let minion = furnish('a.web-to-plex-minion.button.btn.detail-btn', { style: 'padding-top: 3% !important' }, 'Web to Plex'); + + addMinions(minion); + element.appendChild(minion); + }); + }, + ondemand: /\bondemand\b/i.test(top.location.pathname), watch: /\bwatch\b/i.test(top.location.pathname), diff --git a/moz/vrv.css b/moz/vrv.css new file mode 100644 index 0000000..95d2961 --- /dev/null +++ b/moz/vrv.css @@ -0,0 +1,57 @@ +.web-to-plex-minion:nth-child(2) { + margin-left: 0.625rem; +} + +.web-to-plex-minion { + box-shadow: inset 0 0 0 0.125rem var(--orange)!important; + color: var(--orange)!important; +} + +.web-to-plex-minion:hover { + background-color: var(--orange)!important; + color: var(--light-white)!important; +} + +.web-to-plex-minion.wtp--found { + box-shadow: inset 0 0 0 0.125rem var(--orange)!important; + color: var(--orange)!important; +} + +.web-to-plex-minion.wtp--found:hover { + background-color: var(--orange)!important; + color: var(--light-white)!important; +} + +.web-to-plex-minion.wtp--download { + box-shadow: inset 0 0 0 0.125rem var(--blue)!important; + color: var(--blue)!important; +} + +.web-to-plex-minion.wtp--download:hover { + background-color: var(--blue)!important; + color: var(--light-white)!important; +} + +.web-to-plex-minion.wtp--error { + box-shadow: inset 0 0 0 0.125rem var(--red)!important; + color: var(--red)!important; +} + +.web-to-plex-minion.wtp--error:hover { + background-color: var(--red)!important; + color: var(--light-white)!important; +} + +.watchlist-card .web-to-plex-minion { + background: 0!important; + box-shadow: none!important; +} + +#tt--0-0 { + box-shadow: inset 0 0 0 0.125rem var(--grey)!important; + color: var(--grey)!important; +} + +#tt--0-0:hover { + background-color: var(--grey)!important; +} diff --git a/moz/vrv.js b/moz/vrv.js index 1e1f7af..6cafee2 100644 --- a/moz/vrv.js +++ b/moz/vrv.js @@ -1,11 +1,11 @@ let script = { - "url": "*://*.vrv.co/(series|watch)/", + "url": "*://*.vrv.co/(series|watch(list)?)\\b", "ready": () => { let img = $('.h-thumbnail > img').first, pre = $('#content .content .card').first; - return script.getType('list')? pre && pre.textContent: img && img.src; + return (script.getType('list')? pre && pre.textContent: img && img.src) || $('.erc-spinner').empty; }, "init": (ready) => { @@ -29,7 +29,7 @@ let script = { break; case 'list': - let items = $('#content .content .card'); + let items = $('#content .content [class$="card"]'); options = []; @@ -67,14 +67,51 @@ let script = { }, "process": (element) => { - let title = $('.info > *', element).first, - image = $('.poster-image img', element).first, - type = $('.info [class*="series"], .info [class*="movie"]', element).first; + let title = $('[class*="content-title"]', element).first, + image = $('[class*="image-poster"]', element).first, + type = $('[class*="meta-tags"][class*="type"]', element).first; title = title.textContent.trim(); image = image.src; - type = type.getAttribute('class').replace(/[^]*(movie|series)[^]*/, '$1'); + type = type.textContent.trim().replace(/[^]*(movie|series)[^]*/i, '$1').toLowerCase(); return { type, title, image }; }, + + "minions": () => { + let actions = $('.action-buttons, .watchlist-card .c-watchlist-card__actions-wrapper'), + list = script.getType('list'); + + if(actions.empty) + return; + + let processed; + + if(!list) + processed = script.init(); + + actions.forEach(element => { + if(list) + processed = script.process($(element).parent('.watchlist-card')[0]); + + let { type, title } = processed, + uuid = UUID.from({ type, title }); + + let minion = furnish(`a.web-to-plex-minion.${(list? 'c-watchlist-card__icon': 'action-button.c-button')}`, { uuid, style: 'box-shadow: 0 0 0 0.125rem;' }, + ( + list? + furnish('img', { src: IMAGES.icon_16 }): + 'Web to Plex' + ) + ); + + if(list) { + addMinions(minion).stayUnique(true); + element.insertBefore(minion, element.firstElementChild); + } else { + addMinions(minion); + setTimeout(() => element.appendChild(minion), 5000); + } + }); + }, }; diff --git a/moz/vudu.css b/moz/vudu.css new file mode 100644 index 0000000..8af438d --- /dev/null +++ b/moz/vudu.css @@ -0,0 +1,59 @@ +.web-to-plex-minion { + color: #566273!important; + background: transparent; + border: 2px solid #566273; + height: 30px; + font-size: 15px; + line-height: 25px; + border-radius: 6px; + font-weight: 700; + text-align: center; + display: block; + outline: none; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + cursor: pointer; + text-decoration: none!important; + margin-top: 12px; +} + +.web-to-plex-minion:hover { + opacity: 0.7; +} + +[class*="web-to-plex-wrapper"] { + width: 33.333333%!important; +} + +*:not(:first-child) ~ [class*="web-to-plex-wrapper"] { + margin-top: 35px!important; +} + +.web-to-plex-minion.wtp--download { + border-color: #f45a26!important; + color: #f45a26!important; +} + +[class*="web-to-plex-wrapper"]:hover > .web-to-plex-minion.wtp--download { + border-color: #f67e56!important; + color: #f67e56!important; +} + +.web-to-plex-minion.wtp--found { + border-color: #ca7c1f!important; + color: #ca7c1f!important; +} + +[class*="web-to-plex-wrapper"]:hover > .web-to-plex-minion.wtp--found { + border-color: #f8c022!important; + color: #f8c022!important; +} + +#tt--0-0 { + border-color: #666!important;; +} + +#tt--0-0:hover { + border-color: #888!important; +} diff --git a/moz/vudu.js b/moz/vudu.js index 15af40a..a96aad7 100644 --- a/moz/vudu.js +++ b/moz/vudu.js @@ -29,4 +29,19 @@ let script = { 'show': 'movie'; }, + + "minions": () => { + let actions = $('.container .row:nth-child(3) .row > *, .container .row:nth-child(3) ~ * .row > *'); + + console.log({ actions }) + + if(actions.empty) + return; + + let element = actions.child(6), + minion = furnish('a.web-to-plex-minion', {}, 'Web to Plex'); + + addMinions(minion); + element.appendChild(minion); + }, }; diff --git a/opa.crx b/opa.crx index 5b9f4f3..81192ec 100644 Binary files a/opa.crx and b/opa.crx differ diff --git a/opa.zip b/opa.zip index 7996fcd..c7d9ba9 100644 Binary files a/opa.zip and b/opa.zip differ diff --git a/opa/@@layout.js b/opa/@@layout.js new file mode 100644 index 0000000..9e2b387 --- /dev/null +++ b/opa/@@layout.js @@ -0,0 +1,39 @@ +// optional +// "Friendly Name" requires: api|username|password|token +// api - the user's API keys (external, such as TMDb/OMDb) +// username - the user's usernames (internal, such as Radarr/Sonarr/etc.) +// password - the user's passwords (internal) +// token - the user's tokens (internal) +// Example: "Web to Plex" requires: api, token + +let script = { + // required + "url": "< URL RegExp >", + // Example: *://*.amazon.*/*/video/(detail|buy)/* + // *:// - match any protocol (http, https, etc.) + // *.amazon - match any sub-domain (www, ww5, etc.) + // .* - match any TLD (com, net, org, etc.) + // /* - match any path + // (detail|buy) - match one of the items + + // optional + "ready": () => { /* return a boolean to describe if the page is ready */ }, + + // optional + "timeout": 1000, // if the script fails to complete, retry after ... milliseconds + + // required + "init": (ready) => { + let _title, _year, _image, R = RegExp; + + let title = $('#title').first, + year = $('#year').first, + image = $('#image').first, + type = script.getType(); // described below + + return { type, title, year, image }; + }, + + // optional | functionality only + "getType": () => 'movie' || 'show', +}; diff --git a/opa/@@test.js b/opa/@@test.js new file mode 100644 index 0000000..5ec2c9a --- /dev/null +++ b/opa/@@test.js @@ -0,0 +1,37 @@ +let script = { + // required + "url": "*://webtoplex.github.io/web/test/*", + // Example: *://*.amazon.com/*/video/(detail|buy)/* + // *:// - match any protocol (http, https, etc.) + // *.amazon.com - match any sub-domain (www, ww5, etc.) + // /* - match any path + // (detail|buy) - match one of the items + + // optional + "ready": () => { + /* return a boolean to describe if the page is ready */ + return !!$('#title').first.textContent.length; + }, + + // optional + "timeout": 1000, // if the script fails to complete, retry after ... milliseconds + + // required + "init": (ready) => { + let _title, _year, _image, R = RegExp; + + let title = $('#title').first, + year = $('#year').first, + image = $('#poster').first, + type = script.getType(); // described below + + title = title.textContent; + year = +year.textContent; + image = image.src || ''; + + return { type, title, year, image }; + }, + + // optional | functionality only + "getType": () => $('#example').first.getAttribute('type'), +}; diff --git a/opa/Glyphicons Social.woff b/opa/Glyphicons Social.woff new file mode 100644 index 0000000..2ccc999 Binary files /dev/null and b/opa/Glyphicons Social.woff differ diff --git a/opa/Glyphicons.woff b/opa/Glyphicons.woff new file mode 100644 index 0000000..c0114ce Binary files /dev/null and b/opa/Glyphicons.woff differ diff --git a/opa/Plex.bold.woff b/opa/Plex.bold.woff new file mode 100644 index 0000000..cde225e Binary files /dev/null and b/opa/Plex.bold.woff differ diff --git a/opa/Plex.bold.woff2 b/opa/Plex.bold.woff2 new file mode 100644 index 0000000..9217544 Binary files /dev/null and b/opa/Plex.bold.woff2 differ diff --git a/opa/Plex.woff b/opa/Plex.woff new file mode 100644 index 0000000..133f3d6 Binary files /dev/null and b/opa/Plex.woff differ diff --git a/opa/Plex.woff2 b/opa/Plex.woff2 new file mode 100644 index 0000000..b82c15a Binary files /dev/null and b/opa/Plex.woff2 differ diff --git a/opa/amazon.css b/opa/amazon.css new file mode 100644 index 0000000..dac83ab --- /dev/null +++ b/opa/amazon.css @@ -0,0 +1,32 @@ +.web-to-plex-minion { + margin-right: 8px; + color: #fff!important; + margin-bottom: 8px!important; + line-height: 2!important; + text-decoration: none!important; + width: auto!important; +} + +.web-to-plex-minion.wtp--download { + background: linear-gradient(180deg, #f67e56, #f45a26)!important; +} + +.web-to-plex-minion.wtp--download:hover { + background: linear-gradient(180deg, #ff8960, #fd6430)!important; +} + +.web-to-plex-minion.wtp--found { + background: linear-gradient(180deg, #f7dfa5, #ca7c1f)!important; +} + +.web-to-plex-minion.wtp--found:hover { + background: linear-gradient(180deg, #f7dfa5, #f8c022)!important; +} + +#tt--0-0 { + background: linear-gradient(180deg, #70767d 0, #696f78 0, #3d444e 100%)!important; +} + +#tt--0-0:hover { + background: linear-gradient(180deg, #70767d 0, #696f78 0, #3d444e 100%)!important; +} diff --git a/opa/amazon.js b/opa/amazon.js index 0779401..e0b3fe1 100644 --- a/opa/amazon.js +++ b/opa/amazon.js @@ -52,4 +52,18 @@ let script = { 'tv': 'movie' }, + + "minions": () => { + let actions = $(script.getType() == 'tv'? '#dv-action-box .av-action-button-box': '#dv-action-box'); + + if(actions.empty) + return; + + actions.forEach(element => { + let minion = furnish('a.av-button.av-button--default', {}, 'Web to Plex'); + + addMinions(minion); + element.appendChild(minion); + }); + }, }; diff --git a/opa/background.js b/opa/background.js index 1bab0b7..2871245 100644 --- a/opa/background.js +++ b/opa/background.js @@ -67,7 +67,7 @@ function ChangeStatus({ ITEM_ID, ITEM_TITLE, ITEM_TYPE, ID_PROVIDER, ITEM_YEAR, // File friendly title SEARCH_TITLE = ITEM_TITLE.replace(/[\-\s]+/g, '-').replace(/\s*&\s*/g, ' and ').replace(/[^\w\-\'\*\#]+/g, ''), // Search friendly title - SEARCH_PROVIDER = /\b(tv|show|series)\b/i.test(ITEM_TYPE)? 'GG': /^im/i.test(ID_PROVIDER)? 'VO': /^tm/i.test(ID_PROVIDER)? 'GX': 'GG'; + SEARCH_PROVIDER = (ITEM_TYPE == 'show')? 'GG': /^im/i.test(ID_PROVIDER)? 'VO': /^tm/i.test(ID_PROVIDER)? 'GX': 'GG'; ITEM_ID = (ITEM_ID && !/^tt$/i.test(ITEM_ID)? ITEM_ID: '') + ''; ITEM_ID = ITEM_ID.replace(/^.*\b(tt\d+)\b.*$/, '$1').replace(/^.*\bid=(\d+)\b.*$/, '$1').replace(/^.*(?:movie|tv|(?:tv-?)?(?:shows?|series|episodes?))\/(\d+).*$/, '$1'); @@ -898,6 +898,11 @@ chrome.runtime.onMessage.addListener((request = {}, sender, callback) => { FILE_PATH = (item.path || ''), ITEM_ID = ((i, I)=>{for(let p in i)if(RegExp('^'+I,'i').test(p))return i[p]})(item, ID_PROVIDER); + if(/movie|film|cinema|theat[re]{2}/i.test(ITEM_TYPE)) + ITEM_TYPE = 'movie'; + else if(/tv|show|series|episode/i.test(ITEM_TYPE)) + ITEM_TYPE = 'show'; + if(request.type) { try { switch(request.type) { diff --git a/opa/blank.orange.png b/opa/blank.orange.png new file mode 100644 index 0000000..968654c Binary files /dev/null and b/opa/blank.orange.png differ diff --git a/opa/blank.png b/opa/blank.png new file mode 100644 index 0000000..a88188b Binary files /dev/null and b/opa/blank.png differ diff --git a/opa/colors.css b/opa/colors.css new file mode 100644 index 0000000..5c66061 --- /dev/null +++ b/opa/colors.css @@ -0,0 +1,572 @@ +:root { + --light-black: #222; + --black: #282828; + --dark-black: #000; + + --light-blue: #197bcc; + --blue: #265af4; + --dark-blue: #2a2aff; + + --light-grey: #999; + --grey: #666; + --dark-grey: #3f4245; + + --green: #80f080; + + --light-orange: #e59029; + --orange: #cc7b19; + --dark-orange: #f45a26; + + --light-red: #f44; + --red: #ff2a2a; + --dark-red: #d11; + + --light-white: #fff; + --white: #eee; + + --disabled: #909090ee; + --half-black: #0008; + --half-green: #80f08088; + --half-grey: #8888; + --half-white: #fff8; + --quarter-black: #0004; + --quarter-grey: #8884; + --quarter-white: #fff4; + --transparent: #0000; +} + +/* Text Color */ +[light-black]:not([gradient]) { + color: var(--light-black) +} + +[black]:not([gradient]) { + color: var(--black) +} + +[dark-black]:not([gradient]) { + color: var(--dark-black) +} + +[light-blue]:not([gradient]) { + color: var(--light-blue) +} + +[blue]:not([gradient]) { + color: var(--blue) +} + +[dark-blue]:not([gradient]) { + color: var(--dark-blue) +} + +[light-grey]:not([gradient]) { + color: var(--light-grey) +} + +[grey]:not([gradient]) { + color: var(--grey) +} + +[dark-grey]:not([gradient]) { + color: var(--dark-grey) +} + +[green]:not([gradient]) { + color: var(--green) +} + +[light-orange]:not([gradient]) { + color: var(--light-orange) +} + +[orange]:not([gradient]) { + color: var(--orange) +} + +[dark-orange]:not([gradient]) { + color: var(--dark-orange) +} + +[light-red]:not([gradient]) { + color: var(--light-red) +} + +[red]:not([gradient]) { + color: var(--red) +} + +[dark-red]:not([gradient]) { + color: var(--dark-red) +} + +[light-white]:not([gradient]) { + color: var(--light-white) +} + +[white]:not([gradient]) { + color: var(--white) +} + +[disabled]:not([gradient]) { + color: var(--disabled) +} + +[half-black]:not([gradient]) { + color: var(--half-black) +} + +[half-green]:not([gradient]) { + color: var(--half-green) +} + +[half-grey]:not([gradient]) { + color: var(--half-grey) +} + +[half-white]:not([gradient]) { + color: var(--half-white) +} + +[quarter-black]:not([gradient]) { + color: var(--quarter-black) +} + +[quarter-grey]:not([gradient]) { + color: var(--quarter-grey) +} + +[quarter-white]:not([gradient]) { + color: var(--quarter-white) +} + +[transparent]:not([gradient]) { + color: var(--transparent) +} + +/* Text Color (Hover) */ +[light-black\$]:hover { + color: var(--light-black) +} + +[black\$]:hover { + color: var(--black) +} + +[dark-black\$]:hover { + color: var(--dark-black) +} + +[light-blue\$]:hover { + color: var(--light-blue) +} + +[blue\$]:hover { + color: var(--blue) +} + +[dark-blue\$]:hover { + color: var(--dark-blue) +} + +[light-grey\$]:hover { + color: var(--light-grey) +} + +[grey\$]:hover { + color: var(--grey) +} + +[dark-grey\$]:hover { + color: var(--dark-grey) +} + +[green\$]:hover { + color: var(--green) +} + +[light-orange\$]:hover { + color: var(--light-orange) +} + +[orange\$]:hover { + color: var(--orange) +} + +[dark-orange\$]:hover { + color: var(--dark-orange) +} + +[light-red\$]:hover { + color: var(--light-red) +} + +[red\$]:hover { + color: var(--red) +} + +[dark-red\$]:hover { + color: var(--dark-red) +} + +[light-white\$]:hover { + color: var(--light-white) +} + +[white\$]:hover { + color: var(--white) +} + +[disabled\$]:hover { + color: var(--disabled) +} + +[half-black\$]:hover { + color: var(--half-black) +} + +[half-green\$]:hover { + color: var(--half-green) +} + +[half-grey\$]:hover { + color: var(--half-grey) +} + +[half-white\$]:hover { + color: var(--half-white) +} + +[quarter-black\$]:hover { + color: var(--quarter-black) +} + +[quarter-grey\$]:hover { + color: var(--quarter-grey) +} + +[quarter-white\$]:hover { + color: var(--quarter-white) +} + +[transparent\$]:hover { + color: var(--transparent) +} + +/* Background Color */ +[light-black-bg], [light-black][gradient] { + background: var(--light-black) +} + +[black-bg], [black][gradient] { + background: var(--black) +} + +[dark-black-bg], [dark-black][gradient] { + background: var(--dark-black) +} + +[light-blue-bg], [light-blue][gradient] { + background: var(--light-blue) +} + +[blue-bg], [blue][gradient] { + background: var(--blue) +} + +[dark-blue-bg], [dark-blue][gradient] { + background: var(--dark-blue) +} + +[light-grey-bg], [light-grey][gradient] { + background: var(--light-grey) +} + +[grey-bg], [grey][gradient] { + background: var(--grey) +} + +[dark-grey-bg], [dark-grey][gradient] { + background: var(--dark-grey) +} + +[green], [green][gradient] { + background: var(--green) +} + +[light-orange-bg], [light-orange][gradient] { + background: var(--light-orange) +} + +[orange-bg], [orange][gradient] { + background: var(--orange) +} + +[dark-orange-bg], [dark-orange][gradient] { + background: var(--dark-orange) +} + +[light-red-bg], [light-red][gradient] { + background: var(--light-red) +} + +[red-bg], [red][gradient] { + background: var(--red) +} + +[dark-red-bg], [dark-red][gradient] { + background: var(--dark-red) +} + +[light-white-bg], [light-white][gradient] { + background: var(--light-white) +} + +[white-bg], [white][gradient] { + background: var(--white) +} + +[disabled-bg] { + background: var(--disabled) +} + +[half-black-bg] { + background: var(--half-black) +} + +[half-green-bg] { + background: var(--half-green) +} + +[half-grey-bg] { + background: var(--half-grey) +} + +[half-white-bg] { + background: var(--half-white) +} + +[quarter-black-bg] { + background: var(--quarter-black) +} + +[quarter-grey-bg] { + background: var(--quarter-grey) +} + +[quarter-white-bg] { + background: var(--quarter-white) +} + +[transparent-bg] { + background: var(--transparent) +} + +/* Background Color (Hover) */ +[light-black-bg\$]:hover { + background: var(--light-black) +} + +[black-bg\$]:hover { + background: var(--black) +} + +[dark-black-bg\$]:hover { + background: var(--dark-black) +} + +[light-blue-bg\$]:hover { + background: var(--light-blue) +} + +[blue-bg\$]:hover { + background: var(--blue) +} + +[dark-blue-bg\$]:hover { + background: var(--dark-blue) +} + +[light-grey-bg\$]:hover { + background: var(--light-grey) +} + +[grey-bg\$]:hover { + background: var(--grey) +} + +[dark-grey-bg\$]:hover { + background: var(--dark-grey) +} + +[green-bg\$]:hover { + background: var(--green) +} + +[light-orange-bg\$]:hover { + background: var(--light-orange) +} + +[orange-bg\$]:hover { + background: var(--orange) +} + +[dark-orange-bg\$]:hover { + background: var(--dark-orange) +} + +[light-red-bg\$]:hover { + background: var(--light-red) +} + +[red-bg\$]:hover { + background: var(--red) +} + +[dark-red-bg\$]:hover { + background: var(--dark-red) +} + +[light-white-bg\$]:hover { + background: var(--light-white) +} + +[white-bg\$]:hover { + background: var(--white) +} + +[disabled-bg\$]:hover { + background: var(--disabled) +} + +[half-black-bg\$]:hover { + background: var(--half-black) +} + +[half-green-bg\$]:hover { + background: var(--half-green) +} + +[half-grey-bg\$]:hover { + background: var(--half-grey) +} + +[half-white-bg\$]:hover { + background: var(--half-white) +} + +[quarter-black-bg\$]:hover { + background: var(--quarter-black) +} + +[quarter-grey-bg\$]:hover { + background: var(--quarter-grey) +} + +[quarter-white-bg\$]:hover { + background: var(--quarter-white) +} + +[transparent-bg\$]:hover { + background: var(--transparent) +} + +/* Gradient Text (Darken) */ +[light-black][gradient="darken"i] { + background: linear-gradient(var(--light-black), var(--black)) +} + +[black][gradient="darken"i] { + background: linear-gradient(var(--black), var(--dark-black)) +} + +[light-blue][gradient="darken"i] { + background: linear-gradient(var(--light-blue), var(--blue)) +} + +[blue][gradient="darken"i] { + background: linear-gradient(var(--blue), var(--dark-blue)) +} + +[light-grey][gradient="darken"i] { + background: linear-gradient(var(--light-grey), var(--grey)) +} + +[grey][gradient="darken"i] { + background: linear-gradient(var(--grey), var(--dark-grey)) +} + +[light-orange][gradient="darken"i] { + background: linear-gradient(var(--light-orange), var(--orange)) +} + +[orange][gradient="darken"i] { + background: linear-gradient(var(--orange), var(--dark-orange)) +} + +[light-red][gradient="darken"i] { + background: linear-gradient(var(--light-red), var(--red)) +} + +[red][gradient="darken"i] { + background: linear-gradient(var(--red), var(--dark-red)) +} + +[light-white][gradient="darken"i] { + background: linear-gradient(var(--light-white), var(--white)) +} + +/* Gradient Text (Lighten) */ +[black][gradient="lighten"i] { + background: linear-gradient(var(--black), var(--light-black)) +} + +[dark-black][gradient="lighten"i] { + background: linear-gradient(var(--dark-black), var(--black)) +} + +[blue][gradient="lighten"i] { + background: linear-gradient(var(--blue), var(--light-blue)) +} + +[dark-blue][gradient="lighten"i] { + background: linear-gradient(var(--dark-blue), var(--blue)) +} + +[grey][gradient="lighten"i] { + background: linear-gradient(var(--grey), var(--light-grey)) +} + +[dark-grey][gradient="lighten"i] { + background: linear-gradient(var(--dark-grey), var(--grey)) +} + +[orange][gradient="lighten"i] { + background: linear-gradient(var(--orange), var(--light-orange)) +} + +[dark-orange][gradient="lighten"i] { + background: linear-gradient(var(--dark-orange), var(--orange)) +} + +[red][gradient="lighten"i] { + background: linear-gradient(var(--red), var(--light-red)) +} + +[dark-red][gradient="lighten"i] { + background: linear-gradient(var(--dark-red), var(--red)) +} + +[white][gradient="lighten"i] { + background: linear-gradient(var(--white), var(--light-white)) +} + +/* All Gradient Properties */ +[gradient] { + background-clip: text; + -moz-background-clip: text; + -webkit-background-clip: text; + + color: #0000; + -webkit-text-fill-color: #0000; +} diff --git a/opa/common.css b/opa/common.css index 5657b4f..f3f670f 100644 --- a/opa/common.css +++ b/opa/common.css @@ -1,98 +1,123 @@ /** Common CSS - * Web to Plex - */ - /* Basic/Global Styling */ - [class*="web-to-plex"]::-webkit-scrollbar, [class*="web-to-plex"]::-moz-scrollbar { - width: 10px !important; - } - - [class*="web-to-plex"]::-webkit-scrollbar-thumb, [class*="web-to-plex"]::-moz-scrollbar-thumb { - min-height: 50px !important; - background: rgba(255, 255, 255, 0.15) !important; - border: 2px solid rgba(0, 0, 0, 0) !important; - border-radius: 8px !important; - background-clip: padding-box !important; - } - - [class*="web-to-plex"]::-webkit-scrollbar-track, [class*="web-to-plex"]::-moz-scrollbar-track { - background: #0000 !important; - } - - [class*="web-to-plex"]::-webkit-input-placeholder, [class*="web-to-plex"]::-moz-placeholder, [class*="web-to-plex"]:-moz-placeholder { - color: #999 !important; - } - - [class*="web-to-plex"] input[type="text"], [class*="web-to-plex"] input[type="password"], [class*="web-to-plex"] select { - width: 30vw !important; - line-height: 1.5em !important; - transition: background 0.2s !important; - display: block !important; - height: 38px !important; - padding: 6px 12px !important; - font-size: 16px !important; - color: #eee !important; - vertical-align: middle; - background: rgba(255, 255, 255, 0.25) !important; - border: 3px solid rgba(0, 0, 0, 0) !important; - border-radius: 3px !important; - font-family: inherit !important; - margin: 0 !important; - } - - [class*="web-to-plex"] select { - margin-left: 10px !important; - font-size: 16px !important; - line-height: inherit !important; - text-transform: none !important; - } - - [class*="web-to-plex"] option { - background: #3f4245 !important; - } - - [class*="web-to-plex"] button { - padding: 10px 18px !important; - font-size: 16px !important; - line-height: 1.33 !important; - border-radius: 3px !important; - font-family: inherit !important; - text-transform: uppercase !important; - border: 0 !important; - box-shadow: none !important; - position: relative !important; - overflow: hidden !important; - color: #fff; - background: #cc7b19; - margin-bottom: 0 !important; - font-weight: 400 !important; - vertical-align: middle; - cursor: pointer !important; - white-space: nowrap; - user-select: none; - transition: all 0.1s !important; - } +* Web to Plex +*/ + +@charset "utf-8"; +@font-face { + font-family: "Plex"; + src: local(Plex), + url(//webtoplex.github.io/font/Plex.woff2) format('woff2'), + url(//webtoplex.github.io/font/Plex.woff) format('woff'); + font-weight: 400; + font-style: normal; +} + +@font-face { + font-family: "Plex-bold"; + src: local(Plex-bold), + url(//webtoplex.github.io/font/Plex.bold.woff2) format('woff2'), + url(//webtoplex.github.io/font/Plex.bold.woff) format('woff'); + font-weight: 400; + font-style: normal; +} + +/* Basic/Global Styling */ +*::-webkit-scrollbar { + width: 10px; +} + +*::-webkit-scrollbar-thumb { + min-height: 50px; + background: rgba(255, 255, 255, 0.15); + border: 2px solid rgba(0, 0, 0, 0); + border-radius: 8px; + background-clip: padding-box; +} - [class*="web-to-plex"] button:hover { - background: #e59029; - } +*::-webkit-scrollbar-track { + /* background: url(noise.png) fixed, #3f4245 !important; */ +} + +*::placeholder { + color: #999 !important; +} - [class*="web-to-plex"] input::placeholder, [class*="web-to-plex"] input:placeholder { +*::-webkit-input-placeholder { color: #999 !important; - } +} + +/* Web to Plex */ +[class*="web-to-plex"] input[type="text"], [class*="web-to-plex"] input[type="password"], [class*="web-to-plex"] select { + width: 30vw !important; + line-height: 1.5em !important; + transition: background 0.2s !important; + display: block !important; + height: 38px !important; + padding: 6px 12px !important; + font-size: 16px !important; + color: var(--white) !important; + vertical-align: middle; + background: rgba(255, 255, 255, 0.25) !important; + border: 3px solid rgba(0, 0, 0, 0) !important; + border-radius: 3px !important; + font-family: inherit !important; + margin: 0 !important; +} + +[class*="web-to-plex"] select { + margin-left: 10px !important; + font-size: 16px !important; + line-height: inherit !important; + text-transform: none !important; +} + +[class*="web-to-plex"] option { + background: var(--light-grey) !important; +} + +[class*="web-to-plex"] button { + padding: 10px 18px !important; + font-size: 16px !important; + line-height: 1.33 !important; + border-radius: 3px !important; + font-family: inherit !important; + text-transform: uppercase !important; + border: 0 !important; + box-shadow: none !important; + position: relative !important; + overflow: hidden !important; + color: var(--light-white); + background: var(--orange); + margin-bottom: 0 !important; + font-weight: 400 !important; + vertical-align: middle; + cursor: pointer !important; + white-space: nowrap; + user-select: none; + transition: all 0.1s !important; +} - [class*="web-to-plex"][disabled], [class*="web-to-plex"] [disabled] { - cursor: not-allowed !important; - color: #909090EE !important; - } +[class*="web-to-plex"] button:hover { + background: var(--light-orange); +} + +[class*="web-to-plex"] input::placeholder, [class*="web-to-plex"] input:placeholder { + color: var(--dark-grey) !important; +} + +[class*="web-to-plex"][disabled], [class*="web-to-plex"] [disabled] { + cursor: not-allowed !important; + color: var(--disabled) !important; +} /* Web to Plex notifications */ .web-to-plex-notification { - background: #F45A26 !important; + background: var(--dark-orange) !important; border-radius: 4px !important; - color: #FFF !important; + color: var(--light-white) !important; cursor: pointer !important; display: block !important; - font-family: arial, verdana, sans-serif !important; + font-family: "Plex", "Open Sans Regular", "Helvetica Neue", Helvetica, Arial, sans-serif, system !important; font-size: 20px !important; text-align: center !important; @@ -108,31 +133,31 @@ /* Web to Plex general information notifications */ .web-to-plex-notification.info { - background: #666 !important; + background: var(--grey) !important; } /* Web to Plex update notifications */ .web-to-plex-notification.update { - background: #2A2AFF !important; + background: var(--dark-blue) !important; } /* Web to Plex success notifications */ .web-to-plex-notification.success { - background: #03BDF9 !important; + background: var(--light-blue) !important; } /* Web to Plex error/warning notifications */ .web-to-plex-notification.warning, .web-to-plex-notification.error { - background: #FF2A2A !important; + background: var(--red) !important; } /* Web to Plex prompts */ .web-to-plex-prompt { - background: #0008 !important; + background: var(--half-black) !important; box-sizing: border-box !important; - color: #eee !important; + color: var(--white) !important; display: block !important; - font-family: Open Sans Regular, Helvetica Neue, Helvetica, Arial, sans-serif !important; + font-family: "Plex", "Open Sans Regular", "Helvetica Neue", Helvetica, Arial, sans-serif, system !important; font-size: 14px !important; line-height: 24px !important; overflow: auto !important; @@ -149,10 +174,11 @@ } .web-to-plex-prompt-body { - background: #282828; + background: var(--black); border: 0 !important; border-radius: 3px !important; box-shadow: 0 5px 15px #0008 !important; + box-sizing: content-box !important; display: block !important; left: 20% !important; @@ -166,10 +192,10 @@ } .web-to-plex-prompt-header, .web-to-plex-prompt-footer { - background: #32323240 !important; + background: var(--quarter-grey) !important; border: 1px solid #0000 !important; box-sizing: border-box !important; - color: #eee !important; + color: var(--white) !important; font: inherit !important; font-size: 2em !important; line-height: initial !important; @@ -182,11 +208,11 @@ height: 65px !important; width: 100% !important; - -webkit-tap-highlight-color: #0000; + -webkit-tap-highlight-color: var(--transparent); } .web-to-plex-prompt-header { - border-bottom-color: #222 !important; + border-bottom-color: var(--light-black) !important; border-bottom-width: 1px !important; border-top-left-radius: 3px !important; border-top-right-radius: 3px !important; @@ -203,7 +229,7 @@ overflow-x: hidden !important; overflow-y: auto !important; - padding: 12px !important; + padding: 0 12px 5px !important; position: relative !important; top: 65px !important; @@ -211,10 +237,10 @@ } .web-to-plex-prompt-option { - background: #32323240 !important; + background: var(--quarter-grey) !important; border: 1px solid #202020 !important; border-radius: 3px !important; - color: #999 !important; + color: var(--light-grey) !important; display: block !important; text-align: left !important; @@ -225,22 +251,21 @@ } .web-to-plex-prompt-option.mutable { + cursor: pointer; max-width: 60% !important; } .web-to-plex-prompt-option.mutable > h2 { - background: #0000 !important; - color: inherit !important; - font-family: inherit !important; - font-size: initial !important; - text-align: inherit !important; + background: var(--transparent) !important; + color: inherit !important; + font-family: inherit !important; + font-size: initial !important; + text-align: inherit !important; - margin: inherit !important; + margin: inherit !important; } -.web-to-plex-prompt-option.mutable > .remove, .web-to-plex-prompt-option.mutable > .choose { - background: #ffffff40 !important; - border-radius: 3px !important; +.web-to-plex-prompt-option.mutable [glyph] { transition: all 0.1s !important; height: 30px !important; @@ -248,67 +273,67 @@ float: right !important; margin-right: -2% !important; - margin-top: -8% !important; + margin-top: -5% !important; padding: 0 !important; } -.web-to-plex-prompt-option.mutable > .remove:hover, .web-to-plex-prompt-option.mutable > .choose:hover { - background: #ffffff4d !important; +.web-to-plex-prompt-option.mutable.choose [glyph] { + color: var(--grey); } -.web-to-plex-prompt-option.mutable > .remove::after { - content: '\00d7' !important; +.web-to-plex-prompt-option.mutable.chosen:first-child { + border-color: var(--green) !important; } -.web-to-plex-prompt-option.mutable > .choose::after { - content: '\2218' !important; -} - -.web-to-plex-prompt-option.mutable:first-child:last-child > .choose, .web-to-plex-prompt-option.mutable.chosen > .choose { - background: #80F08088 !important; +.web-to-plex-prompt-option.mutable.chosen:first-child [glyph] { + color: var(--green); } .web-to-plex-prompt-option.mutable > .quality { - width: 50% !important; + width: 50% !important; } .web-to-plex-prompt-option.mutable > .location { - width: 90% !important; + width: 90% !important; } .web-to-plex-prompt-option.mutable > .location:last-child:not(:first-child) { - margin-top: 5px !important; + margin-top: 5px !important; } .web-to-plex-permission:after { - background: #0000; - border-radius: 3px; - border: 0; - color: #cc7b19; - content: "\29eb"; - display: inline-block; - font-size: 150%; - padding: 0; - text-align: center; + background: var(--transparent); + border-radius: 3px; + border: 0; + color: var(--orange); + content: "\29eb"; + display: inline-block; + font-size: 150%; + padding: 0; + text-align: center; - margin: 0; - position: absolute; - right: 3%; + margin: 0; + position: absolute; + right: 3%; - height: 2em; - width: 2em; + height: 2em; + width: 2em; } .web-to-plex-prompt-footer { text-align: right !important; border-bottom-left-radius: 3px !important; border-bottom-right-radius: 3px !important; - border-top-color: #222 !important; + border-top-color: var(--light-black) !important; border-top-width: 1px !important; bottom: 0 !important; } +.web-to-plex-prompt-footer > * { + vertical-align: text-bottom !important; +} + .web-to-plex-prompt-input { float: left !important; position: relative !important; @@ -321,338 +346,358 @@ } .web-to-plex-prompt-accept { - background: #cc7b19 !important; + background: var(--orange) !important; margin-left: 5px !important; } .web-to-plex-prompt-accept:hover { - background: #e59029 !important; + background: var(--light-orange) !important; } .web-to-plex-prompt-decline { - background: #ffffff40 !important; + background: var(--quarter-white) !important; } .web-to-plex-prompt-decline:hover { - background: #ffffff4d !important; + background: var(--half-white) !important; } /* Web to Plex buttons */ .web-to-plex-button [module] { - position: relative !important; + position: relative !important; } .web-to-plex-button * { - border: none !important; - text-transform: none !important; + border: none !important; + text-transform: none !important; } .web-to-plex-button { - background-color: #3F4245 !important; - border: none !important; - color: #FFF !important; - font-family: Open Sans Semibold, Helvetica Neue, Helvetica, Arial, sans-serif !important; - font-size: 1em !important; - font-weight: 100 !important; - text-align: center !important; - - bottom: 5px !important; - left: 5px !important; - padding: 10px !important; - position: fixed !important; - right: unset !important; - z-index: 999999 !important; - - min-height: 0 !important; - min-width: 0 !important; - height: 72px !important; - width: 180px !important; - - transition: all 0.3s ease !important; + background-color: var(--light-grey) !important; + border: none !important; + color: var(--light-white) !important; + display: block !important; + font-family: "Plex", "Open Sans Regular", "Helvetica Neue", Helvetica, Arial, sans-serif, system !important; + font-size: 1em !important; + font-weight: 100 !important; + text-align: center !important; + + bottom: 5px !important; + left: 5px !important; + padding: 10px !important; + position: fixed !important; + right: unset !important; + z-index: 999999 !important; + + min-height: 0 !important; + min-width: 0 !important; + height: 72px !important; + width: 180px !important; + + transition: all 0.3s ease !important; } .web-to-plex-button.hide { - display: initial !important; + display: initial !important; } .web-to-plex-button.hide:not(:hover), .web-to-plex-button.sleeper { - opacity: 0.1; + opacity: 0.1; } *:not(#plexit-bookmarklet-frame) ~ .web-to-plex-button { - margin-left: 0px !important; + margin-left: 0px; } #plexit-bookmarklet-frame ~ .web-to-plex-button { - margin-left: 280px !important; + margin-left: 280px !important; } *:not(#plexit-bookmarklet-frame) + .web-to-plex-button #plexit, #plexit-bookmarklet-frame + .web-to-plex-button #wtp-plexit { - display: none !important; + display: none !important; } .floating.web-to-plex-button { - border-radius: 50px !important; - box-shadow: 0 4px 10px 0 rgba(0, 0, 0, 0.3) !important; + border-radius: 50px !important; + box-shadow: 0 4px 10px 0 rgba(0, 0, 0, 0.3) !important; - height: 75px !important; - width: 75px !important; + height: 75px !important; + width: 75px !important; } .floating.web-to-plex-button::after { - content: ' ' !important; + content: ' ' !important; - background: #666 !important; - border: 1px solid #888 !important; - border-radius: 16px !important; + background: var(--grey) !important; + border: 1px solid #888 !important; + border-radius: 16px !important; - right: 0 !important; - top: 0 !important; - position: absolute !important; + right: 0 !important; + top: 0 !important; + position: absolute !important; - height: 16px !important; - width: 16px !important; + height: 16px !important; + width: 16px !important; - transition: background 0.4s linear !important; + transition: background 0.4s linear !important; } .floating.web-to-plex-button:not(.restarting):active, .floating.web-to-plex-button:not(.restarting):hover { - box-shadow: 1px 5px 20px 0 rgba(0, 0, 0, 0.6) !important; - cursor: pointer !important; + box-shadow: 1px 5px 20px 0 rgba(0, 0, 0, 0.6) !important; + cursor: pointer !important; } .floating.web-to-plex-button:focus { - outline: #0000 !important; + outline: #0000 !important; } .web-to-plex-button.wtp--download::after, .web-to-plex-button.wtp--download::before { - background: #265AF4 !important; + background: var(--blue) !important; } .web-to-plex-button.wtp--queued::after, .web-to-plex-button.wtp--queued::before { - background: #568AF4 !important; + background: var(--light-blue) !important; } .web-to-plex-button.wtp--found::after, .web-to-plex-button.wtp--found::before { - background: #F9BD03 !important; + background: var(--light-orange) !important; } .web-to-plex-button.wtp--error::after, .web-to-plex-button.wtp--error::before { - background: #FF2A2A !important; + background: var(--red) !important; } .web-to-plex-button::before { - content: ' ' !important; + content: ' ' !important; - background: #FFF6 !important; - border-radius: inherit !important; - display: none !important; + background: var(--half-white) !important; + border-radius: inherit !important; + display: none !important; - margin-top: -10px !important; - margin-left: -10px !important; + margin-top: -10px !important; + margin-left: -10px !important; - height: 75px !important; - width: 75px !important; + height: 75px !important; + width: 75px !important; - position: absolute !important; - z-index: 9999999 !important; + position: absolute !important; + z-index: 9999999 !important; } .web-to-plex-button.animate::before { - display: block !important; + display: block !important; - -webkit-transform: scale(0); - -moz-transform: scale(0); - -o-transform: scale(0); - transform: scale(0); + -webkit-transform: scale(0); + -moz-transform: scale(0); + -o-transform: scale(0); + transform: scale(0); - -webkit-animation: web-to-plex-ripple 0.5s linear; - -moz-animation: web-to-plex-ripple 0.5s linear; - -o-animation: web-to-plex-ripple 0.5s linear; - animation: web-to-plex-ripple 0.5s linear; + -webkit-animation: web-to-plex-ripple 0.5s linear; + -moz-animation: web-to-plex-ripple 0.5s linear; + -o-animation: web-to-plex-ripple 0.5s linear; + animation: web-to-plex-ripple 0.5s linear; } @-webkit-keyframes web-to-plex-ripple { - 100% { - opacity: 0; - -webkit-transform: scale(2.5); - } + 100% { + opacity: 0; + -webkit-transform: scale(2.5); + } } @-moz-keyframes web-to-plex-ripple { - 100% { - opacity: 0; - -moz-transform: scale(2.5); - } + 100% { + opacity: 0; + -moz-transform: scale(2.5); + } } @-o-keyframes web-to-plex-ripple { - 100% { - opacity: 0; - -o-transform: scale(2.5); - } + 100% { + opacity: 0; + -o-transform: scale(2.5); + } } @keyframes web-to-plex-ripple { - 100% { - opacity: 0; - transform: scale(2.5); - } + 100% { + opacity: 0; + transform: scale(2.5); + } } .web-to-plex-button.open, #plexit-bookmarklet-frame + .web-to-plex-button { - opacity: 1; + opacity: 1; - width: 350px !important; + width: 350px !important; } + .web-to-plex-button .list-name { - float: left !important; + float: left !important; } .web-to-plex-button ul { - margin: 0 !important; - padding-left: 0 !important; + margin: 0 !important; + padding-left: 0 !important; } .web-to-plex-button li { - display: inline-block !important; - list-style: none !important; + display: inline-block !important; + list-style: none !important; - margin: 0 !important; - padding: 5px !important; - vertical-align: bottom; + margin: 0 !important; + padding: 5px !important; + vertical-align: bottom; } .web-to-plex-button li > img { - display: inline !important; + display: inline !important; - margin-top: 0 !important; + margin-top: 0 !important; } .web-to-plex-button.hide.closed li:not(:first-child) { - display: none !important; + display: none !important; } *:not(#plexit-bookmarklet-frame) + .web-to-plex-button.closed .list-item { - float: left !important; - opacity: 0; - transition: opacity 0 !important; + float: left !important; + opacity: 0; + transition: opacity 0 !important; } .web-to-plex-button.open .list-item { - opacity: 1; - transition: opacity 2s !important; + opacity: 1; + transition: opacity 2s !important; } .web-to-plex-button.open li:hover [tooltip]::before, .web-to-plex-button.open [tooltip]:hover::before { - content: attr(tooltip) !important; + content: attr(tooltip) !important; - background: #3F424599 !important; - border-radius: 3px !important; - color: #fff !important; - font-family: arial, calibri, sans-serif, sans, monospace !important; - font-size: 15px !important; + background: var(--quarter-grey) !important; + border-radius: 3px !important; + color: var(--light-white) !important; + font-family: "Plex", "Open Sans Regular", "Helvetica Neue", Helvetica, Arial, sans-serif, system !important; + font-size: 15px !important; - bottom: 85px !important; - left: 35px !important; - padding: 3px 6px !important; - position: absolute !important; + bottom: 85px !important; + left: 35px !important; + padding: 3px 6px !important; + position: absolute !important; +} + +.web-to-plex-minion { + cursor: pointer; } /* bbodine @CodePen - https://codepen.io/bbodine1/pen/novBm */ [class*="web-to-plex"] .checkbox { - width: 80px; - height: 26px; - background: #000; - margin: 15px 0; - position: relative; - border-radius: 50px; - box-shadow: inset 0px 1px 1px rgba(0, 0, 0, 0.5), 0px 1px 0px rgba(255, 255, 255, 0.2); + width: 80px; + height: 26px; + background: var(--dark-black); + margin: 15px 0; + position: relative; + border-radius: 50px; + box-shadow: inset 0px 1px 1px rgba(0, 0, 0, 0.5), 0px 1px 0px rgba(255, 255, 255, 0.2); } [class*="web-to-plex"] span.checkbox { - display: inline-block; + display: inline-block; - margin: 0; - vertical-align: text-bottom; + margin: 0; + vertical-align: text-bottom; } [class*="web-to-plex"] .checkbox::after { - content: 'OFF'; - color: #666; - position: absolute; - right: 10px; - z-index: 0; - font: 12px/26px Arial, sans-serif; - font-weight: bold; - text-shadow: 1px 1px 0px rgba(255, 255, 255, 0.15); + content: 'OFF'; + color: var(--grey); + position: absolute; + right: 10px; + z-index: 0; + font: 12px/26px Arial, sans-serif; + font-weight: bold; + text-shadow: 1px 1px 0px rgba(255, 255, 255, 0.15); } [class*="web-to-plex"] .checkbox::before { - content: 'ON'; - color: #cc7b19; - position: absolute; - left: 10px; - z-index: 0; - font: 12px/26px Arial, sans-serif; - font-weight: bold; + content: 'ON'; + color: var(--orange); + position: absolute; + left: 10px; + z-index: 0; + font: 12px/26px Arial, sans-serif; + font-weight: bold; } [class*="web-to-plex"] .checkbox[prompt-yes]::before { - content: attr(prompt-yes); - text-transform: uppercase; + content: attr(prompt-yes); + text-transform: uppercase; } [class*="web-to-plex"] .checkbox[prompt-no]::after { - content: attr(prompt-no); - text-transform: uppercase; + content: attr(prompt-no); + text-transform: uppercase; } [class*="web-to-plex"] .checkbox[prompt-size="large"i]::before, .checkbox[prompt-size="large"i]::after { - font-size: 30px !important; + font-size: 30px !important; } [class*="web-to-plex"] .checkbox[prompt-size="medium"i]::before, .checkbox[prompt-size="medium"i]::after { - font-size: 21px !important; + font-size: 21px !important; } [class*="web-to-plex"] .checkbox[prompt-size="normal"i]::before, .checkbox[prompt-size="normal"i]::after { - font-size: 12px !important; + font-size: 12px !important; } [class*="web-to-plex"] .checkbox[prompt-size="small"i]::before, .checkbox[prompt-size="small"i]::after { - font-size: 6px !important; + font-size: 6px !important; } [class*="web-to-plex"] .checkbox[prompt="y/n"i]::before { - content: 'YES'; + content: 'YES'; } [class*="web-to-plex"] .checkbox[prompt="y/n"i]::after { - content: 'NO'; + content: 'NO'; } [class*="web-to-plex"] .checkbox label { - display: block; - width: 34px; - height: 20px; - cursor: pointer; - position: absolute; - top: 3px; - left: 3px; - z-index: 1; - background: #666; - border-radius: 50px; - transition: all 0.4s ease; - box-shadow: 0px 2px 5px 0px rgba(0, 0, 0, 0.3); + display: block; + width: 34px; + height: 20px; + cursor: pointer; + position: absolute; + top: 3px; + left: 3px; + z-index: 1; + background: var(--grey); + border-radius: 50px; + transition: all 0.4s ease; + box-shadow: 0px 2px 5px 0px rgba(0, 0, 0, 0.3); } [class*="web-to-plex"] .checkbox input[type=checkbox] { - visibility: hidden; + visibility: hidden; } [class*="web-to-plex"] .checkbox input[type=checkbox]:checked + label { - left: 43px; - background: #cc7b19; + left: 43px; + background: var(--orange); } [class*="web-to-plex"] .checkbox[disabled] { - opacity: 0.25 !important; + opacity: 0.25 !important; +} + +/* Minor Bugs */ +[glyph], [class*="glyphicon"] { + width: 1.1em; +} + +[glyph][gradient], [class*="glyphicon"][gradient] { + background-clip: text !important; + -moz-background-clip: text !important; + -webkit-background-clip: text !important; + + color: #0000 !important; + -webkit-text-fill-color: #0000 !important; } diff --git a/opa/couchpotato.css b/opa/couchpotato.css new file mode 100644 index 0000000..6e2a6b1 --- /dev/null +++ b/opa/couchpotato.css @@ -0,0 +1,23 @@ +.web-to-plex-minion.wtp--download { + color: var(--blue)!important; +} + +.web-to-plex-minion.wtp--download:hover { + color: var(--light-blue)!important; +} + +.web-to-plex-minion.wtp--found { + color: var(--orange)!important; +} + +.web-to-plex-minion.wtp--found:hover { + color: var(--light-orange)!important; +} + +#tt--0-0 { + color: var(--grey)!important; +} + +#tt--0-0:hover { + color: var(--light-grey)!important; +} diff --git a/opa/couchpotato.js b/opa/couchpotato.js index f8f6ddc..f6dee2b 100644 --- a/opa/couchpotato.js +++ b/opa/couchpotato.js @@ -37,4 +37,22 @@ let script = { .replace(/^.*imdb\.com\/title\//, '') .replace(/\/(?:maindetails\/?)?$/, ''); }, + + "minions": () => { + let actions = $('[href*="imdb.com/title/tt"] < *'); + + if(actions.empty) + return; + + actions.forEach(element => { + let minion; + let parent = furnish('span', {}, + furnish('img', { src: IMAGES.icon_16 }), + minion = furnish('a.web-to-plex-minion', {}, 'Web to Plex') + ); + + addMinions(minion); + element.appendChild(parent); + }); + }, }; diff --git a/opa/fandango.css b/opa/fandango.css new file mode 100644 index 0000000..5f5284d --- /dev/null +++ b/opa/fandango.css @@ -0,0 +1,27 @@ +.web-to-plex-minion { + color: #727272; +} + +.web-to-plex-minion.wtp--download { + color: #f45a26!important; +} + +.web-to-plex-minion.wtp--found { + color: #e5a00d!important; +} + +.web-to-plex-wrapper:hover, .web-to-plex-wrapper:hover > *, .web-to-plex-minion.wtp--download:hover { + color: #f67e56!important; +} + +.web-to-plex-minion.wtp--found:hover { + color: #f8c022!important; +} + +#tt--0-0 { + color: #666!important; +} + +#tt--0-0:hover { + color: #888!important; +} diff --git a/opa/fandango.js b/opa/fandango.js index 04a1a4e..de216c8 100644 --- a/opa/fandango.js +++ b/opa/fandango.js @@ -17,4 +17,21 @@ let script = { return { type, title, year, image }; }, + + "minions": () => { + let actions = $('.subnav ul'); + + if(actions.empty) + return; + + actions.forEach(element => { + let minion; + let parent = furnish('li.web-to-plex-wrapper.subnav__link-item', {}, + minion = furnish('a.web-to-plex-minion.subnav__link', {}, 'Web to Plex') + ); + + addMinions(minion); + element.appendChild(parent); + }); + }, }; diff --git a/opa/glyphs.css b/opa/glyphs.css new file mode 100644 index 0000000..31d5a74 --- /dev/null +++ b/opa/glyphs.css @@ -0,0 +1,2463 @@ +/** Available Icons: + * adjust + * airplane + * alarm + * albums + * amazon + * anchor + * android + * apple + * asterisk + * ax + * badoo + * ban + * bank + * barcode + * baseball + * basketball + * bathrobe + * beer + * behance + * bell + * bicycle + * bin + * binoculars + * blacksmith + * blog + * blogger + * bluetooth + * boat + * bold + * bomb + * book + * bookmark + * bowling + * briefcase + * brush + * bug + * building + * bullets + * bullhorn + * buoy + * bus + * cake + * calculator + * calendar + * camera + * candle + * car + * cardio + * cargo + * cars + * celebration + * certificate + * charts + * chat + * check + * cleaning + * clock + * cloud + * cogwheel + * cogwheels + * coins + * collapse + * comments + * compass + * compressed + * conversation + * crop + * crown + * cup + * cutlery + * dashboard + * delete + * deviantart + * direction + * dislikes + * display + * divide + * dog + * download + * dress + * dribbble + * drink + * dropbox + * dumbbell + * earphone + * edit + * eject + * electricity + * embed + * envelope + * euro + * evernote + * exit + * expand + * eyedropper + * fabric + * facebook + * factory + * fax + * female + * file + * film + * filter + * fins + * fire + * fishes + * flag + * flash + * flickr + * flower + * font + * forrst + * forward + * foursquare + * fullscreen + * gamepad + * gbp + * gift + * girl + * github + * glass + * global + * globe + * golf + * goodreads + * google_plus + * grater + * group + * hdd + * header + * headphones + * headset + * heart + * heat + * history + * hockey + * home + * hospital + * imac + * inbox + * instagram + * instapaper + * ios + * ipad + * iphone + * ipod + * italic + * jolicloud + * justify + * kettle + * keynote + * keys + * kiosk + * last_fm + * leaf + * leather + * lightbulb + * link + * linked_in + * list + * lock + * luggage + * macbook + * magic + * magnet + * male + * microphone + * minus + * money + * moon + * more + * move + * music + * mute + * myspace + * nails + * nameplate + * note + * notes + * ok + * package + * pants + * paperclip + * parents + * pause + * pen + * pencil + * piano + * picasa + * picture + * pin + * pinboard + * pinterest + * pipe + * pizza + * play + * playlist + * playstation + * plus + * podium + * pool + * posterous_spaces + * pot + * power + * print + * projector + * pushpin + * qrcode + * quora + * rabbit + * radar + * random + * read_it_later + * readability + * record + * redo + * refresh + * remove + * repeat + * restart + * retweet + * rewind + * riflescope + * ring + * road + * roundabout + * router + * rss + * rugby + * ruller + * sampler + * scissors + * screenshot + * search + * send + * server + * settings + * share + * shield + * shirt + * shop + * signal + * skateboard + * skitch + * skull + * skype + * smoking + * snowflake + * sort + * sorting + * spade + * spotify + * spray + * star + * stats + * stop + * stopwatch + * stroller + * stumbleupon + * subtitles + * suitcase + * sun + * sweater + * table + * tablet + * tag + * tags + * tie + * tint + * tower + * train + * transfer + * translate + * truck + * tumblr + * turtle + * twitter + * umbrella + * unchecked + * underwear + * undo + * unlock + * unshare + * upload + * usd + * user + * vases + * vcard + * vimeo + * vine + * wallet + * webcam + * wifi + * windows + * woman + * wordpress + * wrench + * xbox + * xing + * yahoo + * yelp + * youtube + * zootool + */ + +@charset "utf-8"; +@font-face { + font-family: Glyphicons Regular; + src: local(Glyphicons), + url(Glyphicons.woff) format("woff"), + url(//webtoplex.github.io/font/Glyphicons.woff) format("woff"); + font-weight: 400; + font-style: normal +} + +@font-face { + font-family: Glyphicons Social Regular; + src: local(Glyphicons Social), + url(Glyphicons Social.woff) format("woff"), + url(//webtoplex.github.io/font/Glyphicons Social.woff) format("woff"); + font-weight: 400; + font-style: normal +} + +[class*="glyphicon"i], [glyph] { + position: relative; + top: 1px; + display: inline-block; + font-family: Glyphicons Regular !important; + font-style: normal; + font-weight: 400; + line-height: 1; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale + width: 1em; +} + +.glyphicon-social, [glyph~="social"] { + font-family: Glyphicons Social Regular +} + +.glyphicon-lg, [glyph~="lg"] { + font-size: 1.33333333em; + line-height: .75em; + vertical-align: -15% +} + +.glyphicon-2x, [glyph~="2x"] { + font-size: 2em +} + +.glyphicon-3x, [glyph~="3x"] { + font-size: 3em +} + +.glyphicon-4x, [glyph~="4x"] { + font-size: 4em +} + +.glyphicon-5x, [glyph~="5x"] { + font-size: 5em +} + +[class*="glyphicon"].glass:before, [glyph~="glass"]:before { + content: "\e001" +} + +[class*="glyphicon"].leaf:before, [glyph~="leaf"]:before { + content: "\e002" +} + +[class*="glyphicon"].dog:before, [glyph~="dog"]:before { + content: "\1F415" +} + +[class*="glyphicon"].user:before, [glyph~="user"]:before { + content: "\e004" +} + +[class*="glyphicon"].girl:before, [glyph~="girl"]:before { + content: "\1F467" +} + +[class*="glyphicon"].car:before, [glyph~="car"]:before { + content: "\e006" +} + +[class*="glyphicon"].user-add:before { + content: "\e007" +} + +[class*="glyphicon"].user-remove:before { + content: "\e008" +} + +[class*="glyphicon"].film:before, [glyph~="film"]:before { + content: "\e009" +} + +[class*="glyphicon"].magic:before, [glyph~="magic"]:before { + content: "\e010" +} + +[class*="glyphicon"].envelope:before, [glyph~="envelope"]:before { + content: "\2709" +} + +[class*="glyphicon"].camera:before, [glyph~="camera"]:before { + content: "\1F4F7" +} + +[class*="glyphicon"].heart:before, [glyph~="heart"]:before { + content: "\e013" +} + +[class*="glyphicon"].beach-umbrella:before { + content: "\e014" +} + +[class*="glyphicon"].train:before, [glyph~="train"]:before { + content: "\1F686" +} + +[class*="glyphicon"].print:before, [glyph~="print"]:before { + content: "\e016" +} + +[class*="glyphicon"].bin:before, [glyph~="bin"]:before { + content: "\e017" +} + +[class*="glyphicon"].music:before, [glyph~="music"]:before { + content: "\e018" +} + +[class*="glyphicon"].note:before, [glyph~="note"]:before { + content: "\e019" +} + +[class*="glyphicon"].heart-empty:before { + content: "\e020" +} + +[class*="glyphicon"].home:before, [glyph~="home"]:before { + content: "\e021" +} + +[class*="glyphicon"].snowflake:before, [glyph~="snowflake"]:before { + content: "\2744" +} + +[class*="glyphicon"].fire:before, [glyph~="fire"]:before { + content: "\1F525" +} + +[class*="glyphicon"].magnet:before, [glyph~="magnet"]:before { + content: "\e024" +} + +[class*="glyphicon"].parents:before, [glyph~="parents"]:before { + content: "\e025" +} + +[class*="glyphicon"].binoculars:before, [glyph~="binoculars"]:before { + content: "\e026" +} + +[class*="glyphicon"].road:before, [glyph~="road"]:before { + content: "\e027" +} + +[class*="glyphicon"].search:before, [glyph~="search"]:before { + content: "\e028" +} + +[class*="glyphicon"].cars:before, [glyph~="cars"]:before { + content: "\e029" +} + +[class*="glyphicon"].notes-2:before { + content: "\e030" +} + +[class*="glyphicon"].pencil:before, [glyph~="pencil"]:before { + content: "\270F" +} + +[class*="glyphicon"].bus:before, [glyph~="bus"]:before { + content: "\1F68C" +} + +[class*="glyphicon"].wifi-alt:before { + content: "\e033" +} + +[class*="glyphicon"].luggage:before, [glyph~="luggage"]:before { + content: "\e034" +} + +[class*="glyphicon"].old-man:before { + content: "\e035" +} + +[class*="glyphicon"].woman:before, [glyph~="woman"]:before { + content: "\1F469" +} + +[class*="glyphicon"].file:before, [glyph~="file"]:before { + content: "\e037" +} + +[class*="glyphicon"].coins:before, [glyph~="coins"]:before { + content: "\e038" +} + +[class*="glyphicon"].airplane:before, [glyph~="airplane"]:before { + content: "\2708" +} + +[class*="glyphicon"].notes:before, [glyph~="notes"]:before { + content: "\e040" +} + +[class*="glyphicon"].stats:before, [glyph~="stats"]:before { + content: "\e041" +} + +[class*="glyphicon"].charts:before, [glyph~="charts"]:before { + content: "\e042" +} + +[class*="glyphicon"].pie-chart:before { + content: "\e043" +} + +[class*="glyphicon"].group:before, [glyph~="group"]:before { + content: "\e044" +} + +[class*="glyphicon"].keys:before, [glyph~="keys"]:before { + content: "\e045" +} + +[class*="glyphicon"].calendar:before, [glyph~="calendar"]:before { + content: "\1F4C5" +} + +[class*="glyphicon"].router:before, [glyph~="router"]:before { + content: "\e047" +} + +[class*="glyphicon"].camera-small:before { + content: "\e048" +} + +[class*="glyphicon"].dislikes:before, [glyph~="dislikes"]:before { + content: "\e049" +} + +[class*="glyphicon"].star:before, [glyph~="star"]:before { + content: "\e050" +} + +[class*="glyphicon"].link:before, [glyph~="link"]:before { + content: "\e051" +} + +[class*="glyphicon"].eye-open:before { + content: "\e052" +} + +[class*="glyphicon"].eye-close:before { + content: "\e053" +} + +[class*="glyphicon"].alarm:before, [glyph~="alarm"]:before { + content: "\e054" +} + +[class*="glyphicon"].clock:before, [glyph~="clock"]:before { + content: "\e055" +} + +[class*="glyphicon"].stopwatch:before, [glyph~="stopwatch"]:before { + content: "\e056" +} + +[class*="glyphicon"].projector:before, [glyph~="projector"]:before { + content: "\e057" +} + +[class*="glyphicon"].history:before, [glyph~="history"]:before { + content: "\e058" +} + +[class*="glyphicon"].truck:before, [glyph~="truck"]:before { + content: "\e059" +} + +[class*="glyphicon"].cargo:before, [glyph~="cargo"]:before { + content: "\e060" +} + +[class*="glyphicon"].compass:before, [glyph~="compass"]:before { + content: "\e061" +} + +[class*="glyphicon"].keynote:before, [glyph~="keynote"]:before { + content: "\e062" +} + +[class*="glyphicon"].paperclip:before, [glyph~="paperclip"]:before { + content: "\1F4CE" +} + +[class*="glyphicon"].power:before, [glyph~="power"]:before { + content: "\e064" +} + +[class*="glyphicon"].lightbulb:before, [glyph~="lightbulb"]:before { + content: "\e065" +} + +[class*="glyphicon"].tag:before, [glyph~="tag"]:before { + content: "\e066" +} + +[class*="glyphicon"].tags:before, [glyph~="tags"]:before { + content: "\e067" +} + +[class*="glyphicon"].cleaning:before, [glyph~="cleaning"]:before { + content: "\e068" +} + +[class*="glyphicon"].ruller:before, [glyph~="ruller"]:before { + content: "\e069" +} + +[class*="glyphicon"].gift:before, [glyph~="gift"]:before { + content: "\e070" +} + +[class*="glyphicon"].umbrella:before, [glyph~="umbrella"]:before { + content: "\2602" +} + +[class*="glyphicon"].book:before, [glyph~="book"]:before { + content: "\e072" +} + +[class*="glyphicon"].bookmark:before, [glyph~="bookmark"]:before { + content: "\1F516" +} + +[class*="glyphicon"].wifi:before, [glyph~="wifi"]:before { + content: "\e074" +} + +[class*="glyphicon"].cup:before, [glyph~="cup"]:before { + content: "\e075" +} + +[class*="glyphicon"].stroller:before, [glyph~="stroller"]:before { + content: "\e076" +} + +[class*="glyphicon"].headphones:before, [glyph~="headphones"]:before { + content: "\e077" +} + +[class*="glyphicon"].headset:before, [glyph~="headset"]:before { + content: "\e078" +} + +[class*="glyphicon"].warning-sign:before { + content: "\e079" +} + +[class*="glyphicon"].signal:before, [glyph~="signal"]:before { + content: "\e080" +} + +[class*="glyphicon"].retweet:before, [glyph~="retweet"]:before { + content: "\e081" +} + +[class*="glyphicon"].refresh:before, [glyph~="refresh"]:before { + content: "\e082" +} + +[class*="glyphicon"].roundabout:before, [glyph~="roundabout"]:before { + content: "\e083" +} + +[class*="glyphicon"].random:before, [glyph~="random"]:before { + content: "\e084" +} + +[class*="glyphicon"].heat:before, [glyph~="heat"]:before { + content: "\e085" +} + +[class*="glyphicon"].repeat:before, [glyph~="repeat"]:before { + content: "\e086" +} + +[class*="glyphicon"].display:before, [glyph~="display"]:before { + content: "\e087" +} + +[class*="glyphicon"].log-book:before { + content: "\e088" +} + +[class*="glyphicon"].address-book:before { + content: "\e089" +} + +[class*="glyphicon"].building:before, [glyph~="building"]:before { + content: "\e090" +} + +[class*="glyphicon"].eyedropper:before, [glyph~="eyedropper"]:before { + content: "\e091" +} + +[class*="glyphicon"].adjust:before, [glyph~="adjust"]:before { + content: "\e092" +} + +[class*="glyphicon"].tint:before, [glyph~="tint"]:before { + content: "\e093" +} + +[class*="glyphicon"].crop:before, [glyph~="crop"]:before { + content: "\e094" +} + +[class*="glyphicon"].vector-path-square:before { + content: "\e095" +} + +[class*="glyphicon"].vector-path-circle:before { + content: "\e096" +} + +[class*="glyphicon"].vector-path-polygon:before { + content: "\e097" +} + +[class*="glyphicon"].vector-path-line:before { + content: "\e098" +} + +[class*="glyphicon"].vector-path-curve:before { + content: "\e099" +} + +[class*="glyphicon"].vector-path-all:before { + content: "\e100" +} + +[class*="glyphicon"].font:before, [glyph~="font"]:before { + content: "\e101" +} + +[class*="glyphicon"].italic:before, [glyph~="italic"]:before { + content: "\e102" +} + +[class*="glyphicon"].bold:before, [glyph~="bold"]:before { + content: "\e103" +} + +[class*="glyphicon"].text-underline:before { + content: "\e104" +} + +[class*="glyphicon"].text-strike:before { + content: "\e105" +} + +[class*="glyphicon"].text-height:before { + content: "\e106" +} + +[class*="glyphicon"].text-width:before { + content: "\e107" +} + +[class*="glyphicon"].text-resize:before { + content: "\e108" +} + +[class*="glyphicon"].left-indent:before { + content: "\e109" +} + +[class*="glyphicon"].right-indent:before { + content: "\e110" +} + +[class*="glyphicon"].align-left:before { + content: "\e111" +} + +[class*="glyphicon"].align-center:before { + content: "\e112" +} + +[class*="glyphicon"].align-right:before { + content: "\e113" +} + +[class*="glyphicon"].justify:before, [glyph~="justify"]:before { + content: "\e114" +} + +[class*="glyphicon"].list:before, [glyph~="list"]:before { + content: "\e115" +} + +[class*="glyphicon"].text-smaller:before { + content: "\e116" +} + +[class*="glyphicon"].text-bigger:before { + content: "\e117" +} + +[class*="glyphicon"].embed:before, [glyph~="embed"]:before { + content: "\e118" +} + +[class*="glyphicon"].embed-close:before { + content: "\e119" +} + +[class*="glyphicon"].table:before, [glyph~="table"]:before { + content: "\e120" +} + +[class*="glyphicon"].message-full:before { + content: "\e121" +} + +[class*="glyphicon"].message-empty:before { + content: "\e122" +} + +[class*="glyphicon"].message-in:before { + content: "\e123" +} + +[class*="glyphicon"].message-out:before { + content: "\e124" +} + +[class*="glyphicon"].message-plus:before { + content: "\e125" +} + +[class*="glyphicon"].message-minus:before { + content: "\e126" +} + +[class*="glyphicon"].message-ban:before { + content: "\e127" +} + +[class*="glyphicon"].message-flag:before { + content: "\e128" +} + +[class*="glyphicon"].message-lock:before { + content: "\e129" +} + +[class*="glyphicon"].message-new:before { + content: "\e130" +} + +[class*="glyphicon"].inbox:before, [glyph~="inbox"]:before { + content: "\e131" +} + +[class*="glyphicon"].inbox-plus:before { + content: "\e132" +} + +[class*="glyphicon"].inbox-minus:before { + content: "\e133" +} + +[class*="glyphicon"].inbox-lock:before { + content: "\e134" +} + +[class*="glyphicon"].inbox-in:before { + content: "\e135" +} + +[class*="glyphicon"].inbox-out:before { + content: "\e136" +} + +[class*="glyphicon"].cogwheel:before, [glyph~="cogwheel"]:before { + content: "\e137" +} + +[class*="glyphicon"].cogwheels:before, [glyph~="cogwheels"]:before { + content: "\e138" +} + +[class*="glyphicon"].picture:before, [glyph~="picture"]:before { + content: "\e139" +} + +[class*="glyphicon"].adjust-alt:before { + content: "\e140" +} + +[class*="glyphicon"].database-lock:before { + content: "\e141" +} + +[class*="glyphicon"].database-plus:before { + content: "\e142" +} + +[class*="glyphicon"].database-minus:before { + content: "\e143" +} + +[class*="glyphicon"].database-ban:before { + content: "\e144" +} + +[class*="glyphicon"].folder-open:before { + content: "\e145" +} + +[class*="glyphicon"].folder-plus:before { + content: "\e146" +} + +[class*="glyphicon"].folder-minus:before { + content: "\e147" +} + +[class*="glyphicon"].folder-lock:before { + content: "\e148" +} + +[class*="glyphicon"].folder-flag:before { + content: "\e149" +} + +[class*="glyphicon"].folder-new:before { + content: "\e150" +} + +[class*="glyphicon"].edit:before, [glyph~="edit"]:before { + content: "\e151" +} + +[class*="glyphicon"].new-window:before { + content: "\e152" +} + +[class*="glyphicon"].check:before, [glyph~="check"]:before { + content: "\e153" +} + +[class*="glyphicon"].unchecked:before, [glyph~="unchecked"]:before { + content: "\e154" +} + +[class*="glyphicon"].more-windows:before { + content: "\e155" +} + +[class*="glyphicon"].show-big-thumbnails:before { + content: "\e156" +} + +[class*="glyphicon"].show-thumbnails:before { + content: "\e157" +} + +[class*="glyphicon"].show-thumbnails-with-lines:before { + content: "\e158" +} + +[class*="glyphicon"].show-lines:before { + content: "\e159" +} + +[class*="glyphicon"].playlist:before, [glyph~="playlist"]:before { + content: "\e160" +} + +[class*="glyphicon"].imac:before, [glyph~="imac"]:before { + content: "\e161" +} + +[class*="glyphicon"].macbook:before, [glyph~="macbook"]:before { + content: "\e162" +} + +[class*="glyphicon"].ipad:before, [glyph~="ipad"]:before { + content: "\e163" +} + +[class*="glyphicon"].iphone:before, [glyph~="iphone"]:before { + content: "\e164" +} + +[class*="glyphicon"].iphone-transfer:before { + content: "\e165" +} + +[class*="glyphicon"].iphone-exchange:before { + content: "\e166" +} + +[class*="glyphicon"].ipod:before, [glyph~="ipod"]:before { + content: "\e167" +} + +[class*="glyphicon"].ipod-shuffle:before { + content: "\e168" +} + +[class*="glyphicon"].ear-plugs:before { + content: "\e169" +} + +[class*="glyphicon"].record:before, [glyph~="record"]:before { + content: "\e170" +} + +[class*="glyphicon"].step-backward:before { + content: "\e171" +} + +[class*="glyphicon"].fast-backward:before { + content: "\e172" +} + +[class*="glyphicon"].rewind:before, [glyph~="rewind"]:before { + content: "\e173" +} + +[class*="glyphicon"].play:before, [glyph~="play"]:before { + content: "\e174" +} + +[class*="glyphicon"].pause:before, [glyph~="pause"]:before { + content: "\e175" +} + +[class*="glyphicon"].stop:before, [glyph~="stop"]:before { + content: "\e176" +} + +[class*="glyphicon"].forward:before, [glyph~="forward"]:before { + content: "\e177" +} + +[class*="glyphicon"].fast-forward:before { + content: "\e178" +} + +[class*="glyphicon"].step-forward:before { + content: "\e179" +} + +[class*="glyphicon"].eject:before, [glyph~="eject"]:before { + content: "\e180" +} + +[class*="glyphicon"].facetime-video:before { + content: "\e181" +} + +[class*="glyphicon"].download-alt:before { + content: "\e182" +} + +[class*="glyphicon"].mute:before, [glyph~="mute"]:before { + content: "\e183" +} + +[class*="glyphicon"].volume-down:before { + content: "\e184" +} + +[class*="glyphicon"].volume-up:before { + content: "\e185" +} + +[class*="glyphicon"].screenshot:before, [glyph~="screenshot"]:before { + content: "\e186" +} + +[class*="glyphicon"].move:before, [glyph~="move"]:before { + content: "\e187" +} + +[class*="glyphicon"].more:before, [glyph~="more"]:before { + content: "\e188" +} + +[class*="glyphicon"].brightness-reduce:before { + content: "\e189" +} + +[class*="glyphicon"].brightness-increase:before { + content: "\e190" +} + +[class*="glyphicon"].circle-plus:before { + content: "\e191" +} + +[class*="glyphicon"].circle-minus:before { + content: "\e192" +} + +[class*="glyphicon"].circle-remove:before { + content: "\e193" +} + +[class*="glyphicon"].circle-ok:before { + content: "\e194" +} + +[class*="glyphicon"].circle-question-mark:before { + content: "\e195" +} + +[class*="glyphicon"].circle-info:before { + content: "\e196" +} + +[class*="glyphicon"].circle-exclamation-mark:before { + content: "\e197" +} + +[class*="glyphicon"].remove:before, [glyph~="remove"]:before { + content: "\e198" +} + +[class*="glyphicon"].ok:before, [glyph~="ok"]:before { + content: "\e199" +} + +[class*="glyphicon"].ban:before, [glyph~="ban"]:before { + content: "\e200" +} + +[class*="glyphicon"].download:before, [glyph~="download"]:before { + content: "\e201" +} + +[class*="glyphicon"].upload:before, [glyph~="upload"]:before { + content: "\e202" +} + +[class*="glyphicon"].shopping-cart:before { + content: "\e203" +} + +[class*="glyphicon"].lock:before, [glyph~="lock"]:before { + content: "\1F512" +} + +[class*="glyphicon"].unlock:before, [glyph~="unlock"]:before { + content: "\e205" +} + +[class*="glyphicon"].electricity:before, [glyph~="electricity"]:before { + content: "\e206" +} + +[class*="glyphicon"].ok-2:before { + content: "\e207" +} + +[class*="glyphicon"].remove-2:before { + content: "\e208" +} + +[class*="glyphicon"].cart-out:before { + content: "\e209" +} + +[class*="glyphicon"].cart-in:before { + content: "\e210" +} + +[class*="glyphicon"].left-arrow:before { + content: "\e211" +} + +[class*="glyphicon"].right-arrow:before { + content: "\e212" +} + +[class*="glyphicon"].down-arrow:before { + content: "\e213" +} + +[class*="glyphicon"].up-arrow:before { + content: "\e214" +} + +[class*="glyphicon"].resize-small:before { + content: "\e215" +} + +[class*="glyphicon"].resize-full:before { + content: "\e216" +} + +[class*="glyphicon"].circle-arrow-left:before { + content: "\e217" +} + +[class*="glyphicon"].circle-arrow-right:before { + content: "\e218" +} + +[class*="glyphicon"].circle-arrow-top:before { + content: "\e219" +} + +[class*="glyphicon"].circle-arrow-down:before { + content: "\e220" +} + +[class*="glyphicon"].play-button:before { + content: "\e221" +} + +[class*="glyphicon"].unshare:before, [glyph~="unshare"]:before { + content: "\e222" +} + +[class*="glyphicon"].share:before, [glyph~="share"]:before { + content: "\e223" +} + +[class*="glyphicon"].chevron-right:before { + content: "\e224" +} + +[class*="glyphicon"].chevron-left:before { + content: "\e225" +} + +[class*="glyphicon"].chevron-up:before { + content: "\e224" +} + +[class*="glyphicon"].chevron-up { + -webkit-transform: rotate(-90deg); + -ms-transform: rotate(-90deg); + transform: rotate(-90deg) +} + +[class*="glyphicon"].chevron-down:before { + content: "\e224" +} + +[class*="glyphicon"].chevron-down { + -webkit-transform: rotate(90deg); + -ms-transform: rotate(90deg); + transform: rotate(90deg) +} + +[class*="glyphicon"].bluetooth:before, [glyph~="bluetooth"]:before { + content: "\e226" +} + +[class*="glyphicon"].euro:before, [glyph~="euro"]:before { + content: "\20AC" +} + +[class*="glyphicon"].usd:before, [glyph~="usd"]:before { + content: "\e228" +} + +[class*="glyphicon"].gbp:before, [glyph~="gbp"]:before { + content: "\e229" +} + +[class*="glyphicon"].retweet-2:before { + content: "\e230" +} + +[class*="glyphicon"].moon:before, [glyph~="moon"]:before { + content: "\e231" +} + +[class*="glyphicon"].sun:before, [glyph~="sun"]:before { + content: "\2609" +} + +[class*="glyphicon"].cloud:before, [glyph~="cloud"]:before { + content: "\2601" +} + +[class*="glyphicon"].direction:before, [glyph~="direction"]:before { + content: "\e234" +} + +[class*="glyphicon"].brush:before, [glyph~="brush"]:before { + content: "\e235" +} + +[class*="glyphicon"].pen:before, [glyph~="pen"]:before { + content: "\e236" +} + +[class*="glyphicon"].zoom-in:before { + content: "\e237" +} + +[class*="glyphicon"].zoom-out:before { + content: "\e238" +} + +[class*="glyphicon"].pin:before, [glyph~="pin"]:before { + content: "\e239" +} + +[class*="glyphicon"].albums:before, [glyph~="albums"]:before { + content: "\e240" +} + +[class*="glyphicon"].rotation-lock:before { + content: "\e241" +} + +[class*="glyphicon"].flash:before, [glyph~="flash"]:before { + content: "\e242" +} + +[class*="glyphicon"].google-maps:before { + content: "\e243" +} + +[class*="glyphicon"].anchor:before, [glyph~="anchor"]:before { + content: "\2693" +} + +[class*="glyphicon"].conversation:before, [glyph~="conversation"]:before { + content: "\e245" +} + +[class*="glyphicon"].chat:before, [glyph~="chat"]:before { + content: "\e246" +} + +[class*="glyphicon"].male:before, [glyph~="male"]:before { + content: "\e247" +} + +[class*="glyphicon"].female:before, [glyph~="female"]:before { + content: "\e248" +} + +[class*="glyphicon"].asterisk:before, [glyph~="asterisk"]:before { + content: "\002A" +} + +[class*="glyphicon"].divide:before, [glyph~="divide"]:before { + content: "\00F7" +} + +[class*="glyphicon"].snorkel-diving:before { + content: "\e251" +} + +[class*="glyphicon"].scuba-diving:before { + content: "\e252" +} + +[class*="glyphicon"].oxygen-bottle:before { + content: "\e253" +} + +[class*="glyphicon"].fins:before, [glyph~="fins"]:before { + content: "\e254" +} + +[class*="glyphicon"].fishes:before, [glyph~="fishes"]:before { + content: "\e255" +} + +[class*="glyphicon"].boat:before, [glyph~="boat"]:before { + content: "\e256" +} + +[class*="glyphicon"].delete:before, [glyph~="delete"]:before { + content: "\e257" +} + +[class*="glyphicon"].sheriffs-star:before { + content: "\e258" +} + +[class*="glyphicon"].qrcode:before, [glyph~="qrcode"]:before { + content: "\e259" +} + +[class*="glyphicon"].barcode:before, [glyph~="barcode"]:before { + content: "\e260" +} + +[class*="glyphicon"].pool:before, [glyph~="pool"]:before { + content: "\e261" +} + +[class*="glyphicon"].buoy:before, [glyph~="buoy"]:before { + content: "\e262" +} + +[class*="glyphicon"].spade:before, [glyph~="spade"]:before { + content: "\e263" +} + +[class*="glyphicon"].bank:before, [glyph~="bank"]:before { + content: "\1F3E6" +} + +[class*="glyphicon"].vcard:before, [glyph~="vcard"]:before { + content: "\e265" +} + +[class*="glyphicon"].electrical-plug:before { + content: "\e266" +} + +[class*="glyphicon"].flag:before, [glyph~="flag"]:before { + content: "\e267" +} + +[class*="glyphicon"].credit-card:before { + content: "\e268" +} + +[class*="glyphicon"].keyboard-wireless:before { + content: "\e269" +} + +[class*="glyphicon"].keyboard-wired:before { + content: "\e270" +} + +[class*="glyphicon"].shield:before, [glyph~="shield"]:before { + content: "\e271" +} + +[class*="glyphicon"].ring:before, [glyph~="ring"]:before { + content: "\02DA" +} + +[class*="glyphicon"].cake:before, [glyph~="cake"]:before { + content: "\e273" +} + +[class*="glyphicon"].drink:before, [glyph~="drink"]:before { + content: "\e274" +} + +[class*="glyphicon"].beer:before, [glyph~="beer"]:before { + content: "\e275" +} + +[class*="glyphicon"].fast-food:before { + content: "\e276" +} + +[class*="glyphicon"].cutlery:before, [glyph~="cutlery"]:before { + content: "\e277" +} + +[class*="glyphicon"].pizza:before, [glyph~="pizza"]:before { + content: "\e278" +} + +[class*="glyphicon"].birthday-cake:before { + content: "\e279" +} + +[class*="glyphicon"].tablet:before, [glyph~="tablet"]:before { + content: "\e280" +} + +[class*="glyphicon"].settings:before, [glyph~="settings"]:before { + content: "\e281" +} + +[class*="glyphicon"].bullets:before, [glyph~="bullets"]:before { + content: "\e282" +} + +[class*="glyphicon"].cardio:before, [glyph~="cardio"]:before { + content: "\e283" +} + +[class*="glyphicon"].t-shirt:before { + content: "\e284" +} + +[class*="glyphicon"].pants:before, [glyph~="pants"]:before { + content: "\e285" +} + +[class*="glyphicon"].sweater:before, [glyph~="sweater"]:before { + content: "\e286" +} + +[class*="glyphicon"].fabric:before, [glyph~="fabric"]:before { + content: "\e287" +} + +[class*="glyphicon"].leather:before, [glyph~="leather"]:before { + content: "\e288" +} + +[class*="glyphicon"].scissors:before, [glyph~="scissors"]:before { + content: "\e289" +} + +[class*="glyphicon"].bomb:before, [glyph~="bomb"]:before { + content: "\1F4A3" +} + +[class*="glyphicon"].skull:before, [glyph~="skull"]:before { + content: "\1F480" +} + +[class*="glyphicon"].celebration:before, [glyph~="celebration"]:before { + content: "\e292" +} + +[class*="glyphicon"].tea-kettle:before { + content: "\e293" +} + +[class*="glyphicon"].french-press:before { + content: "\e294" +} + +[class*="glyphicon"].coffe-cup:before { + content: "\e295" +} + +[class*="glyphicon"].pot:before, [glyph~="pot"]:before { + content: "\e296" +} + +[class*="glyphicon"].grater:before, [glyph~="grater"]:before { + content: "\e297" +} + +[class*="glyphicon"].kettle:before, [glyph~="kettle"]:before { + content: "\e298" +} + +[class*="glyphicon"].hospital:before, [glyph~="hospital"]:before { + content: "\1F3E5" +} + +[class*="glyphicon"].hospital-h:before { + content: "\e300" +} + +[class*="glyphicon"].microphone:before, [glyph~="microphone"]:before { + content: "\1F3A4" +} + +[class*="glyphicon"].webcam:before, [glyph~="webcam"]:before { + content: "\e302" +} + +[class*="glyphicon"].temple-christianity-church:before { + content: "\e303" +} + +[class*="glyphicon"].temple-islam:before { + content: "\e304" +} + +[class*="glyphicon"].temple-hindu:before { + content: "\e305" +} + +[class*="glyphicon"].temple-buddhist:before { + content: "\e306" +} + +[class*="glyphicon"].bicycle:before, [glyph~="bicycle"]:before { + content: "\1F6B2" +} + +[class*="glyphicon"].life-preserver:before { + content: "\e308" +} + +[class*="glyphicon"].share-alt:before { + content: "\e309" +} + +[class*="glyphicon"].comments:before, [glyph~="comments"]:before { + content: "\e310" +} + +[class*="glyphicon"].flower:before, [glyph~="flower"]:before { + content: "\2698" +} + +[class*="glyphicon"].baseball:before, [glyph~="baseball"]:before { + content: "\26BE" +} + +[class*="glyphicon"].rugby:before, [glyph~="rugby"]:before { + content: "\e313" +} + +[class*="glyphicon"].ax:before, [glyph~="ax"]:before { + content: "\e314" +} + +[class*="glyphicon"].table-tennis:before { + content: "\e315" +} + +[class*="glyphicon"].bowling:before, [glyph~="bowling"]:before { + content: "\1F3B3" +} + +[class*="glyphicon"].tree-conifer:before { + content: "\e317" +} + +[class*="glyphicon"].tree-deciduous:before { + content: "\e318" +} + +[class*="glyphicon"].more-items:before { + content: "\e319" +} + +[class*="glyphicon"].sort:before, [glyph~="sort"]:before { + content: "\e320" +} + +[class*="glyphicon"].filter:before, [glyph~="filter"]:before { + content: "\e321" +} + +[class*="glyphicon"].gamepad:before, [glyph~="gamepad"]:before { + content: "\e322" +} + +[class*="glyphicon"].playing-dices:before { + content: "\e323" +} + +[class*="glyphicon"].calculator:before, [glyph~="calculator"]:before { + content: "\e324" +} + +[class*="glyphicon"].tie:before, [glyph~="tie"]:before { + content: "\e325" +} + +[class*="glyphicon"].wallet:before, [glyph~="wallet"]:before { + content: "\e326" +} + +[class*="glyphicon"].piano:before, [glyph~="piano"]:before { + content: "\e327" +} + +[class*="glyphicon"].sampler:before, [glyph~="sampler"]:before { + content: "\e328" +} + +[class*="glyphicon"].podium:before, [glyph~="podium"]:before { + content: "\e329" +} + +[class*="glyphicon"].soccer-ball:before { + content: "\e330" +} + +[class*="glyphicon"].blog:before, [glyph~="blog"]:before { + content: "\e331" +} + +[class*="glyphicon"].dashboard:before, [glyph~="dashboard"]:before { + content: "\e332" +} + +[class*="glyphicon"].certificate:before, [glyph~="certificate"]:before { + content: "\e333" +} + +[class*="glyphicon"].bell:before, [glyph~="bell"]:before { + content: "\1F514" +} + +[class*="glyphicon"].candle:before, [glyph~="candle"]:before { + content: "\e335" +} + +[class*="glyphicon"].pushpin:before, [glyph~="pushpin"]:before { + content: "\1F4CC" +} + +[class*="glyphicon"].iphone-shake:before { + content: "\e337" +} + +[class*="glyphicon"].pin-flag:before { + content: "\e338" +} + +[class*="glyphicon"].turtle:before, [glyph~="turtle"]:before { + content: "\1F422" +} + +[class*="glyphicon"].rabbit:before, [glyph~="rabbit"]:before { + content: "\1F407" +} + +[class*="glyphicon"].globe:before, [glyph~="globe"]:before { + content: "\e341" +} + +[class*="glyphicon"].briefcase:before, [glyph~="briefcase"]:before { + content: "\1F4BC" +} + +[class*="glyphicon"].hdd:before, [glyph~="hdd"]:before { + content: "\e343" +} + +[class*="glyphicon"].thumbs-up:before { + content: "\e344" +} + +[class*="glyphicon"].thumbs-down:before { + content: "\e345" +} + +[class*="glyphicon"].hand-right:before { + content: "\e346" +} + +[class*="glyphicon"].hand-left:before { + content: "\e347" +} + +[class*="glyphicon"].hand-up:before { + content: "\e348" +} + +[class*="glyphicon"].hand-down:before { + content: "\e349" +} + +[class*="glyphicon"].fullscreen:before, [glyph~="fullscreen"]:before { + content: "\e350" +} + +[class*="glyphicon"].shopping-bag:before { + content: "\e351" +} + +[class*="glyphicon"].book-open:before { + content: "\e352" +} + +[class*="glyphicon"].nameplate:before, [glyph~="nameplate"]:before { + content: "\e353" +} + +[class*="glyphicon"].nameplate-alt:before { + content: "\e354" +} + +[class*="glyphicon"].vases:before, [glyph~="vases"]:before { + content: "\e355" +} + +[class*="glyphicon"].bullhorn:before, [glyph~="bullhorn"]:before { + content: "\e356" +} + +[class*="glyphicon"].dumbbell:before, [glyph~="dumbbell"]:before { + content: "\e357" +} + +[class*="glyphicon"].suitcase:before, [glyph~="suitcase"]:before { + content: "\e358" +} + +[class*="glyphicon"].file-import:before { + content: "\e359" +} + +[class*="glyphicon"].file-export:before { + content: "\e360" +} + +[class*="glyphicon"].bug:before, [glyph~="bug"]:before { + content: "\1F41B" +} + +[class*="glyphicon"].crown:before, [glyph~="crown"]:before { + content: "\1F451" +} + +[class*="glyphicon"].smoking:before, [glyph~="smoking"]:before { + content: "\e363" +} + +[class*="glyphicon"].cloud-upload:before { + content: "\e364" +} + +[class*="glyphicon"].cloud-download:before { + content: "\e365" +} + +[class*="glyphicon"].restart:before, [glyph~="restart"]:before { + content: "\e366" +} + +[class*="glyphicon"].security-camera:before { + content: "\e367" +} + +[class*="glyphicon"].expand:before, [glyph~="expand"]:before { + content: "\e368" +} + +[class*="glyphicon"].collapse:before, [glyph~="collapse"]:before { + content: "\e369" +} + +[class*="glyphicon"].collapse-top:before { + content: "\e370" +} + +[class*="glyphicon"].globe-af:before { + content: "\e371" +} + +[class*="glyphicon"].global:before, [glyph~="global"]:before { + content: "\e372" +} + +[class*="glyphicon"].spray:before, [glyph~="spray"]:before { + content: "\e373" +} + +[class*="glyphicon"].nails:before, [glyph~="nails"]:before { + content: "\e374" +} + +[class*="glyphicon"].claw-hammer:before { + content: "\e375" +} + +[class*="glyphicon"].classic-hammer:before { + content: "\e376" +} + +[class*="glyphicon"].hand-saw:before { + content: "\e377" +} + +[class*="glyphicon"].riflescope:before, [glyph~="riflescope"]:before { + content: "\e378" +} + +[class*="glyphicon"].electrical-socket-eu:before { + content: "\e379" +} + +[class*="glyphicon"].electrical-socket-us:before { + content: "\e380" +} + +[class*="glyphicon"].message-forward:before { + content: "\e381" +} + +[class*="glyphicon"].coat-hanger:before { + content: "\e382" +} + +[class*="glyphicon"].dress:before, [glyph~="dress"]:before { + content: "\1F457" +} + +[class*="glyphicon"].bathrobe:before, [glyph~="bathrobe"]:before { + content: "\e384" +} + +[class*="glyphicon"].shirt:before, [glyph~="shirt"]:before { + content: "\e385" +} + +[class*="glyphicon"].underwear:before, [glyph~="underwear"]:before { + content: "\e386" +} + +[class*="glyphicon"].log-in:before { + content: "\e387" +} + +[class*="glyphicon"].log-out:before { + content: "\e388" +} + +[class*="glyphicon"].exit:before, [glyph~="exit"]:before { + content: "\e389" +} + +[class*="glyphicon"].new-window-alt:before { + content: "\e390" +} + +[class*="glyphicon"].video-sd:before { + content: "\e391" +} + +[class*="glyphicon"].video-hd:before { + content: "\e392" +} + +[class*="glyphicon"].subtitles:before, [glyph~="subtitles"]:before { + content: "\e393" +} + +[class*="glyphicon"].sound-stereo:before { + content: "\e394" +} + +[class*="glyphicon"].sound-dolby:before { + content: "\e395" +} + +[class*="glyphicon"].sound-5-1:before { + content: "\e396" +} + +[class*="glyphicon"].sound-6-1:before { + content: "\e397" +} + +[class*="glyphicon"].sound-7-1:before { + content: "\e398" +} + +[class*="glyphicon"].copyright-mark:before { + content: "\e399" +} + +[class*="glyphicon"].registration-mark:before { + content: "\e400" +} + +[class*="glyphicon"].radar:before, [glyph~="radar"]:before { + content: "\e401" +} + +[class*="glyphicon"].skateboard:before, [glyph~="skateboard"]:before { + content: "\e402" +} + +[class*="glyphicon"].golf-course:before { + content: "\e403" +} + +[class*="glyphicon"].sorting:before, [glyph~="sorting"]:before { + content: "\e404" +} + +[class*="glyphicon"].sort-by-alphabet:before { + content: "\e405" +} + +[class*="glyphicon"].sort-by-alphabet-alt:before { + content: "\e406" +} + +[class*="glyphicon"].sort-by-order:before { + content: "\e407" +} + +[class*="glyphicon"].sort-by-order-alt:before { + content: "\e408" +} + +[class*="glyphicon"].sort-by-attributes:before { + content: "\e409" +} + +[class*="glyphicon"].sort-by-attributes-alt:before { + content: "\e410" +} + +[class*="glyphicon"].compressed:before, [glyph~="compressed"]:before { + content: "\e411" +} + +[class*="glyphicon"].package:before, [glyph~="package"]:before { + content: "\1F4E6" +} + +[class*="glyphicon"].cloud-plus:before { + content: "\e413" +} + +[class*="glyphicon"].cloud-minus:before { + content: "\e414" +} + +[class*="glyphicon"].disk-save:before { + content: "\e415" +} + +[class*="glyphicon"].disk-open:before { + content: "\e416" +} + +[class*="glyphicon"].disk-saved:before { + content: "\e417" +} + +[class*="glyphicon"].disk-remove:before { + content: "\e418" +} + +[class*="glyphicon"].disk-import:before { + content: "\e419" +} + +[class*="glyphicon"].disk-export:before { + content: "\e420" +} + +[class*="glyphicon"].tower:before, [glyph~="tower"]:before { + content: "\e421" +} + +[class*="glyphicon"].send:before, [glyph~="send"]:before { + content: "\e422" +} + +[class*="glyphicon"].git-branch:before { + content: "\e423" +} + +[class*="glyphicon"].git-create:before { + content: "\e424" +} + +[class*="glyphicon"].git-private:before { + content: "\e425" +} + +[class*="glyphicon"].git-delete:before { + content: "\e426" +} + +[class*="glyphicon"].git-merge:before { + content: "\e427" +} + +[class*="glyphicon"].git-pull-request:before { + content: "\e428" +} + +[class*="glyphicon"].git-compare:before { + content: "\e429" +} + +[class*="glyphicon"].git-commit:before { + content: "\e430" +} + +[class*="glyphicon"].construction-cone:before { + content: "\e431" +} + +[class*="glyphicon"].shoe-steps:before { + content: "\e432" +} + +[class*="glyphicon"].plus:before, [glyph~="plus"]:before { + content: "\002B" +} + +[class*="glyphicon"].minus:before, [glyph~="minus"]:before { + content: "\2212" +} + +[class*="glyphicon"].redo:before, [glyph~="redo"]:before { + content: "\e435" +} + +[class*="glyphicon"].undo:before, [glyph~="undo"]:before { + content: "\e436" +} + +[class*="glyphicon"].golf:before, [glyph~="golf"]:before { + content: "\e437" +} + +[class*="glyphicon"].hockey:before, [glyph~="hockey"]:before { + content: "\e438" +} + +[class*="glyphicon"].pipe:before, [glyph~="pipe"]:before { + content: "\e439" +} + +[class*="glyphicon"].wrench:before, [glyph~="wrench"]:before { + content: "\1F527" +} + +[class*="glyphicon"].folder-closed:before { + content: "\e441" +} + +[class*="glyphicon"].phone-alt:before { + content: "\e442" +} + +[class*="glyphicon"].earphone:before, [glyph~="earphone"]:before { + content: "\e443" +} + +[class*="glyphicon"].floppy-disk:before { + content: "\e444" +} + +[class*="glyphicon"].floppy-saved:before { + content: "\e445" +} + +[class*="glyphicon"].floppy-remove:before { + content: "\e446" +} + +[class*="glyphicon"].floppy-save:before { + content: "\e447" +} + +[class*="glyphicon"].floppy-open:before { + content: "\e448" +} + +[class*="glyphicon"].translate:before, [glyph~="translate"]:before { + content: "\e449" +} + +[class*="glyphicon"].fax:before, [glyph~="fax"]:before { + content: "\e450" +} + +[class*="glyphicon"].factory:before, [glyph~="factory"]:before { + content: "\1F3ED" +} + +[class*="glyphicon"].shop-window:before { + content: "\e452" +} + +[class*="glyphicon"].shop:before, [glyph~="shop"]:before { + content: "\e453" +} + +[class*="glyphicon"].kiosk:before, [glyph~="kiosk"]:before { + content: "\e454" +} + +[class*="glyphicon"].kiosk-wheels:before { + content: "\e455" +} + +[class*="glyphicon"].kiosk-light:before { + content: "\e456" +} + +[class*="glyphicon"].kiosk-food:before { + content: "\e457" +} + +[class*="glyphicon"].transfer:before, [glyph~="transfer"]:before { + content: "\e458" +} + +[class*="glyphicon"].money:before, [glyph~="money"]:before { + content: "\e459" +} + +[class*="glyphicon"].header:before, [glyph~="header"]:before { + content: "\e460" +} + +[class*="glyphicon"].blacksmith:before, [glyph~="blacksmith"]:before { + content: "\e461" +} + +[class*="glyphicon"].saw-blade:before { + content: "\e462" +} + +[class*="glyphicon"].basketball:before, [glyph~="basketball"]:before { + content: "\e463" +} + +[class*="glyphicon"].server:before, [glyph~="server"]:before { + content: "\e464" +} + +[class*="glyphicon"].server-plus:before { + content: "\e465" +} + +[class*="glyphicon"].server-minus:before { + content: "\e466" +} + +[class*="glyphicon"].server-ban:before { + content: "\e467" +} + +[class*="glyphicon"].server-flag:before { + content: "\e468" +} + +[class*="glyphicon"].server-lock:before { + content: "\e469" +} + +[class*="glyphicon"].server-new:before { + content: "\e470" +} + +.glyphicon-social.pinterest:before, [glyph~="social pinterest"]:before { + content: "\e001" +} + +.glyphicon-social.dropbox:before, [glyph~="social dropbox"]:before { + content: "\e002" +} + +.glyphicon-social.google_plus:before, [glyph~="social google_plus"]:before { + content: "\e003" +} + +.glyphicon-social.jolicloud:before, [glyph~="social jolicloud"]:before { + content: "\e004" +} + +.glyphicon-social.yahoo:before, [glyph~="social yahoo"]:before { + content: "\e005" +} + +.glyphicon-social.blogger:before, [glyph~="social blogger"]:before { + content: "\e006" +} + +.glyphicon-social.picasa:before, [glyph~="social picasa"]:before { + content: "\e007" +} + +.glyphicon-social.amazon:before, [glyph~="social amazon"]:before { + content: "\e008" +} + +.glyphicon-social.tumblr:before, [glyph~="social tumblr"]:before { + content: "\e009" +} + +.glyphicon-social.wordpress:before, [glyph~="social wordpress"]:before { + content: "\e010" +} + +.glyphicon-social.instapaper:before, [glyph~="social instapaper"]:before { + content: "\e011" +} + +.glyphicon-social.evernote:before, [glyph~="social evernote"]:before { + content: "\e012" +} + +.glyphicon-social.xing:before, [glyph~="social xing"]:before { + content: "\e013" +} + +.glyphicon-social.zootool:before, [glyph~="social zootool"]:before { + content: "\e014" +} + +.glyphicon-social.dribbble:before, [glyph~="social dribbble"]:before { + content: "\e015" +} + +.glyphicon-social.deviantart:before, [glyph~="social deviantart"]:before { + content: "\e016" +} + +.glyphicon-social.read_it_later:before, [glyph~="social read_it_later"]:before { + content: "\e017" +} + +.glyphicon-social.linked_in:before, [glyph~="social linked_in"]:before { + content: "\e018" +} + +.glyphicon-social.forrst:before, [glyph~="social forrst"]:before { + content: "\e019" +} + +.glyphicon-social.pinboard:before, [glyph~="social pinboard"]:before { + content: "\e020" +} + +.glyphicon-social.behance:before, [glyph~="social behance"]:before { + content: "\e021" +} + +.glyphicon-social.github:before, [glyph~="social github"]:before { + content: "\e022" +} + +.glyphicon-social.youtube:before, [glyph~="social youtube"]:before { + content: "\e023" +} + +.glyphicon-social.skitch:before, [glyph~="social skitch"]:before { + content: "\e024" +} + +.glyphicon-social.foursquare:before, [glyph~="social foursquare"]:before { + content: "\e025" +} + +.glyphicon-social.quora:before, [glyph~="social quora"]:before { + content: "\e026" +} + +.glyphicon-social.badoo:before, [glyph~="social badoo"]:before { + content: "\e027" +} + +.glyphicon-social.spotify:before, [glyph~="social spotify"]:before { + content: "\e028" +} + +.glyphicon-social.stumbleupon:before, [glyph~="social stumbleupon"]:before { + content: "\e029" +} + +.glyphicon-social.readability:before, [glyph~="social readability"]:before { + content: "\e030" +} + +.glyphicon-social.facebook:before, [glyph~="social facebook"]:before { + content: "\e031" +} + +.glyphicon-social.twitter:before, [glyph~="social twitter"]:before { + content: "\e032" +} + +.glyphicon-social.instagram:before, [glyph~="social instagram"]:before { + content: "\e033" +} + +.glyphicon-social.posterous_spaces:before, [glyph~="social posterous_spaces"]:before { + content: "\e034" +} + +.glyphicon-social.vimeo:before, [glyph~="social vimeo"]:before { + content: "\e035" +} + +.glyphicon-social.flickr:before, [glyph~="social flickr"]:before { + content: "\e036" +} + +.glyphicon-social.last_fm:before, [glyph~="social last_fm"]:before { + content: "\e037" +} + +.glyphicon-social.rss:before, [glyph~="social rss"]:before { + content: "\e038" +} + +.glyphicon-social.skype:before, [glyph~="social skype"]:before { + content: "\e039" +} + +.glyphicon-social.e-mail:before { + content: "\e040" +} + +.glyphicon-social.vine:before, [glyph~="social vine"]:before { + content: "\e041" +} + +.glyphicon-social.myspace:before, [glyph~="social myspace"]:before { + content: "\e042" +} + +.glyphicon-social.goodreads:before, [glyph~="social goodreads"]:before { + content: "\e043" +} + +.glyphicon-social.apple:before, [glyph~="social apple"]:before { + content: "\F8FF" +} + +.glyphicon-social.windows:before, [glyph~="social windows"]:before { + content: "\e045" +} + +.glyphicon-social.yelp:before, [glyph~="social yelp"]:before { + content: "\e046" +} + +.glyphicon-social.playstation:before, [glyph~="social playstation"]:before { + content: "\e047" +} + +.glyphicon-social.xbox:before, [glyph~="social xbox"]:before { + content: "\e048" +} + +.glyphicon-social.android:before, [glyph~="social android"]:before { + content: "\e049" +} + +.glyphicon-social.ios:before, [glyph~="social ios"]:before { + content: "\e050" +} diff --git a/opa/google.css b/opa/google.css new file mode 100644 index 0000000..b800ac7 --- /dev/null +++ b/opa/google.css @@ -0,0 +1,68 @@ +.wtp-w { + display: inline-block; + margin: 8px 0 0 4px; + transition: all 0.2s!important; +} + +.wtp-w, .web-to-plex-minion { + color: #ffffff!important; +} + +.web-to-plex-minion { + background: rgba(0,0,0,0)!important; +} + +.wtp-b { + background-color: #e5a00d!important; + line-height: 36px; + border-radius: 4px; + border: 1px; + font-size: 14px; + height: 36px; + padding: 0 20px; + box-shadow: 0 1px 0 rgba(0,0,0,0.05); + box-sizing: border-box; + cursor: pointer; + display: inline-block; + font-style: normal; + font-weight: 500; + min-width: 40px; + position: relative; + text-align: center; + text-decoration: none; + white-space: no-wrap; + vertical-align: middle; +} + +.wtp-b:hover { + background-color: #fbc022!important; + box-shadow: inset 0 -2px 0 rgba(0,0,0,0.27); +} + +.wtp--download .wtp-b { + background-color: var(--blue)!important; +} + +.wtp--download .wtp-b:hover { + background-color: var(--light-blue)!important; +} + +.wtp--found .wtp-b { + background-color: var(--orange)!important; +} + +.wtp--found .wtp-b:hover { + background-color: var(--light-orange)!important; +} + +.wtp-b:empty { + display: none!important; +} + +#tt--0-0 { + color: #666!important; +} + +#tt--0-0:hover { + color: #888!important; +} diff --git a/opa/google.play.js b/opa/google.play.js index 603e989..1e2c0b8 100644 --- a/opa/google.play.js +++ b/opa/google.play.js @@ -21,4 +21,46 @@ let script = { 'movie': 'show' ), + + "minions": () => { + let type = script.getType(); + + let actions = $('wishlist-add, wishlist-added'); + + if(actions.empty) + return; + + + actions.forEach(element => { + while(/c-wiz/i.test(element.parentElement.tagName)) + element = element.parentElement; + element = element.parentElement; + + let next, first, second; + + if(type == 'movie') { + next = element.nextElementSibling.firstElementChild; + first = next.firstChild; + second = first.firstChild; + } else { + next = furnish('div', {}, + first = furnish('span.wtp-w', {}, + second = furnish('button.wtp-b') + ) + ); + + element.appendChild(next); + } + + let minion; + let parent = furnish(`span.${['wtp-w', ...first.classList].join('.')}`, {}, + furnish(`button.${['wtp-b', ...second.classList].join('.')}`, {}, + minion = furnish('a.web-to-plex-minion', {}, 'Web to Plex') + ) + ); + + addMinions(parent, minion); + next.insertBefore(parent, first); + }); + }, }; diff --git a/opa/helpers.js b/opa/helpers.js index 9ac30a1..df07bf6 100644 --- a/opa/helpers.js +++ b/opa/helpers.js @@ -1,8 +1,10 @@ +function $(selector, container = document) { + return queryBy(selector, container); +} + async function load(name = '') { if(!name) return; - let HELPERS_STORAGE = chrome.storage.sync || chrome.storage.local; - name = '~/cache/' + (name.toLowerCase().replace(/\s+/g, '_')); return new Promise((resolve, reject) => { @@ -12,20 +14,13 @@ async function load(name = '') { return resolve(data); } - HELPERS_STORAGE.get(null, DISK => { - if (chrome.runtime.lastError) - chrome.storage.local.get(null, LOAD); - else - LOAD(DISK); - }); + HELPERS_STORAGE.get(null, DISK => LOAD(DISK)); }); } async function save(name = '', data) { if(!name) return; - let HELPERS_STORAGE = chrome.storage.sync || chrome.storage.local; - name = '~/cache/' + (name.toLowerCase().replace(/\s+/g, '_')); data = JSON.stringify(data); @@ -35,8 +30,6 @@ async function save(name = '', data) { } async function kill(name) { - let HELPERS_STORAGE = chrome.storage.sync || chrome.storage.local; - return HELPERS_STORAGE.remove(['~/cache/' + (name.toLowerCase().replace(/\s+/g, '_'))]); } diff --git a/opa/hulu.css b/opa/hulu.css new file mode 100644 index 0000000..f75c93d --- /dev/null +++ b/opa/hulu.css @@ -0,0 +1,37 @@ +.web-to-plex-minion { + background: 0 !important; + border: 0 !important; + display: inline-block !important; + color: #e6e6e6 !important; + font-size: 12px !important; + height: 20px !important; + text-decoration: none !important; + user-select: none !important; + vertical-align: top !important; + margin-top: 35%!important; + outline: #0000!important; +} + +.web-to-plex-minion.wtp--download { + color: #f45a26 !important; +} + +.web-to-plex-minion.wtp--download:hover { + color: #f67e56 !important; +} + +.web-to-plex-minion.wtp--found { + color: #e5a00d !important; +} + +.web-to-plex-minion.wtp--found:hover { + color: #f9be03 !important; +} + +#tt--0-0 { + color: #666 !important; +} + +#tt--0-0:hover { + color: #888 !important; +} diff --git a/opa/hulu.js b/opa/hulu.js index 89a8081..99ad143 100644 --- a/opa/hulu.js +++ b/opa/hulu.js @@ -45,4 +45,25 @@ let script = { 'show'; } }, + + "minions": () => { + let actions = $('.Details > .SimpleModalNav'); + + if(actions.empty) + return; + + actions.forEach(element => { + let minion, + sibling = $('.Nav__spacer ~ .Nav__item', element).last; + + let parent = furnish('div.Nav__item', {}, + minion = furnish('button.web-to-plex-minion', {}, + furnish('img', { src: IMAGES.icon_32 }) + ) + ); + + addMinions(minion); + element.insertBefore(parent, sibling); + }); + }, }; diff --git a/opa/imdb.css b/opa/imdb.css new file mode 100644 index 0000000..48c3858 --- /dev/null +++ b/opa/imdb.css @@ -0,0 +1,78 @@ +.web-to-plex-minion { + background: #727272; + border-radius: 1000px; + color: #fff!important; + display: inline-block; + text-decoration: none!important; + text-transform: uppercase; + + margin-top: 10px; + padding: 5px 10px; +} + +.web-to-plex-minion.wtp--queued { + background: var(--light-blue)!important; +} + +.web-to-plex-minion.wtp--queued:hover { + background: var(--blue)!important; +} + +.web-to-plex-minion.wtp--download { + background: var(--dark-blue)!important; +} + +.web-to-plex-minion.wtp--download:hover { + background: var(--blue)!important; +} + +.web-to-plex-minion.wtp--found { + background: var(--orange)!important; +} + +.web-to-plex-minion.wtp--found:hover { + background: var(--light-orange)!important; +} + +.lister-list .web-to-plex-minion { + background: 0!important; + border: 0!important; + border-bottom: 5px solid #727272!important; + border-radius: 0!important; + font-size: 10px; + + margin-top: 0; + padding: 4px 6px; +} + +.lister-list .web-to-plex-minion.wtp--queued { + border-color: var(--light-blue)!important; +} + +.lister-list .web-to-plex-minion.wtp--queued:hover { + border-color: var(--blue)!important; +} + +.lister-list .web-to-plex-minion.wtp--download { + border-color: var(--dark-blue)!important; +} + +.lister-list .web-to-plex-minion.wtp--download:hover { + border-color: var(--blue)!important; +} + +.lister-list .web-to-plex-minion.wtp--found { + border-color: var(--orange)!important; +} + +.lister-list .web-to-plex-minion.wtp--found:hover { + border-color: var(--light-orange)!important; +} + +#tt--0-0 { + background: var(--grey)!important; +} + +#tt--0-0:hover { + background: var(--light-grey)!important; +} diff --git a/opa/imdb.js b/opa/imdb.js index dc60999..a187f60 100644 --- a/opa/imdb.js +++ b/opa/imdb.js @@ -116,4 +116,40 @@ let script = { }, "clean": year => (year + '').replace(/^\(|\)$/g, '').trim(), + + "minions": () => { + let type = script.getType(), + actions; + + if(type == 'list') + actions = $('.lister-list .lister-col-wrapper'); + else + actions = $('.plot_summary'); + + if(actions.empty) + return; + + actions.forEach(element => { + let minion; + + if(type == 'list') { + let processed = script.process(element.parentElement.parentElement); + + if(!processed) + return; + + minion = furnish('a.web-to-plex-minion', { imdb: processed.IMDbID }, + furnish('img', { src: IMAGES.icon_32 }) + ); + + addMinions(minion).stayUnique(true); + } else { + minion = furnish('a.web-to-plex-minion', {}, 'Web to Plex'); + + addMinions(minion); + } + + element.appendChild(minion); + }); + }, }; diff --git a/opa/itunes.css b/opa/itunes.css new file mode 100644 index 0000000..1e33870 --- /dev/null +++ b/opa/itunes.css @@ -0,0 +1,9 @@ +.web-to-plex-minion { + border-color: var(--orange) !important; + color: var(--orange) !important; +} + +.web-to-plex-minion:hover { + border-color: var(--light-orange) !important; + color: var(--light-orange) !important; +} diff --git a/opa/itunes.js b/opa/itunes.js index c764b89..f668e5d 100644 --- a/opa/itunes.js +++ b/opa/itunes.js @@ -1,5 +1,7 @@ let script = { - "url": "", + "url": "*://itunes.apple.com/\\w{2,4}/(movie|tv(-season)?)/*", + + "ready": () => (!$('.section').empty && top.__NewCSP__), "init": (ready) => { let _title, _year, _image, R = RegExp; @@ -8,18 +10,18 @@ let script = { switch(type) { case 'movie': - title = $('[class*="movie-header__title"i]').first.textContent; - year = +$('[datetime]').first.textContent; - image = ($('[class*="product"] ~ * picture img').first || {}).src; + title = $('[class~="movie-header__title"i]').first.textContent; + year = +$('time, [datetime]').first.textContent; + image = ($('picture img, [class*="product"] ~ * picture img').first || {}).src; title = title.replace(RegExp(`\\s*\\(${ year }|\\d{4}\\)`), ''); year = year || +R.$1; break; case 'tv': - title = $('h1[itemprop="name"], h1').first.textContent.replace(/\s*\((\d+)\)\s*/, '').trim(); - year = +$('.release-date > *:last-child').first.textContent.replace(/[^]*(\d{4})[^]*?$/g, '$1').trim(); - image = $('[class*="product"] ~ * picture img').first.src; + title = $('[class~="show-header__title"i], h1[itemprop="name"], h1').first.textContent.replace(/\s*\((\d+)\)\s*/, '').trim(); + year = +$('time, .release-date > *:last-child').first.textContent.replace(/[^]*(\d{4})[^]*?$/g, '$1').trim(); + image = $('picture img, [class*="product"] ~ * picture img').first.src; title = title.replace(RegExp(`\\s*\\(${ year }\\)`), ''); break; @@ -35,7 +37,7 @@ let script = { }, "getType": () => { - return /(\/\w+)?\/tv-season\//.test(top.location.pathname)? + return /(\/\w+)?\/tv(-season)?\//.test(top.location.pathname)? 'tv': 'movie' }, @@ -45,4 +47,47 @@ let script = { button.attributes.style.value += '; box-sizing: border-box !important; font-size: 16px !important; line-height: normal !important;'; }, + + "minions": () => { + let actions = $('.product-header > *:last-child > *:first-child'); + + if(actions.empty) + return; + + actions.forEach(element => { + let minion = furnish('a.web-to-plex-minion.we-button.we-button--outlined.we-button-external.icon.icon-external', {}, 'Web to Plex '); + + addMinions(minion); + element.appendChild(minion); + }); + }, }; + + +top.__NewCSP__ = top.__NewCSP__ || false; +if(!top.__NewCSP__) { + /* Add 'data:' to the CSP */ + let ContentSecurityPolicies = $('meta[http-equiv="Content-Security-Policy"]'); + + if(ContentSecurityPolicies.empty) + return; + + ContentSecurityPolicies.forEach(ContentSecurityPolicy => + ContentSecurityPolicy.content = ContentSecurityPolicy.content + .split(';') + .map(src => { + let type; + + src = src.trim().split(' '); + type = src[0]; + + if(type == 'font-src') + src.push('http://webtoplex.github.io', 'https://webtoplex.github.io'); + + return src.join(' '); + }) + .join(';') + ); + + top.__NewCSP__ = true; +} diff --git a/opa/letterboxd.css b/opa/letterboxd.css new file mode 100644 index 0000000..bfe692d --- /dev/null +++ b/opa/letterboxd.css @@ -0,0 +1,10 @@ +.web-to-plex-minion { + display: inline-block; + vertical-align: middle; + font-size: 12px; + line-height: 1.66666667; +} + +.web-to-plex-minion:hover { + color: #f67e56!important; +} diff --git a/opa/letterboxd.js b/opa/letterboxd.js index d8c1ac7..73b15dc 100644 --- a/opa/letterboxd.js +++ b/opa/letterboxd.js @@ -1,5 +1,5 @@ let script = { - "url": "*://*.letterboxd.com/(film|list)/", + "url": "*://*.letterboxd.com/(?:\\w+/)?(film|list)/*", "ready": () => (script.getType('list')? true: !$('.js-watch-panel').empty), @@ -10,16 +10,16 @@ let script = { switch(type) { case 'movie': - title = $('.headline-1[itemprop="name"]').first.textContent.trim(); - year = +$('small[itemprop="datePublished"]').first.textContent.trim(); - image = ($('.image').first || {}).src; + title = $('#featured-film-header .headline-1, .headline-1[itemprop="name"]').first.textContent.trim(); + year = +$('#featured-film-header [href*="/year/"], small[itemprop="datePublished"]').first.textContent.trim(); + image = ($('.film-poster img, .image').first || {}).src; IMDbID = script.getIMDbID(type); return { type, title, year, image, IMDbID }; break; case 'list': - let items = $('.poster-list .poster-container'), + let items = $('.poster-list .poster-container, .poster-list .film-detail'), options = []; items.forEach((element, index, array) => { @@ -73,4 +73,51 @@ let script = { return { type, title, year, image }; }, + + "minions": () => { + let actions = $('.actions-panel ul, .js-watch-panel .services, #watch').first, + type = script.getType(), + featured = (actions.id == 'watch'); + + if(!actions) + return; + + let minion, parent; + + if(type == 'list') { + parent = furnish('li', {}, + furnish('span', {}, + furnish('span.has-icon.icon-16', {}, + furnish('img.web-to-plex-icon.icon', { style: 'background: none !important', src: IMAGES.icon_16, height: 16, width: 16 }), + minion = furnish('a.web-to-plex-minion', {}, 'Web to Plex') + ) + ), + ); + + addMinions(minion); + actions.appendChild(parent); + } else if(featured) { + parent = furnish('div.other', {}, + furnish('img.web-to-plex-icon', { src: IMAGES.icon_16, height: 16, width: 16 }), + minion = furnish('a.web-to-plex-minion.label.more', {}, 'Web to Plex') + ); + + addMinions(minion); + actions.appendChild(parent); + } else { + parent = furnish('p.service', { style: 'display: flex !important' }, + minion = furnish('a.web-to-plex-minion.label.tooltip', {}, + furnish('span.brand', {}, + furnish('img', { src: IMAGES.icon_32, height: 24, width: 24 }) + ), + furnish('span.title', {}, + furnish('span.name', {}, 'Web to Plex') + ) + ) + ); + + addMinions(minion); + actions.appendChild(parent); + } + }, }; diff --git a/opa/manifest.json b/opa/manifest.json index 428b3f6..0fa214f 100644 --- a/opa/manifest.json +++ b/opa/manifest.json @@ -4,7 +4,7 @@ "homepage_url": "https://webtoplex.github.io/", "manifest_version": 2, - "version": "4.1.1.11", + "version": "4.1.2.0", "icons": { "16": "16.png", @@ -155,8 +155,7 @@ "js": ["utils.js", "plex$.js"] },{ "matches": ["*://*/*"], - "js": ["utils.js", "common.js"], - "css": ["common.css", "theme.css"] + "js": ["utils.js", "common.js"] } ], @@ -189,5 +188,5 @@ "contextMenus", "" ], - "web_accessible_resources": ["*.png", "options.*"] + "web_accessible_resources": ["*.woff", "*.png", "options.*", "*.css"] } diff --git a/opa/movieo.css b/opa/movieo.css new file mode 100644 index 0000000..7638814 --- /dev/null +++ b/opa/movieo.css @@ -0,0 +1,35 @@ +.mid-top-actions .share-box { + padding-left: 20px!important; +} + +.web-to-plex-minion { + margin-right: 8px; + color: #566273!important; +} + +.web-to-plex-minion.wtp--download { + border-color: #f45a26!important; + color: #acb4bf!important; +} + +.web-to-plex-minion.wtp--download:hover { + border-color: #f67e56!important; +} + +.web-to-plex-minion.wtp--found { + border-color: #ca7c1f!important; + color: #acb4bf!important; +} + +.web-to-plex-minion.wtp--found:hover { + border-color: #f8c022!important; +} + +#tt--0-0 { + background: #666!important; +} + +#tt--0-0:hover { + background: #888!important; + border-color: #888!important; +} diff --git a/opa/movieo.js b/opa/movieo.js index bca58ba..b052634 100644 --- a/opa/movieo.js +++ b/opa/movieo.js @@ -72,4 +72,25 @@ let script = { return { type, title, year, image }; }, + + "minions": () => { + // The button text in the "Comments" button takes up too much space, so we hide it + // It's very clear that it's about comments event without the text + let comments = $('.mid-top-actions .comments-link .txt'); + + if(!comments.empty) + comments.forEach(comment => comment.remove()); + + let actions = $('.mid-top-actions'); + + if(actions.empty) + return; + + actions.forEach(element => { + let minion = furnish('a.web-to-plex-minion.button.comments-link', {}, 'Web to Plex'); + + addMinions(minion); + element.appendChild(minion); + }); + }, }; diff --git a/opa/options.css b/opa/options.css index 36be984..fe4cd39 100644 --- a/opa/options.css +++ b/opa/options.css @@ -1,3 +1,25 @@ +@charset "UTF-8"; +@import url(glyphs.css); +@import url(colors.css); + +@font-face { + font-family: "Plex"; + src: local(Plex), + url(Plex.woff2) format('woff2'), + url(Plex.woff) format('woff'); + font-weight: 400; + font-style: normal; +} + +@font-face { + font-family: "Plex-bold"; + src: local(Plex-bold), + url(Plex.bold.woff2) format('woff2'), + url(Plex.bold.woff) format('woff'); + font-weight: 400; + font-style: normal; +} + * { outline: #0000 !important; } @@ -9,7 +31,7 @@ html, body { body { background: url(noise.png) fixed, url(background.png) no-repeat fixed center/cover, #3f4245 !important; color: #999 !important; - font-family: Open Sans Regular, Helvetica Neue, Helvetica, Arial, sans-serif, system; + font-family: "Plex", "Open Sans Regular", "Helvetica Neue", Helvetica, Arial, sans-serif, system; flex-grow: 1; overflow-x: hidden; overflow-y: auto; @@ -81,11 +103,26 @@ a { text-decoration: none !important; } +a:hover { + color: #e59029 !important; +} + [target="_blank"]:not(#version)::after { - content: " [\2197]"; + content: "\e223" !important; + font-family: Glyphicons Regular !important; + display: inline-block; font-size: 70%; - vertical-align: super; + height: 12px; + width: 12px; + + margin-left: 0.4em; + /* vertical-align: super; */ +} + +button[target="_blank"]::after { + content: "\e223" !important; + font-family: Glyphicons Regular !important; } hr { @@ -171,62 +208,109 @@ div:not(body > div) { z-index: 18 !important; } +.checkbox input:checked + label:hover, input[type="range"]::-webkit-slider-thumb:hover { + background: #e59029 !important; + transition: all 0.2s; +} + +.checkbox input:not(:checked) + label:hover { + background: #999; +} + button, input[type="button"i], .button { - padding: 10px 18px !important; - font-size: 16px !important; - line-height: 1.33 !important; + background: #fff2 !important; + border: 0.1em solid #cc7b19 !important; + border-bottom-width: 0.4em !important; border-radius: 3px; - font-family: inherit; - text-transform: uppercase; - border: 0; box-shadow: none !important; - position: relative; - overflow: hidden; color: #fff !important; - background: #cc7b19 !important; - margin-bottom: 0; + cursor: pointer !important; + font-family: inherit; + font-size: 16px !important; font-weight: 400 !important; + line-height: 1.33 !important; + overflow: hidden; + text-transform: uppercase; + user-select: none; vertical-align: middle; - cursor: pointer !important; white-space: nowrap; - user-select: none; + + margin-bottom: 0; + padding: 10px 18px !important; + position: relative; + transition: all 0.1s; } -button:hover, input[type="button"i]:hover, .button:hover, .checkbox input:checked + label:hover, input[type="range"]::-webkit-slider-thumb:hover { - background: #e59029 !important; - transition: all 0.1s; +[danger], .danger { + border-color: #f33 !important; + color: #f33; + outline: #f33; } -.checkbox input:not(:checked) + label:hover { - background: #999; +button:hover, input[type="button"i]:hover, .button:hover { + background: #fff4 !important; + border-color: #e59029 !important; transition: all 0.1s; } -[id$="_test"] { - background: #cc7b19 !important; +[danger]:hover, .danger:hover { + border-color: #f44 !important; + color: #f44; + outline: #f44; +} + +[id$="_test"], [id^="enable-"] { margin-bottom: 2px; padding: 10px 8px 10px 10px !important; } [id$="_status"] { - padding: 0 6px !important; - font-size: 16px !important; - border-radius: 3px; - font-family: monospace, sans-serif, sans, arial; + /* background: #6668 !important; */ border: 0; + /* border-radius: 4px; */ box-shadow: none !important; color: #fff !important; - background: #666 !important; - border-radius: 4px; + font-size: 16px !important; + font-family: monospace, sans-serif, sans, arial; + + padding: 0 3px !important; +} + +[notice]:after { + color: #9998 !important; + content: attr(notice); + font-family: Plex; + text-transform: capitalize; + + padding-left: 6px; +} + +svg:not(:root) { + overflow: hidden; +} + +.no-container { + margin: 0 10px !important; } -[id$="_status"].false { - background: #cc1b19 !important; +.no-container + .no-container { + margin-left: 0 !important; } -[id$="_status"].true { - background: #7bcc19 !important; +code ~ .no-container { + vertical-align: bottom !important; +} + +.icon { + position: relative; + top: -2px; + display: inline-block; + width: 1em; + height: 1em; + vertical-align: middle; + line-height: 1; + fill: currentColor; } [id$="token"], [data-option$="Token"], [data-option$="API"] { @@ -238,21 +322,29 @@ button:hover, input[type="button"i]:hover, .button:hover, .checkbox input:checke } em { - color: #cc7b19 !important; + color: #cc7b19; } strike, st, k { text-decoration: line-through !important; } +[href^="#!/NaCl+"], [href^="#!/NaCl+"] *{ + font-family: system-ui, Plex !important; + text-transform: none !important; +} + [href="#!/NaCl+Iw"], [href="#!/NaCl+Iw"] * { cursor: not-allowed !important; opacity: 0.5 !important; } select { + border: 0.1em solid #cc7b19; + border-left-width: 0.4em; + cursor: pointer; margin-left: 10px !important; - font-size: 16px !important; + font-size: 14px !important; line-height: inherit; text-transform: none; } @@ -341,6 +433,16 @@ display summary > .checkbox { display: block; } +summary.button, summary.button:hover { + border-color: #197bcc !important; + width: fit-content; +} + +details[open] > summary.button, summary.button:hover { + background: #fff4 !important; + border-color: #298bdc !important; +} + select:not([multiple]) > option { background: url(noise.png), #3f4245 !important; } @@ -349,9 +451,9 @@ details:last-child > summary { margin-bottom: 0 !important; } -.bar > article > details > summary { - display: list-item; - list-style-type: none; +.bar > article > details > summary, summary.button { + display: list-item !important; + list-style-type: none !important; } .bar > article > details[open] > *:not(summary) { @@ -368,7 +470,7 @@ details:last-child > summary { font-size: 16px; margin-left: 2px; - margin-top: -2px; + margin-top: -4px; position: absolute; } @@ -376,6 +478,15 @@ details:last-child > summary { margin: 6px 12px; } +#save > svg { + position: absolute; + + margin: inherit; + padding: inherit; + + padding-left: 25%; +} + footer { bottom: 0; position: fixed; @@ -437,12 +548,12 @@ footer { } .test { - background: #197bcc !important; + border-color: #197bcc !important; font-family: monospace; } .test:hover { - background: #298bdc !important; + border-color: #298bdc !important; } #sidebar .checkbox:not([special]) { @@ -550,8 +661,9 @@ span.checkbox { background: #cc7b19; } -.checkbox[disabled] { +[disabled] { opacity: 0.25 !important; + transition: opacity 0.5s !important; } .checkbox[disabled] + [using] { @@ -686,6 +798,15 @@ code[new], [code][new], [type^="code"i][new] { color: #efc371 !important; } +#ip-address, #ip-address ~ * { + color: var(--green) !important; +} + +#ip-address:empty::before, #ip-address:empty ~ * { + color: var(--light-red) !important; + content: 'Unknown' !important; +} + #version { background: #0000 !important; border: 1px solid #666 !important; @@ -698,22 +819,62 @@ code[new], [code][new], [type^="code"i][new] { transition: border 0.15s, color 0.15s; } +#version::before { + margin-right: 4px; +} + /* Release is higher than GitHub */ #version[status="high"] { - border-color: #6cc644 !important; - color: #6cc644 !important; + /* Blue */ + border-color: #197bcc !important; + color: #197bcc !important; +} + +#version[status="high"]::before { + /* content: '\25b4'; */ } /* Release is same as GitHub */ #version[status="same"] { - border-color: #197bcc !important; - color: #197bcc !important; + /* Green */ + border-color: #6cc644 !important; + color: #6cc644 !important; +} + +#version[status="same"]::before { + /* content: '\25b8'; */ } /* Release is lower than GitHub */ #version[status="low"] { + /* Orange/Red (#f3582c) */ border-color: #f66a0a !important; - color: #f3582c !important; + color: #f66a0a !important; + /* Grey */ + /* border-color: #949494 !important; + color: #949494 !important; */ +} + +#version[status="low"]::before { + /* content: '\25be'; */ +} + +.maybe, [disabled].maybe, [disabled] .maybe, [disabled] .maybe path { + border-color: #888 !important; + color: #888 !important; + outline: #888 !important; +} + +.false, [disabled].false, [disabled] .false, [disabled] .false path { + border-color: #f53 !important; + color: #f53 !important; + outline: #f53 !important; +} + +.true, [disabled].true, [disabled] .true, [disabled] .true path { + border-color: #6b5 !important; + color: #6b5 !important; + outline: #6b5 !important; } /* notifications */ @@ -906,7 +1067,6 @@ code[new], [code][new], [type^="code"i][new] { background: #ffffff4d !important; } - *::-webkit-scrollbar { width: 10px; } @@ -928,7 +1088,7 @@ code[new], [code][new], [type^="code"i][new] { } *::-webkit-input-placeholder { - color: #999; + color: #999 !important; } @keyframes spin { diff --git a/opa/options.html b/opa/options.html index 206c530..ece70ab 100644 --- a/opa/options.html +++ b/opa/options.html @@ -41,61 +41,62 @@

    Login Settings

    - Use your Plex token - -
    + Use your Plex token + +
    How to find your Plex token. -
    +

    — OR —

    - Login with Plex - - + Login with Plex + +
    Your Plex username and password are never stored, only your Plex token. -
    - Your username and password are used in order to get a token from Plex itself. +
    +
    + Your username and password are used in order to get a token from Plex.

    — OR —

    - Attach to Ombi* - - + Attach to Ombi* + +
    -

    Fill in Manager Settings with Ombi?

    +

    Would you like to use Ombi to fill in your Manager Settings?

    - +
    - Your Ombi Plex token will be shared by this extension. + Ombi's Plex token will be shared by this extension.


    - +
    - +
    - Advance + Advanced

    Plex Server Options

    - +
    Use this to communicate directly with your Plex server.
    Such as http://localhost:32400/ or http://192.168.1.100:32400/ @@ -111,14 +112,14 @@

    Plex Server Options

    Ombi (Movies/TV Shows) - +

    Connection Settings

    - +
    Such as https://example.com/ombi or http://192.168.1.100:5000
    @@ -126,35 +127,38 @@

    Connection Settings

    - +
    - 1. Go to Ombi | Settings | Ombi
    - 2. Copy/Paste the "API Key."
    - Such as aa756d33242f6g8ffbca2b3963586f21 + 1. Go to OmbiSettingsOmbi
    + 2. Copy/Paste the API Key
    + — Such as aa756d33242f6a8ffbca2b3963586f21
    - +


    - +
    @@ -163,14 +167,14 @@

    Login (saved)

    Watcher (Movies) - +

    Connection Settings

    - +
    Such as https://example.com/watcher or http://192.168.1.100:9090
    @@ -178,63 +182,68 @@

    Connection Settings

    - +
    - 1. Go to Watcher | Settings | Server
    - 2. Copy/Paste the "API Key."
    - Such as aa756d33242f6g8ffbca2b3963586f21 + 1. Go to WatcherSettingsServer
    + 2. Copy/Paste the API Key
    + — Such as aa756d33242f6a8ffbca2b3963586f21
    -

    Login (saved)

    +

    Login (saved)

    The default username is watcher. The default password is rehctaw.

    - +
    Only use this if you setup a Watcher username.
    - +
    Only use this if you setup a Watcher password.
    Your password will be hidden once saved.

    -
    This information will be used for Basic Access Authentication only. This will allow the extension to ask Watcher for your list of films, or to add to your list of films.
    +
    + This information will be used for Basic Access Authentication only.
    + This will allow the extension to ask Watcher for your list of films, or to add to your list of films. +
    - +
    - +
    - +


    - +
    Radarr (Movies) - +

    Connection Settings

    - +
    Such as https://example.com/radarr or http://192.168.1.100:7878
    @@ -242,37 +251,40 @@

    Connection Settings

    - +
    - 1. Go to Radarr | Settings | General
    - 2. Click on "Show advance," then copy/paste the "API Key."
    - Such as aa756d33242f6g8ffbca2b3963586f21 + 1. Go to RadarrSettingsGeneral
    + 2. Click on Show Advanced then copy/paste the API Key
    + — Such as aa756d33242f6a8ffbca2b3963586f21
    -

    Login (saved)

    +

    Login (saved)

    - +
    Only use this if you setup a Radarr username.
    - +
    Only use this if you setup a Radarr password.
    Your password will be hidden once saved.

    -
    This information will be used for Basic Access Authentication only. This will allow the extension to ask Radarr for your list of films, or to add to your list of films.
    +
    + This information will be used for Basic Access Authentication only.
    + This will allow the extension to ask Radarr for your list of films, or to add to your list of films. +
    - +
    - +
    This should be the same path (verbatim) used in Radarr. @@ -280,27 +292,27 @@

    Login (saved)

    - +


    - +
    CouchPotato (Movies) - +

    Connection Settings

    - +
    Such as https://example.com/couchpotato or http://192.168.1.100:5050
    @@ -308,38 +320,41 @@

    Connection Settings

    - +
    - 1. Go to CouchPotato | Settings
    - 2. Copy/Paste the "API Key."
    - Such as aa756d33242f6g8ffbca2b3963586f21 + 1. Go to CouchPotatoSettings
    + 2. Copy/Paste the API Key
    + — Such as aa756d33242f6a8ffbca2b3963586f21
    -

    Login (saved)

    +

    Login (saved)

    - +
    Only use this if you setup a CouchPotato username.
    - +
    Only use this if you setup a CouchPotato password.
    Your password will be hidden once saved.
    -
    This information will be used for Basic Access Authentication only. This will allow the extension to ask Couchpotato for your list of films, or to add to your list of films.
    +
    + This information will be used for Basic Access Authentication only.
    + This will allow the extension to ask Couchpotato for your list of films, or to add to your list of films. +
    @@ -348,7 +363,7 @@

    Login (saved)



    - +
    @@ -357,14 +372,14 @@

    Login (saved)

    Medusa (TV Shows) - +

    Connection Settings

    - +
    Such as https://example.com/medusa or http://192.168.1.100:8081
    @@ -372,35 +387,38 @@

    Connection Settings

    - +
    - 1. Go to Medusa | Settings | General | Interface | Web Interface
    - 2. Copy/Paste the "API key."
    - Such as aa756d33242f6g8ffbca2b3963586f21 + 1. Go to MedusaSettingsGeneralInterfaceWeb Interface
    + 2. Copy/Paste the API key
    + — Such as aa756d33242f6a8ffbca2b3963586f21
    -

    Login (saved)

    +

    Login (saved)

    - +
    - +
    Your password will be hidden once saved.

    -
    This information will be used for Basic Access Authentication only. This will allow the extension to ask Medusa for your list of TV shows, or to add to your list of TV shows.
    +
    + This information will be used for Basic Access Authentication only.
    + This will allow the extension to ask Medusa for your list of TV shows, or to add to your list of TV shows. +
    - +
    - +
    This should be the same path (verbatim) used in Medusa. @@ -408,33 +426,33 @@

    Login (saved)

    - - +


    - +
    Sonarr (TV Shows) - +

    Connection Settings

    - +
    Such as https://example.com/sonarr or http://192.168.1.100:8989
    @@ -442,37 +460,40 @@

    Connection Settings

    - +
    - 1. Go to Sonarr | Settings | General
    - 2. Click on "Show advance," then copy/paste the "API Key."
    - Such as aa756d33242f6g8ffbca2b3963586f21 + 1. Go to SonarrSettingsGeneral
    + 2. Click on Show Advanced then copy/paste the API Key
    + — Such as aa756d33242f6a8ffbca2b3963586f21
    -

    Login (saved)

    +

    Login (saved)

    - +
    Only use this if you setup a Sonarr username.
    - +
    Only use this if you setup a Sonarr password.
    Your password will be hidden once saved.

    -
    This information will be used for Basic Access Authentication only. This will allow the extension to ask Sonarr for your list of TV shows, or to add to your list of TV shows.
    +
    + This information will be used for Basic Access Authentication only.
    + This will allow the extension to ask Sonarr for your list of TV shows, or to add to your list of TV shows. +
    - +
    - +
    This should be the same path (verbatim) used in Sonarr. @@ -480,27 +501,27 @@

    Login (saved)

    - +


    - +
    Sick Beard (TV Shows) - +

    Connection Settings

    - +
    Such as https://example.com/sickBeard or http://192.168.1.100:8081
    @@ -508,37 +529,40 @@

    Connection Settings

    - +
    - 1. Go to Sick Beard | Config | General | API
    - — a. Ensure the checkbox "Enable API" is enabled
    - — b. Press the "Generate" button
    - 2. Copy/Paste the "API Key."
    - Such as aa756d33242f6g8ffbca2b3963586f21 + 1. Go to Sick BeardConfigGeneralAPI
    + — a. Ensure the checkbox Enable API is checked
    + — b. Press the Generate button
    + 2. Copy/Paste the API Key
    + — Such as aa756d33242f6a8ffbca2b3963586f21
    -

    Login (saved)

    +

    Login (saved)

    - +
    - +
    Your password will be hidden once saved.

    -
    This information will be used for Basic Access Authentication only. This will allow the extension to ask Sick Beard for your list of TV shows, or to add to your list of TV shows.
    +
    + This information will be used for Basic Access Authentication only.
    + This will allow the extension to ask Sick Beard for your list of TV shows, or to add to your list of TV shows. +
    - +
    - +
    This should be the same path (verbatim) used in Sick Beard. @@ -546,13 +570,13 @@

    Login (saved)

    - - + + +

    +
    + — a minimalist, rectangular button (top center of page) +
    +
    + — a normal, circular button (bottom left or right of page) +
    - -->
    - Which direction should the button open ( only)? + Which direction should the master button open (◯ only)?
    - - + +
    - How transparent should the button be when hidden? + How transparent should the master button be when hidden? +
    + + +
    +
    +

    The Minion Button(s)

    +
    + Allow the use of custom (minion) buttons?
    - - + + + + +
@@ -609,14 +649,14 @@

The Button

Experimental Sites -
+


Default Sites -
+

@@ -635,18 +675,18 @@

Proxy Settings

Force Secure Connections - +

- If enabled, all insecure (HTTP) requests will be through an HTTPS proxy. + If enabled, all requests sent by Web to Plex will be proxied.

Proxy URL & Syntax

- +
Please provide the URL of your proxy.
@@ -662,9 +702,9 @@

Proxy URL & Syntax

Proxy Headers

-
+
- If your proxy requires special headers, enter that information in here. + If your proxy requires special headers, enter that information in here.
  • @{raw-url} OR @{url} — the raw, uneditied URL
  • @@ -675,11 +715,23 @@

    Proxy Headers

+
+ +
    +
  • + IP Address + + + +
  • +
+
+

Native Proxy Settings

@@ -693,20 +745,20 @@

Native Proxy Settings

Auto Grab - +

- When the user presses the Grab button, the extension should: + When you press the Grab button, the extension should:
    -
  • Grab ALL: Find items not on Plex, and grab them
  • -
  • ASK user: Find items not on Plex, and grab what the user approves
  • +
  • Grab ALL — Find items not on Plex, and grab them
  • +
  • ASK you — Find items not on Plex, and grab what you approve

Maximum Auto Grabs

- - + +
How many items can be automatically handled before requiring permission to continue?
@@ -716,12 +768,12 @@

Maximum Auto Grabs

Prompt for Save Location - +

- When the user presses the Grab button should the save location be asked for? + When you press the Grab button, should the save location be asked for?
Only supports Medusa, Radarr, and Sonarr.
@@ -731,12 +783,12 @@

Prompt for Quality - +

- When the user presses the Grab button should the quality be asked for? + When you press the Grab button, should the quality be asked for?
Only supports Medusa, Radarr, and Sonarr.
@@ -750,23 +802,23 @@

Ignore Found Items - +

- When the user presses the Grab button and an item already exists, should the notification be ignored or not? + When you press the Grab button and the item already exists, should the notification be ignored or not?

Ignore Repetitive Notifications - +

- When the user presses the Grab button and there are several incoming notifications of similar information, should the notifications be ignored (after the first one) or not? + When you press the Grab button and there are several incoming notifications of similar information, should the notifications be ignored (after the first one) or not?
@@ -778,14 +830,14 @@

Loose Searching - +


- - + +
Allows the extension to search for non-English titles using pattern matching (as a last resort).
@@ -797,13 +849,13 @@

Manager Searching - +

Allows the extension to use your manager(s) to find media.
- Currently supports: Medusa, Ombi, Radarr, and Sonarr. + Currently supports Medusa, Ombi, Radarr, and Sonarr.

@@ -811,13 +863,13 @@

ID Fetching Mode - +

@@ -825,13 +877,13 @@

- Advance Settings + Advanced Settings BETA

API Keys

- +
You can sign up for an API key, or have OMDb used as a last resort.
@@ -839,21 +891,33 @@

API Keys

- +
You can learn more on how to obtain an API key.

Data Handling

+
+

Settings' Data

+ +
+ +
+ WARNING — This will remove all of your data. +
+ If you don't press Save, this action will be ignored. +
+
+

Data Compression

- +
- WARNING: may cause data loss.
+ WARNING — may cause data loss.
Enabling this option will compress your cached search data using the BWT (transform) and LZW Compression Algorithm.
@@ -888,13 +952,13 @@

Developer Options

Developer Mode - +

- WARNING: may cause data loss.
- Enables developer (debugging) mode, showing advance errors and logging to the console when possible. + WARNING — may cause data loss.
+ Enables developer (debugging) mode, showing Advanced errors and logging to the console when possible.
@@ -907,12 +971,17 @@

External Links

- +
Browse the Web to Plex Wiki for help setting up
+
Browse issues if you're having trouble
+
+ +
+ Download Plex Media Server. +
+
+
@@ -996,9 +1065,10 @@

External Links

+
- ... + ... diff --git a/opa/options.js b/opa/options.js index 0609174..2fa2e81 100644 --- a/opa/options.js +++ b/opa/options.js @@ -14,7 +14,7 @@ if(chrome.runtime.lastError) // FireFox doesn't support sync storage. const storage = (chrome.storage.sync || chrome.storage.local), - $ = top.$ = (selector, all) => (all? [...document.querySelectorAll(selector)]: document.querySelector(selector)), + $ = top.$ = (selector, all = false, container = document) => (all? [...container.querySelectorAll(selector)]: container.querySelector(selector)), $$ = top.$$ = (selector, all) => (all? [...$('display').querySelectorAll(selector)]: $('display').querySelector(selector)), __servers__ = $('[data-option="preferredServer"]'), __sickBeard_qualityProfile__ = $(`[data-option="sickBeardQualityProfileId"]`), @@ -117,7 +117,7 @@ const storage = (chrome.storage.sync || chrome.storage.local), 'ManagerSearch', 'UseLowCache', - // Advance Settings + // Advanced Settings 'OMDbAPI', 'TMDbAPI', 'UseLZW', @@ -158,7 +158,7 @@ const storage = (chrome.storage.sync || chrome.storage.local), 'builtin_imdb', 'builtin_justwatch', 'builtin_letterboxd', - 'builtin_metacritic', + // 'builtin_metacritic', // demoted - 01.18.2020 15:10 MST 'builtin_moviemeter', 'builtin_movieo', 'builtin_netflix', @@ -182,7 +182,7 @@ const storage = (chrome.storage.sync || chrome.storage.local), // Plugins - End of file, "let plugins =" 'plugin_toloka', - 'plugin_shanaproject', + // 'plugin_shanaproject', // promoted - 01.18.2020 15:10 MST 'plugin_myanimelist', 'plugin_myshows', 'plugin_indomovie', @@ -192,9 +192,14 @@ const storage = (chrome.storage.sync || chrome.storage.local), 'plugin_snagfilms', 'plugin_freemoviescinema', 'plugin_foxsearchlight', + 'plugin_metacritic', // Theme Settings - ...(() => [...$('[data-option^="theme:"i]', true)].map(e => e.dataset.option))() + 'UseMinions', + ...(() => [...$('[data-option^="theme:"i]', true)].map(e => e.dataset.option))(), + + // Other Settings + '__defaults', ]; let PlexServers = [], @@ -216,7 +221,23 @@ let __caught = { tvdb: [], }, // The theme classes - __theme = []; + __theme = {}; + +// Icon Markers +let MARKERS = [ + // yes + ``, + // no + ``, + // maybe + `` +]; + +MARKERS.yes = MARKERS[0]; +MARKERS.no = MARKERS[1]; +MARKERS.maybe = MARKERS[2]; + +let RESETTING_SETTINGS = false; // create and/or queue a notification // state = "error" - red @@ -520,6 +541,7 @@ function performPlexLogin({ event }) { s.title = ''; __servers__.innerHTML = ''; __save__.disabled = true; + __save__.innerHTML = 'Save ' + MARKERS.maybe; LoadingAnimation(true); tryPlexLogin(u, p) @@ -536,7 +558,8 @@ function performPlexLogin({ event }) { return performPlexTest({}); } - }); + }) + .catch(error => { LoadingAnimation(); new Notification('error', error); __save__.innerHTML = 'Save ' + MARKERS.no; }); } @@ -546,8 +569,9 @@ function performPlexTest({ ServerID, event }) { inusestatus = [...$('[using="plex"]', true)]; __save__.disabled = true; + __save__.innerHTML = 'Save ' + MARKERS.maybe; __servers__.innerHTML = ''; - teststatus.textContent = '?'; + teststatus.innerHTML = MARKERS.maybe; inusestatus.map(e => e.setAttribute('in-use', false)); LoadingAnimation(true); @@ -555,7 +579,7 @@ function performPlexTest({ ServerID, event }) { LoadingAnimation(); PlexServers = servers; - teststatus.textContent = '!'; + teststatus.innerHTML = MARKERS[+!servers]; inusestatus.map(e => e.setAttribute('in-use', false)); if(!servers) @@ -563,6 +587,7 @@ function performPlexTest({ ServerID, event }) { inusestatus.map(e => e.setAttribute('in-use', true)); __save__.disabled = false; + __save__.innerHTML = 'Save ' + MARKERS.yes; teststatus.classList = true; (servers = [{ sourceTitle: 'GitHub', clientIdentifier: '', name: 'No Server', notice: 'This will not connect to any Plex servers' }, ...servers]).forEach(server => { @@ -578,7 +603,8 @@ function performPlexTest({ ServerID, event }) { if(ServerID) { __servers__.value = ServerID; } - }); + }) + .catch(error => { LoadingAnimation(); new Notification('error', error); teststatus.innerHTML = MARKERS.no; __save__.innerHTML = 'Save ' + MARKERS.no; }); } function getPlexConnections(server) { @@ -610,12 +636,16 @@ function getOptionValues() { } }); - let COM = options.UseLZW; + let COM = options.UseLZW, + DEF = options.__defaults == 'true'; for(let key in __caught) __caught[key] = __caught[key].filter(id => id).slice(0, (COM? 200: 100)).sort(); - __theme = __theme.filter(v => v); + // if(options.__theme) + // __theme = JSON.parse(options.__theme); + // + // __theme = __theme.filter(v => v); let _c = JSON.stringify(__caught), _t = JSON.stringify(__theme); @@ -638,6 +668,7 @@ function performOmbiLogin({ event }) { s.title = ''; __servers__.innerHTML = ''; __save__.disabled = true; + __save__.innerHTML = 'Save ' + MARKERS.maybe; LoadingAnimation(true); let APIURL = `${ l }api/v1/`, @@ -746,12 +777,14 @@ function performOmbiLogin({ event }) { } __save__.disabled = false; + __save__.innerHTML = 'Save ' + MARKERS.yes; } else { /* Plex either doesn't exist, or is disabled */ new Notification('error', 'Error getting Plex details from Ombi'); + __save__.innerHTML = 'Save ' + MARKERS.no; } } ) - .catch( error => { new Notification('error', error); throw error } ); + .catch(error => { LoadingAnimation(); new Notification('error', error); __save__.innerHTML = 'Save ' + MARKERS.no; }); } function performOmbiTest({ refreshing = false, event }) { @@ -763,7 +796,7 @@ function performOmbiTest({ refreshing = false, event }) { enabled = refreshing? $('#using-ombi'): $$('#using-ombi'), inusestatus = [...$('[using="ombi"]', true)]; - teststatus.textContent = '?'; + teststatus.innerHTML = MARKERS.maybe; options.ombiURLRoot = url = path.value = options.ombiURLRoot.replace(/^(\:\d+)/, 'localhost$1').replace(/^(?!^http(s)?:)/, 'http$1://').replace(/\/+$/, ''); inusestatus.map(e => e.setAttribute('in-use', false)); LoadingAnimation(true); @@ -777,38 +810,40 @@ function performOmbiTest({ refreshing = false, event }) { __caught.imdb.push(item.imdbId); __caught.tmdb.push(item.theMovieDbId); }); - }); - fetch(`${ url }/api/v1/Request/tv`) - .then(r => r.json()) - .then(json => { - json.map(item => { - __caught.imdb.push(item.imdbId); - __caught.tvdb.push(item.tvDbId); - }); - }); - - fetch(`${ url }/api/v1/Status`, headers) - .then( response => response.text() ) - .then( status => { - LoadingAnimation(); - if (!status || !status.length) throw new Error('Unable to communicate with Ombi'); - - if ((status = +status) >= 200 && status < 400) { - teststatus.textContent = '!'; - enabled.checked = teststatus.classList = true; - enabled.parentElement.removeAttribute('disabled'); - inusestatus.map(e => e.setAttribute('in-use', true)); - } else { - teststatus.textContent = '!'; - enabled.checked = teststatus.classList = false; - enabled.parentElement.setAttribute('disabled'); - inusestatus.map(e => e.setAttribute('in-use', false)); - - throw new Error(`Ombi error [${ status }]`); - } - } ) - .catch( error => { new Notification('error', error) } ); + fetch(`${ url }/api/v1/Request/tv`) + .then(r => r.json()) + .then(json => { + json.map(item => { + __caught.imdb.push(item.imdbId); + __caught.tvdb.push(item.tvDbId); + }); + }) + .catch(error => { LoadingAnimation(); new Notification('error', error); teststatus.innerHTML = MARKERS.no }); + + fetch(`${ url }/api/v1/Status`, headers) + .then( response => response.text() ) + .then( status => { + LoadingAnimation(); + if (!status || !status.length) throw new Error('Unable to communicate with Ombi'); + + if ((status = +status) >= 200 && status < 400) { + teststatus.innerHTML = MARKERS.yes; + enabled.checked = teststatus.classList = true; + enabled.parentElement.removeAttribute('disabled'); + inusestatus.map(e => e.setAttribute('in-use', true)); + } else { + teststatus.innerHTML = MARKERS.no; + enabled.checked = teststatus.classList = false; + enabled.parentElement.setAttribute('disabled'); + inusestatus.map(e => e.setAttribute('in-use', false)); + + throw new Error(`Ombi error [${ status }]`); + } + }) + .catch(error => { LoadingAnimation(); new Notification('error', error); teststatus.innerHTML = MARKERS.no }); + }) + .catch(error => { LoadingAnimation(); new Notification('error', error); teststatus.innerHTML = MARKERS.no }); } catch(error) { LoadingAnimation(); @@ -858,7 +893,7 @@ function performWatcherTest({ QualityProfileID = 'Default', refreshing = false, inusestatus = [...$('[using="watcher"]', true)]; quality.innerHTML = ''; - teststatus.textContent = '?'; + teststatus.innerHTML = MARKERS.maybe; storagepath.value = '[Empty]'; options.watcherURLRoot = url = path.value = options.watcherURLRoot.replace(/^(\:\d+)/, 'localhost$1').replace(/^(?!^http(s)?:)/, 'http$1://').replace(/\/+$/, ''); inusestatus.map(e => e.setAttribute('in-use', false)); @@ -871,7 +906,8 @@ function performWatcherTest({ QualityProfileID = 'Default', refreshing = false, __caught.imdb.push(item.movies.imdbid); __caught.tmdb.push(item.movies.tmdbid); }); - }); + }) + .catch(error => { LoadingAnimation(); new Notification('error', error); teststatus.innerHTML = MARKERS.no }); getWatcher(options, 'getconfig').then(configuration => { LoadingAnimation(); @@ -890,8 +926,7 @@ function performWatcherTest({ QualityProfileID = 'Default', refreshing = false, name }); - teststatus.textContent = '!'; - teststatus.classList = enabled.checked = !!profiles.length; + teststatus.innerHTML = MARKERS[+!(teststatus.classList = enabled.checked = !!profiles.length)]; inusestatus.map(e => e.setAttribute('in-use', enabled.checked)); if(!profiles.length) @@ -918,11 +953,11 @@ function performWatcherTest({ QualityProfileID = 'Default', refreshing = false, storagepath.value = path || '[Default Location]'; $('[data-option="watcherStoragePaths"i]').value = JSON.stringify(path || { path: '[Default Location]', id: 0 }); - }); + }) + .catch(error => { LoadingAnimation(); new Notification('error', error); teststatus.innerHTML = MARKERS.no }); } catch(error) { LoadingAnimation(); - - throw error; + new Notification('error', error); } } @@ -968,7 +1003,7 @@ function performRadarrTest({ QualityProfileID, StoragePath, refreshing = false, inusestatus = [...$('[using="radarr"]', true)]; quality.innerHTML = ''; - teststatus.textContent = '?'; + teststatus.innerHTML = MARKERS.maybe; storagepath.textContent = ''; options.radarrURLRoot = url = path.value = options.radarrURLRoot.replace(/^(\:\d+)/, 'localhost$1').replace(/^(?!^http(s)?:)/, 'http$1://').replace(/\/+$/, ''); inusestatus.map(e => e.setAttribute('in-use', false)); @@ -981,14 +1016,14 @@ function performRadarrTest({ QualityProfileID, StoragePath, refreshing = false, __caught.imdb.push(movie.imdbId); __caught.tmdb.push(movie.tmdbId); }); - }); + }) + .catch(error => { LoadingAnimation(); new Notification('error', error); teststatus.innerHTML = MARKERS.no }); getRadarr(options, 'profile').then(profiles => { LoadingAnimation(); if(!profiles) return new Notification('error', 'Failed to get Radarr configuration'); - teststatus.textContent = '!'; - teststatus.classList = enabled.checked = !!profiles.length; + teststatus.innerHTML = MARKERS[+!(teststatus.classList = enabled.checked = !!profiles.length)]; inusestatus.map(e => e.setAttribute('in-use', enabled.checked)); if(!profiles.length) @@ -1011,29 +1046,42 @@ function performRadarrTest({ QualityProfileID, StoragePath, refreshing = false, // Because the was reset, the original value is lost. - if(StoragePath) { - storagepath.value = StoragePath; - $('[data-option="__radarrStoragePath"i]').value = StoragePaths.indexOf(StoragePath.replace(/\\/g, '/')) + 1; - } - }); + let paths = []; + storagepaths.forEach(folder => { + let option = document.createElement('option'); + let { id, path } = folder; + + option.value = id; + option.textContent = path; + paths.push({ id, path }); + storagepath.appendChild(option); + }); + + $('[data-option="radarrStoragePaths"i]').value = JSON.stringify(paths); + + // Because the was reset, the original value is lost. if(QualityProfileID) $('[data-option="__sonarrQuality"i]').value = quality.value = QualityProfileID; - }); + }) + .catch(error => { LoadingAnimation(); new Notification('error', error); teststatus.innerHTML = MARKERS.no }); let StoragePaths = []; getSonarr(options, 'rootfolder').then(storagepaths => { - storagepaths.forEach(path => { + LoadingAnimation(); + if(!storagepaths) return new Notification('error', 'Failed to get Sonarr configuration'); + + teststatus.innerHTML = MARKERS[+!(teststatus.classList = enabled.checked = !!storagepaths.length)]; + inusestatus.map(e => e.setAttribute('in-use', enabled.checked)); + + if(!storagepaths.length) + return teststatus.title = 'Failed to communicate with Sonarr'; + enabled.parentElement.removeAttribute('disabled'); + + let paths = []; + storagepaths.forEach(folder => { let option = document.createElement('option'); + let { id, path } = folder; - StoragePaths.push((option.value = option.textContent = path.path).replace(/\\/g, '/')); + option.value = id; + option.textContent = path; + paths.push({ id, path }); storagepath.appendChild(option); }); - $('[data-option="sonarrStoragePaths"i]').value = JSON.stringify(storagepaths); + $('[data-option="sonarrStoragePaths"i]').value = JSON.stringify(paths); // Because the was reset, the original value is lost. if(QualityProfileID) $('[data-option="__medusaQuality"i]').value = quality.value = QualityProfileID; - }); + }) + .catch(error => { LoadingAnimation(); new Notification('error', error); teststatus.innerHTML = MARKERS.no }); let StoragePaths = []; getMedusa(options, 'config').then(configuration => { @@ -1258,11 +1320,11 @@ function performMedusaTest({ QualityProfileID, StoragePath, refreshing = false, $('[data-option="__medusaStoragePath"i]').value = StoragePath; storagepath.selectedIndex = StoragePaths.indexOf(StoragePath.replace(/\\/g, '/').replace(/\/+$/, '')); } - }); + }) + .catch(error => { LoadingAnimation(); new Notification('error', error); teststatus.innerHTML = MARKERS.no }); } catch(error) { LoadingAnimation(); - - throw error; + new Notification('error', error); } }; @@ -1308,7 +1370,7 @@ function performSickBeardTest({ QualityProfileID, StoragePath, refreshing = fals inusestatus = [...$('[using="sickbeard"]', true)]; quality.innerHTML = ''; - teststatus.textContent = '?'; + teststatus.innerHTML = MARKERS.maybe; storagepath.textContent = ''; options.sickBeardURLRoot = url = path.value = options.sickBeardURLRoot.replace(/^(\:\d+)/, 'localhost$1').replace(/^(?!^http(s)?:)/, 'http$1://').replace(/\/+$/, ''); inusestatus.map(e => e.setAttribute('in-use', false)); @@ -1327,7 +1389,8 @@ function performSickBeardTest({ QualityProfileID, StoragePath, refreshing = fals shows.map(show => { __caught.tvdb.push(show.tvdbid); }); - }); + }) + .catch(error => { LoadingAnimation(); new Notification('error', error); teststatus.innerHTML = MARKERS.no }); getSickBeard(options, 'sb.getdefaults').then(configuration => { LoadingAnimation(); @@ -1339,8 +1402,7 @@ function performSickBeardTest({ QualityProfileID, StoragePath, refreshing = fals profiles = qualities[profiles]; - teststatus.textContent = '!'; - teststatus.classList = enabled.checked = !!profiles.length; + teststatus.innerHTML = MARKERS[+!(teststatus.classList = enabled.checked = !!profiles.length)]; inusestatus = [...$('[using="sickbeard"]', true)]; if(!profiles.length) @@ -1362,7 +1424,8 @@ function performSickBeardTest({ QualityProfileID, StoragePath, refreshing = fals // Because the +

@@ -2007,7 +2135,7 @@ for(let index = 0, length = builtin_array.length; builtinElement && index < leng `

${ title }

- +
@@ -2062,7 +2190,6 @@ addListener($('#all-builtin'), 'click', event => { let plugins = { 'Indomovie': ['https://indomovietv.club/', 'https://indomovietv.org/', 'https://indomovietv.net/'], 'Toloka': 'https://toloka.to/', - 'Shana Project': 'https://www.shanaproject.com/', 'My Anime List': 'https://myanimelist.net/', 'My Shows': 'https://myshows.me/', 'Redbox': 'https://www.redbox.com/', @@ -2071,6 +2198,7 @@ let plugins = { 'SnagFilms': 'http://snagfilms.com/', 'Free Movies Cinema': 'https://freemoviescinema.com/', 'Fox Searchlight': 'http://foxsearchlight.com/', + 'Metacritic': 'https://www.metacritic.com/', // Don't forget to add to the __options__ array! }, plugin_array = [], plugin_sites = {}, pluginElement = $('#plugins'); @@ -2098,7 +2226,7 @@ for(let index = 0, length = plugin_array.length; pluginElement && index < length `

${ title }

- +
@@ -2122,7 +2250,7 @@ for(let index = 0, length = plugin_array.length; pluginElement && index < length `

${ title }

- +
@@ -2236,6 +2364,29 @@ addListener($('#erase_cache'), 'mouseup', event => { saveOptions(); }); +/* Erase ALL Settings */ +addListener($('#reset_settings'), 'mouseup', event => { + let options = JSON.stringify(getOptionValues()); + + $('[default]', true) + .forEach(el => { + let de = el.getAttribute('default'); + + if(el.type == 'checkbox') + el.checked = de === 'true'; + else if('contenteditable' in el.attributes) + el.innerHTML = de || ''; + else + el.value = de || ''; + }); + + Recall.CountEnabledSites(); + + new Notification('update', 'All settings have been reset'); + + RESETTING_SETTINGS = true; +}); + $('[type="range"]', true) .forEach((element, index, array) => { let sibling = element.nextElementSibling, @@ -2260,13 +2411,17 @@ $('.checkbox', true) switch(self.id.toLowerCase()) { /* Update the database when the option is toggled */ case 'use-lzw': - if(!self.checked) + let enabled; + + if(enabled = !self.checked) new Notification('update', 'Compressing data...', 3000, () => new Notification('update', 'Compressed', 3000), false); else new Notification('update', 'Decompressing data...', 3000, () => new Notification('update', 'Decompressed', 3000), false); let options = getOptionValues(); + Recall.ToggleConfigurationAvailability(enabled); + for(let name in options) if(/^__/.test(name)) { if(!self.checked) @@ -2309,26 +2464,27 @@ $('.test', true) $('[data-option^="theme:"i], [data-option^="theme:"i] + label', true) .forEach((element, index, array) => { - addListener(element, 'mouseup', async event => { + let UpdateTheme; + + addListener(element, 'mouseup', UpdateTheme = async event => { let self = traverse(event.target, element => /^theme:/i.test(element.dataset.option), true), R = RegExp; - let [a, b] = self.getAttribute('theme').split(/\s*:\s*/).filter(v => v), + let [a, b] = self.getAttribute('theme').split(/^([^]+):([^]+?)$/).filter(v => v), value = `${self.dataset.option.replace(/^theme:/i, '')}-${b}`; if(/^(get|read|for)$/i.test(a)) - __theme.push(`${ value }=${ self.value }`) + __theme[value] = (self.value == 'true'? true: self.value == 'false'? false: self.value); else if(/^(checkbox)$/i.test(self.type) && (self.checked + '') != a) // backwards; fires late - __theme.push(value); - else if(/^(text|input|button|\B)$/i.test(self.type) && R(self.value + '', 'i').test(a)) - __theme.push(value); + __theme[value] = JSON.parse(a); + else if(/^(text|input|button|\B)$/i.test(self.type) && R(a, 'i').test(self.value)) + __theme[value] = self.value; else - __theme = __theme.filter(v => v != value); - - /* Get rid of repeats */ - // __theme = __theme.join('\u0000').replace(/([\w\-]+\=)([^\u0000]+?)\u0000\1[^\u0000]+?/g, ($0, $1, $2, $$, $_) => $1 + $2); + delete __theme[value]; }); + + setTimeout(() => UpdateTheme({ target: element }), 1000); }); let hold = document.createElement('summary'), @@ -2471,6 +2627,11 @@ $('[href^="#!/"]', true) }; }); +$('[id$="_test_status"]', true) + .forEach(element => { + element.innerHTML = MARKERS.maybe; + }); + // CORS exception: SecurityError // MUST be { window }, never { top } let { hash } = window.location; @@ -2491,7 +2652,7 @@ if(hash.length > 1) /* #!/SETTING[/SUB-SETTING] * #!/radarr - * #!/advance-settings/api-keys + * #!/advanced-settings/api-keys */ case '': break; @@ -2503,7 +2664,7 @@ if(hash.length > 1) /* Functions that require some time */ let Recall = { - '@auto': {}, // run at 100ms, and be recallable + '@auto': {}, // run at 100ms '@0sec': {}, // run at 1ms '@1sec': {}, // run at 1000ms }; @@ -2544,37 +2705,153 @@ Recall['@0sec'].SetVersionInfo = async() => { status; switch(compareVer(remote, local)) { - case 0: - status = 'same'; - verEl.setAttribute('title', `The installed version is the most recent. No update required`); - break; - case -1: status = 'high'; - verEl.setAttribute('title', `The installed version is ahead of GitHub. No update required`); + verEl.setAttribute('title', `The installed version (v${ local }) is ahead of GitHub. No update required`); + break; + + case 0: + status = 'same'; + verEl.setAttribute('title', `The installed version (v${ local }) is the most recent. No update required`); break; case 1: status = 'low'; verEl.href += (DM? '': '/latest'); - verEl.setAttribute('title', `The installed version is behind GitHub. Update available`); + verEl.setAttribute('title', `The installed version (v${ local }) is behind GitHub. Update to v${ remote } available`); break; + + default: + verEl.setAttribute('title', `An error has occured comparing Web to Plex versions ([v${ local }] \u2194 [v${ remote }])`); + verEl.setAttribute('status', 'low'); + verEl.innerHTML = 'ERROR'; + return; } - verEl.innerHTML = `v${ manifest.version }`; + verEl.innerHTML = `v${ local }`; verEl.setAttribute('status', status); } if(DM) - await fetch('https://api.github.com/repos/SpaceK33z/web-to-plex/releases') + await fetch('https://api.github.com/repos/webtoplex/browser-extension/releases') .then(response => response.json()) .then(versions => useVer(versions[0])); else - await fetch('https://api.github.com/repos/SpaceK33z/web-to-plex/releases/latest') + await fetch('https://api.github.com/repos/webtoplex/browser-extension/releases/latest') .then(response => response.json()) .then(version => useVer(version)); }; +/* Get the user's IP address */ +Recall['@auto'].GetIPAddress = async() => { + let self = $('#ip-address'), + teststatus = $('#proxy_test_status'), + options = getOptionValues(); + + + self.innerHTML = 'Loading...'; + teststatus.innerHTML = MARKERS.maybe; + + let proxy = HandleProxySettings(options); + + if(proxy.enabled) { + let { url, headers } = proxy, + tor = 'https://check.torproject.org'; + + headers = HandleProxyHeaders(headers, tor); + + if(/(^https?:\/\/)(?!localhost|127\.0\.0\.1(?:\/8)?|::1(?:\/128)?|:\d+)\b/i.test(url)) { + url = url + .replace(/\{b(ase-?)?64-url\}/gi, btoa(tor)) + .replace(/\{enc(ode)?-url\}/gi, encodeURIComponent(tor)) + .replace(/\{(raw-)?url\}/gi, tor); + } else { + self.innerHTML = ''; + teststatus.innerHTML = MARKERS.no; + + throw new SyntaxError('Unable to proxy: The URL must be a public (HTTPS/HTTP) address'); + } + + await fetch(url, { mode: 'cors', headers }) + .then(results => results.text()) + .then(text => { + let DOM = new DOMParser, + html = DOM.parseFromString(text, 'text/html'), + strong = $('.content strong', false, html), + IPAddress; + + if(strong) + IPAddress = strong.textContent; + else if(/([\d\.]{7,15})/.test(html.body.textContent)) + IPAddress = RegExp.$1; + else + IPAddress = ''; + + self.innerHTML = IPAddress; + teststatus.innerHTML = MARKERS[+!IPAddress]; + }) + .catch(error => { + self.innerHTML = ''; + teststatus.innerHTML = MARKERS.no; + + new Notification('error', error); + }); + } else { + await fetch('https://check.torproject.org', { mode: 'cors' }) + .then(results => results.text()) + .then(text => { + let DOM = new DOMParser, + html = DOM.parseFromString(text, 'text/html'), + strong = $('.content strong', false, html), + IPAddress; + + if(strong) + IPAddress = strong.textContent; + else if(/([\d\.]{7,15})/.test(html.body.textContent)) + IPAddress = RegExp.$1; + else + IPAddress = ''; + + self.innerHTML = IPAddress; + teststatus.innerHTML = MARKERS[+!IPAddress]; + }) + .catch(error => { + self.innerHTML = ''; + teststatus.innerHTML = MARKERS.no; + + new Notification('error', error); + }); + } + + self.setAttribute('notice', `Public${proxy.enabled?' (Proxy)':''}`); +}; + +let ToggleConfigurationAvailabilityListener = false; +/* Setting the Configuration Data disabled state */ +Recall['@1sec'].ToggleConfigurationAvailability = (enabled = null) => { + if(enabled === null) + enabled = $('#use-lzw').checked; + + if(enabled) { + let parent = $('#json_data').parentElement; + + parent.setAttribute('disabled', ''); + + if(!ToggleConfigurationAvailabilityListener) + $('*:not([data-option])', true, parent).forEach(element => { + addListener(element, 'mousedown', event => { + let disabled = traverse(element, () => 'disabled' in element.attributes, false); + + if(disabled) + return event.preventDefault(); + }); + }); + ToggleConfigurationAvailabilityListener = true; + } else { + $('#json_data').parentElement.removeAttribute('disabled'); + } +}; + for(let func in Recall) { if(/^@/.test(func)) { let f; @@ -2593,15 +2870,21 @@ for(let func in Recall) { for(let fn in Recall[func]) { f = Recall[func][fn]; + Recall[fn] = f; setTimeout(f, 1); } break; - case '@1sec': + default: + /^@(\d+)sec$/i.test(func); + + let time = +RegExp.$1; + for(let fn in Recall[func]) { f = Recall[func][fn]; - setTimeout(f, 1000); + Recall[fn] = f; + setTimeout(f, time * 1000); } break; } @@ -2941,3 +3224,5 @@ function restoreSelection({ anchor, focus }, editor, key) { selection.setBaseAndExtent(nodes.anchor, index.anchor, nodes.focus, index.focus); } + +addListener($('#test-proxy-settings'), 'click', Recall.GetIPAddress); diff --git a/opa/plugn.js b/opa/plugn.js index 1c23c60..d1aef56 100644 --- a/opa/plugn.js +++ b/opa/plugn.js @@ -257,7 +257,15 @@ async function prepare({ code, alias, type, allowed, url }) { Type = type.replace(/^\w/, ($0, $$, $_) => $0.toUpperCase()); let org = url.origin, - ali = TLDHost(url.host); + ali = TLDHost(url.host), + runOnInit = ['(null)']; + + let funcs = { + minions: PLUGN_CONFIGURATION.UseMinions, + }; + for(let func in funcs) + runOnInit.push(`(${ funcs[func] } && ${ type }.${ func } instanceof Function? ${ type }.${ func }(): null)`); + runOnInit = runOnInit.join(','); let { authorized, ...A } = await GetAuthorization(alias); @@ -294,7 +302,7 @@ ${ ${ code .replace(/\/\/+\s*"([^\"\n\f\r\v]+?)"\s*requires?\:?\s*(.+)/i, ($0, $1, $2, $$, $_) => - `;(async() => await Require("${ $2 }", "${ alias }", "${ $1 }", "${ instance }"))();` + `;(async() => await Require("cache,${ $2 }", "${ alias }", "${ $1 }", "${ instance }"))();` ) .replace(/\b(chrome|browser)\.storage\.(sync|local|managed)\.?/g, ($0, $1, $2, $$, $_) => `;console.warn("This ${ type } attempted to access <${ $1 }.storage.${ $2 }>; use , , and instead.");` @@ -334,11 +342,17 @@ return ( /* "ready" is a sync (normal) function */ ${ type }.ready() )? - ${ type }.init( ${ Type }ReadyState ): + ( + ${ runOnInit }, + ${ type }.init( ${ Type }ReadyState ) + ): /* Injected ${ type } isn't ready */ (${ type }.timeout || 1000): /* Injected ${ type } doesn't have the "ready" property */ - ${ type }.init(): + ( + ${ runOnInit }, + ${ type }.init() + ): /* Injected ${ type } isn't properly structured */ (console.warn("The ${ type } (${ alias }) is incorrectly structured. Could not find required function ${ type }.init"), -1): /* URL doesn't match pattern */ @@ -358,8 +372,13 @@ let handle = async(results, tabID, instance, script, type) => { results = await results; + let extURL = url => chrome.extension.getURL(url); + /* Always display a pretty button */ - chrome.tabs.insertCSS(tabID, { file: 'common.css' }); + chrome.tabs.insertCSS(tabID, { cssOrigin: 'user', file: 'common.css' }); + chrome.tabs.insertCSS(tabID, { cssOrigin: 'user', file: 'theme.css' }); + chrome.tabs.insertCSS(tabID, { cssOrigin: 'user', file: 'glyphs.css' }); + chrome.tabs.insertCSS(tabID, { cssOrigin: 'user', file: 'colors.css' }); if((!results || !results[0] || !instance) && !FOUND[instance]) try { @@ -483,6 +502,10 @@ let tabchange = async tabs => { chrome.runtime.getURL(`plugin.${ js }.js`): `https://webtoplex.github.io/web/${ type }s/${ js }.js`; + let style = (PLUGN_DEVELOPER)? + chrome.runtime.getURL(`${ js }.css`): + `https://webtoplex.github.io/web/styles/${ js }.css`; + await fetch(file, { mode: 'cors' }) .then(response => response.text()) .then(async code => { @@ -495,6 +518,10 @@ let tabchange = async tabs => { }) .then(() => running.push(id, instance)) .catch(error => { throw error }); + + await fetch(style, { mode: 'cors' }) + .then(response => response.text()) + .then(async code => chrome.tabs.insertCSS({ code })); }; // listen for message event @@ -540,6 +567,10 @@ chrome.runtime.onMessage.addListener(processMessage = async(request = {}, sender chrome.runtime.getURL(`plugin.${ plugin }.js`): `https://webtoplex.github.io/web/${ _type }s/${ options[_type] }.js`; + let style = (PLUGN_DEVELOPER)? + chrome.runtime.getURL(`${ options[_type] }.css`): + `https://webtoplex.github.io/web/styles/${ options[_type] }.css`; + let { authorized, ...A } = await GetAuthorization(options[_type]); try { @@ -602,6 +633,9 @@ chrome.runtime.onMessage.addListener(processMessage = async(request = {}, sender break; case 'GRANT_PERMISSION': + if(!options[_type]) + return false; + await Save(`has/${ options[_type] }`, options.allowed); await Save(`get/${ options[_type] }`, options.permissions); break; @@ -629,6 +663,10 @@ chrome.runtime.onMessage.addListener(processMessage = async(request = {}, sender return false; } + await fetch(style, { mode: 'cors' }) + .then(response => response.text()) + .then(async code => browser.tabs.insertCSS({ code })); + return true; } catch(error) { PLUGN_TERMINAL.error(error); diff --git a/opa/popup.css b/opa/popup.css index 36ee11b..6aa209d 100644 --- a/opa/popup.css +++ b/opa/popup.css @@ -14,8 +14,10 @@ body { font-size: 1.25em !important; flex-grow: 1 !important; padding: 1px !important; - overflow: hidden !important; - position: absolute !important; + /* Disable for Firefox to work properly */ + /* overflow: hidden !important; */ + overflow-x: hidden; + /* position: absolute !important; */ } a { @@ -337,11 +339,11 @@ tbody[header]:before { float: right; } -*::-webkit-scrollbar { +*::-moz-scrollbar { width: 10px; } -*::-webkit-scrollbar-thumb { +*::-moz-scrollbar-thumb { min-height: 50px; background: rgba(255, 255, 255, 0.15); border: 2px solid rgba(0, 0, 0, 0); @@ -349,6 +351,6 @@ tbody[header]:before { background-clip: padding-box; } -*::-webkit-scrollbar-track { +*::-moz-scrollbar-track { background: url(noise.png) repeat, #3f4245 !important; } diff --git a/opa/rottentomatoes.css b/opa/rottentomatoes.css new file mode 100644 index 0000000..a757498 --- /dev/null +++ b/opa/rottentomatoes.css @@ -0,0 +1,43 @@ +.mid-top-actions .share-box { + padding-left: 20px!important; +} + +.web-to-plex-minion { + border: 1px solid #f3f3f3; + border-radius: 22px; + color: #fff!important; + font: inherit; + font-family: 'Franklin Gothic FS Med', Arila, Helvetica, Tahoma, Century, Verdana, sans-serif; + font-size: 16px; + font-weight: 400; + visibility: visible; + + margin-right: 8px; + + height: 44px; +} + +.web-to-plex-minion.wtp--download { + background-color: #f45a26!important; +} + +.web-to-plex-minion.wtp--download:hover { + background-color: #f67e56!important; +} + +.web-to-plex-minion.wtp--found { + background-color: #ca7c1f!important; +} + +.web-to-plex-minion.wtp--found:hover { + background-color: #f8c022!important; +} + +#tt--0-0 { + background: #666!important; +} + +#tt--0-0:hover { + background: #888!important; + border-color: #888!important; +} diff --git a/opa/rottentomatoes.js b/opa/rottentomatoes.js index f06644b..ce71ccd 100644 --- a/opa/rottentomatoes.js +++ b/opa/rottentomatoes.js @@ -1,11 +1,7 @@ let script = { "url": "*://*.rottentomatoes.com/([mt]|browse)/*", - "ready": () => { - let element = $('#reviews').first; - - return !!element; - }, + "ready": () => !$('#reviews').empty, "init": (ready) => { let _title, _year, _image, R = RegExp; @@ -83,4 +79,37 @@ let script = { return { type, title, image }; }, + + "minions": () => { + let actions = $('.franchiseLink, #topSection > *'), + type = script.getType(); + + if(actions.empty || type == 'error') + return; + let element = actions.first; + + if(type == 'movie') { + let minion; + + let parent = furnish('div', { style: 'box-shadow: none' }, + furnish('div.wts-button__container', {}, + minion = furnish('button.web-to-plex-minion.button--wts', { style: 'margin-bottom: 25px' }, + ' Web to Plex' + ) + ) + ); + + addMinions(minion); + element.appendChild(minion); + } else { + let minion; + + let parent = furnish('div.poster_button.hidden-xs', {}, + minion = furnish('a.web-to-plex-minion.fullWidth', {}, 'Web to Plex') + ); + + addMinions(minion); + element.appendChild(parent); + } + }, }; diff --git a/opa/theme.css b/opa/theme.css index 75130c8..5d983c2 100644 --- a/opa/theme.css +++ b/opa/theme.css @@ -23,11 +23,11 @@ BUTTON [style].web-to-plex-button... LI [tooltip]#wtp-list-name.list-name // The actionable anchor A [tooltip][href][`id`].list-action - IMG [src=/img/48.png] // Web to Plex logo + IMG [src=/48.png] // Web to Plex logo // The Plex It! button LI [tooltip]#wtp-plexit.list-item - IMG [src=/img/plexit.48.png] // Alarm bell + IMG [src=/plexit.48.png] // Alarm bell // The Add to Plex It! button (normally hidden, until Plex It! is visible) LI [tooltip][data]#plexit.list-item @@ -35,22 +35,22 @@ BUTTON [style].web-to-plex-button... // The hide button LI [tooltip]#wtp-hide.list-item - IMG [src=/img/hide.48.png] // Eye icon + IMG [src=/hide.48.png] // Eye icon // The refresh button LI [tooltip]#wtp-refresh.list-item - IMG [src=/img/reload.48.png] // Refresh + IMG [src=/reload.48.png] // Refresh // The settings button LI [tooltip]#wtp-options.list-item - IMG [src=/img/settings.48.png] // Gear icon + IMG [src=/settings.48.png] // Gear icon // The small, circular, colored indicator ::after */ -/* Button location */ -.web-to-plex-button.button-location-right { +/* Button location (Circular) */ +.web-to-plex-button.button-location-right:not(.button-shape-box) { left: unset !important; right: 5px !important; } @@ -68,7 +68,72 @@ BUTTON [style].web-to-plex-button... opacity: 0.10 !important; } -/* Button shape */ -.web-to-plex-button.button-shape-box { - /* ... */ +/* Button shape (Box) */ +.web-to-plex-button.button-shape-box.animate::before { + border-radius: 75px !important; +} + +.web-to-plex-button.button-shape-box, #plexit-bookmarklet-frame ~ .web-to-plex-button.button-shape-box { + border-radius: 0 !important; + border-bottom-left-radius: 2px !important; + border-bottom-right-radius: 2px !important; + + bottom: 0 !important; + margin-left: calc(50% - 34.5px) !important; + margin-top: -3px !important; + padding: 0 !important; + top: 0 !important; + + height: 20px !important; + width: 75px !important; +} + +.web-to-plex-button.button-shape-box .list-action img { + height: 16px !important; + width: 16px !important; +} + +.web-to-plex-button.button-shape-box:not(:hover) { + margin-top: -20px !important; +} + +.web-to-plex-button.button-shape-box::after { + border: 0 !important; + border-radius: 0 !important; + border-bottom: inherit !important; + + top: 100% !important; + + height: 2px !important; + width: 100% !important; +} + +.web-to-plex-button.button-shape-box:not(:hover)::after { + height: 6px !important; +} + +.web-to-plex-button.button-shape-box #wtp-list-name { + padding: 0 !important; + margin-top: 0 !important; + + width: 75px !important; +} + +.web-to-plex-button.button-shape-box #wtp-list-name img[src$="16.png"] { + height: 12px !important; +} + +.web-to-plex-button.button-shape-box:not(:hover) #wtp-list-name ~ * { + display: none !important; +} + +.web-to-plex-button.button-shape-box.open li:hover [tooltip]::before, .web-to-plex-button.button-shape-box.open [tooltip]::before { + background: #000c !important; + + left: -115px !important; + top: 30px !important; + z-index: 999999999 !important; + + height: fit-content !important; + width: 300px !important; } diff --git a/opa/tmdb.css b/opa/tmdb.css new file mode 100644 index 0000000..f6e5cef --- /dev/null +++ b/opa/tmdb.css @@ -0,0 +1,34 @@ +.web-to-plex-minion:hover, .web-to-plex-minion.wtp--downloader:hover, .web-to-plex-minion.wtp--found:hover { + cursor: pointer!important; + transition: background 0.2s, border 0.2s; +} + +.web-to-plex-minion, .web-to-plex-minion.wtp--downloader { + border-color: #f45a26!important; + background: #f45a26!important; + color: #fff!important; +} + +.web-to-plex-minion.wtp--downloader:hover { + border-color: #f67e56!important; + background: #f67e56!important; +} + +.web-to-plex-minion.wtp--found { + background: #e5a00d!important; + border-color: #e5a00d!important; + color: #fff!important; +} + +.web-to-plex-minion.wtp--found:hover { + background: #f9be03!important; +} + +#tt--0-0 { + background: #666!important; +} + +#tt--0-0:hover { + background: #888!important; + border-color: #888!important; +} diff --git a/opa/tmdb.js b/opa/tmdb.js index 5a98c24..27414d6 100644 --- a/opa/tmdb.js +++ b/opa/tmdb.js @@ -76,4 +76,22 @@ let script = { return { type, title, year, image, TMDbID }; }, + + "minions": () => { + let actions = $('.header .actions'); + + if(actions.empty) + return; + + actions.forEach(element => { + let minion; + + let parent = furnish('li.tooltip.use_tooltip', { title: 'Web to Plex' }, + minion = furnish('a.web-to-plex-minion', { style: `background: url("${ IMAGES.icon_32 }") center/50% no-repeat !important` }) + ); + + addMinions(minion); + element.insertBefore(parent, element.lastElementChild); + }); + }, }; diff --git a/opa/trakt.css b/opa/trakt.css new file mode 100644 index 0000000..5c11d0f --- /dev/null +++ b/opa/trakt.css @@ -0,0 +1,22 @@ +.web-to-plex-minion { + background-color: #f45a26!important; + border-color: #f45a26!important; + color: #fff!important; +} + +.web-to-plex-minion:hover { + background-color: #d43a06!important; +} + +.wtp-min.under-info[title]::after { + content: attr(title); +} + +#tt--0-0 { + color: #666!important; + text-decoration: line-through!important; +} + +#tt--0-0:hover { + color: #888!important; +} diff --git a/opa/trakt.js b/opa/trakt.js index 77c531d..71cb111 100644 --- a/opa/trakt.js +++ b/opa/trakt.js @@ -101,4 +101,42 @@ let script = { return { type, title, year }; }, + + "minions": () => { + let actions = $('#info-wrapper .action-buttons'); + + if(actions.empty) + return; + + actions.forEach(element => { + let subtitle; + + let minion = furnish('a.web-to-plex-minion.btn.btn-block.btn-summary.selected', {}, + furnish('i.fa.fa-fw.fa-download'), + furnish('div.text', {}, + furnish('div.main-info', {}, 'Web to Plex'), + subtitle = furnish('div.wtp-min.under-info', { + title: 'Loading...', + onmouseenter: event => { + let self = event.target, + title = self.getAttribute('title'); + + self.setAttribute('title', ''); + self.innerHTML = title; + }, + onmouseleave: event => { + let self = event.target, + title = self.innerHTML; + + self.setAttribute('title', title); + self.innerHTML = ''; + }, + }) + ) + ); + + addMinions(minion, subtitle); + element.insertBefore(minion, element.childNodes[3]); + }); + }, }; diff --git a/opa/tvmaze.css b/opa/tvmaze.css new file mode 100644 index 0000000..d503832 --- /dev/null +++ b/opa/tvmaze.css @@ -0,0 +1,27 @@ +.web-to-plex-minion { + background: #727272; + color: #fff!important; + text-decoration: none!important; + text-transform: none!important; +} + +.web-to-plex-minion.wtp--download { + background: #f45a26!important; +} + +.web-to-plex-wrapper:hover > .web-to-plex-minion.wtp--download, .web-to-plex-minion.wtp--download:hover { + background-color: #f67e56!important; + color: #ffffff!important; +} + +.web-to-plex-wrapper:hover > .web-to-plex-minion.wtp--found, .web-to-plex-minion.wtp--found:hover { + background-color: #f8c022!important; +} + +#tt--0-0 { + background: #666!important; +} + +#tt--0-0:hover { + background: #888!important; +} diff --git a/opa/tvmaze.js b/opa/tvmaze.js index 3332237..8a690d4 100644 --- a/opa/tvmaze.js +++ b/opa/tvmaze.js @@ -24,4 +24,28 @@ let script = { return pathname.replace(/\/shows\/(\d+).*/, '$1'); }, + + "minions": () => { + let actions = $('nav.page-subnav > ul'); + + if(actions.empty) + return; + + actions.forEach(element => { + let li = /^[ou]l$/i.test(element.tagName); + + let parent = furnish('li.web-to-plex-wrapper', {}), + minion = furnish(`a.web-to-plex-minion.${ li? 'flatButton': 'roundButton' }`, {}, 'Web to Plex'); + + if(li) { + parent.appendChild(minion); + element.appendChild(parent); + } else { + element.appendChild(minion); + } + + addMinions(minion); + }); + + }, }; diff --git a/opa/utils.js b/opa/utils.js index c6b649e..1a19cec 100644 --- a/opa/utils.js +++ b/opa/utils.js @@ -1,9 +1,105 @@ /* eslint-disable no-unused-vars */ /* global configuration, init, Update, "Helpers" */ -let configuration, init, Update; +let configuration, init, Update, IMAGES, Glyphs = {}, + HELPERS_STORAGE = { + get(keys, callback = () => {}) { + let results; + + if(keys === null) { + return callback(configuration); + } else if(keys instanceof String) { + return callback(configuration[keys]); + } else if(keys instanceof Array) { + results = [...keys]; + + for(let key of keys) + result.push(configuration[key]); + return callback(results); + } else if(keys instanceof Object) { + results = { ...keys }; + + for(let key in keys) + results[key] = configuration[key]; + return callback(results); + } + }, + + set(keys, callback = () => {}) { + let results = {}; + + for(let key in keys) + configuration[key] = results[key] = keys[key]; + return callback(results); + }, + + remove(keys, callback = () => {}) { + if(keys === null) + for(let key in configuration) + delete configuration[key]; + else if(keys instanceof String) + delete configuration[key]; + else if(keys instanceof Array) + for(let key of keys) + delete configuration[key]; + else if(keys instanceof Object) + for(let key in keys) + delete configuration[key]; + + callback(); + } + }, + MINIONS = [], + addMinions = (...minions) => { + MINIONS = [...minions, ...MINIONS]; + + return { + stayUnique: status => { + MINIONS = MINIONS.map(minion => ((!!~minions.indexOf(minion)? minion.setAttribute('ignore-web-to-plex-updates', status): null), minion)) + } + } + }; + +class UUID { + constructor(length = 16, symbol = '-') { + let values = []; + + window.crypto.getRandomValues(new Uint32Array(length)).forEach(value => values.push(value.toString(36))); + + return values.join(symbol).replace(/^[^a-z]+/i, ''); + } + + static from(object, seed = 64, symbol = '-') { + let id = []; + + for(let key in object) { + let o = object[key]; + + if(o instanceof Array) + o = o.join(symbol); + else if(o instanceof Object) + o = Object.values(o).join(symbol); + else + o = o + ''; + + if(typeof(o) == 'string') + o = o + .toLowerCase() + .split('') + .reduce((a, b) => ((typeof(a) == 'number'? a: a.charCodeAt(0)) + b.charCodeAt(0)), seed) + .toString(36); + else + return + /* Error occurred */; + + id.push(o); + } -(async date => { + return id.join('').replace(/(\w{1,8})(\w{4})?(\w{4})?(\w{4})?(\w{12})?(\w+)?/, '$1-$2-$3-$4-$5:$6').replace(/\-+\:/g, ''); + } +} + +let INITIALIZE = (async date => { // default date items let YEAR = date.getFullYear(), @@ -14,7 +110,38 @@ let configuration, init, Update; RUNNING = false, // Other items /* Items that the user has already asked for */ - CAUGHT, COMPRESS; + CAUGHT, COMPRESS, + REFINED = {}; + + // update ALL minions + let updateMinions = (attributes) => { + let { title, href, text, hover, classes, event } = attributes; + + for(let minion of MINIONS.filter(minion => minion.getAttribute('ignore-web-to-plex-updates') != 'true')) { + classes.forEach(c => minion.classList.add(c)); + minion.setAttribute('title', hover); + minion.addEventListener('click', event? event: href? (() => top.open(href, '_top')): null); + } + }, + // update a single (ID) minion + updateMinion = (properties, status, options) => { + let minions; + + for(let property in properties) + if(!(minions = $(`.web-to-plex-minion[${ property }="${properties[property]}"], .web-to-plex-minion[${ property }id="${properties[property]}"], .web-to-plex-minion[${ property }-id="${properties[property]}"]`)).empty) + break; + + if(!minions || minions.empty) + return; + + minions.forEach(minion => status? minion.classList.add(`wtp--${status}`): ''); + + if(status == 'found' && options.key) + minions.forEach(minion => { + minion.setAttribute('href', Request_PlexURL(__CONFIG__.server.id, options.key)); + minion.setAttribute('title', `Watch "${options.title} (${options.year})" on Plex`); + }); + }; // simple helpers let extURL = url => chrome.extension.getURL(url), @@ -22,9 +149,10 @@ let configuration, init, Update; // DO NOT EXPOSE __CONFIG__, ALLOWED, PERMISS; - let IMG_URL = { + let IMG_URL = IMAGES = { 'nil': extURL('null.png'), 'icon_16': extURL('16.png'), + 'icon_32': extURL('32.png'), 'icon_48': extURL('48.png'), 'background': extURL('background.png'), 'hide_icon_16': extURL('hide.16.png'), @@ -34,6 +162,7 @@ let configuration, init, Update; 'close_icon_16': extURL('close.16.png'), 'close_icon_48': extURL('close.48.png'), 'icon_white_16': extURL('w16.png'), + 'icon_white_32': extURL('w32.png'), 'icon_white_48': extURL('w48.png'), 'plexit_icon_16': extURL('plexit.16.png'), 'plexit_icon_48': extURL('plexit.48.png'), @@ -46,6 +175,15 @@ let configuration, init, Update; 'settings_icon_48': extURL('settings.48.png'), }; + for(let glyph of "adjust,airplane,alarm,albums,amazon,anchor,android,apple,asterisk,ax,badoo,ban,bank,barcode,baseball,basketball,bathrobe,beer,behance,bell,bicycle,bin,binoculars,blacksmith,blog,blogger,bluetooth,boat,bold,bomb,book,bookmark,bowling,briefcase,brush,bug,building,bullets,bullhorn,buoy,bus,cake,calculator,calendar,camera,candle,car,cardio,cargo,cars,celebration,certificate,charts,chat,check,cleaning,clock,cloud,cogwheel,cogwheels,coins,collapse,comments,compass,compressed,conversation,crop,crown,cup,cutlery,dashboard,delete,deviantart,direction,dislikes,display,divide,dog,download,dress,dribbble,drink,dropbox,dumbbell,earphone,edit,eject,electricity,embed,envelope,euro,evernote,exit,expand,eyedropper,fabric,facebook,factory,fax,female,file,film,filter,fins,fire,fishes,flag,flash,flickr,flower,font,forrst,forward,foursquare,fullscreen,gamepad,gbp,gift,girl,github,glass,global,globe,golf,goodreads,google_plus,grater,group,hdd,header,headphones,headset,heart,heat,history,hockey,home,hospital,imac,inbox,instagram,instapaper,ios,ipad,iphone,ipod,italic,jolicloud,justify,kettle,keynote,keys,kiosk,last_fm,leaf,leather,lightbulb,link,linked_in,list,lock,luggage,macbook,magic,magnet,male,microphone,minus,money,moon,more,move,music,mute,myspace,nails,nameplate,note,notes,ok,package,pants,paperclip,parents,pause,pen,pencil,piano,picasa,picture,pin,pinboard,pinterest,pipe,pizza,play,playlist,playstation,plus,podium,pool,posterous_spaces,pot,power,print,projector,pushpin,qrcode,quora,rabbit,radar,random,read_it_later,readability,record,redo,refresh,remove,repeat,restart,retweet,rewind,riflescope,ring,road,roundabout,router,rss,rugby,ruller,sampler,scissors,screenshot,search,send,server,settings,share,shield,shirt,shop,signal,skateboard,skitch,skull,skype,smoking,snowflake,sort,sorting,spade,spotify,spray,star,stats,stop,stopwatch,stroller,stumbleupon,subtitles,suitcase,sun,sweater,table,tablet,tag,tags,tie,tint,tower,train,transfer,translate,truck,tumblr,turtle,twitter,umbrella,unchecked,underwear,undo,unlock,unshare,upload,usd,user,vases,vcard,vimeo,vine,wallet,webcam,wifi,windows,woman,wordpress,wrench,xbox,xing,yahoo,yelp,youtube,zootool".split(',')) + Object.defineProperty(Glyphs, glyph, { + get() { return document.furnish('i', { glyph }) }, + set(value) { return document.furnish('i', { glyph: value }) }, + + configurable: true, + enumerable: true, + }); + // the storage - priority to sync const UTILS_STORAGE = chrome.storage.sync || chrome.storage.local; @@ -64,7 +202,7 @@ let configuration, init, Update; UTILS_STORAGE.get(null, DISK => { if(chrome.runtime.lastError) - chrome.storage.local.get(null, LOAD); + UTILS_STORAGE.get(null, LOAD); else LOAD(DISK); }); @@ -302,7 +440,7 @@ let configuration, init, Update; ); let preX = document.queryBy('.web-to-plex-prompt').first, - movie = /^(m(?:ovies?)?|f(?:ilms?)?|c(?:inemas?)?)?$/i; + movie = /^(m(?:ovies?)?|f(?:ilms?)?|c(?:inemas?)?|theat[re]{2})?$/i; if(preX) return /* Ignore while another prompt is open, prevents double prompts */; @@ -329,7 +467,7 @@ let configuration, init, Update; header.innerText = 'Approve ' + counter.children.length + (counter.children.length == 1?' item': ' items'); }; - prompt = furnish('div.web-to-plex-prompt', {}, + prompt = furnish('div.web-to-plex-prompt', { type: prompt_type }, furnish('div.web-to-plex-prompt-body', { style: `background-image: url(${ IMG_URL.noise_background }), url(${ IMG_URL.background }); background-size: auto, cover;` }, // The prompt's title furnish('h1.web-to-plex-prompt-header', {}, 'Approve ' + array.length + (array.length == 1? ' item': ' items')), @@ -346,14 +484,14 @@ let configuration, init, Update; elements.push( furnish('li.web-to-plex-prompt-option.mutable', { value: index, innerHTML: `

${ index + 1 }. ${ title }${ year? ` (${ year })`: '' } \u2014 ${ type }

` }, - furnish('button.remove', { title: `Remove "${ title }"`, onmouseup: event => { remove(event.target.parentElement); event.target.remove() } }), + furnish('i[glyph=remove]', { title: `Remove "${ title }"`, onmouseup: event => { remove(event.target.parentElement); event.target.remove() } }), ( __CONFIG__.PromptQuality? - P_QUA = furnish('select.quality', { index, onchange: event => data[event.target.getAttribute('index')].quality = event.target.value }, ...profiles[/(movie|film|cinema)/i.test(type)?'movie':'show'].map(Q => furnish('option', { value: Q.id }, Q.name))): + P_QUA = furnish('select.quality', { index, onchange: event => data[event.target.getAttribute('index')].quality = event.target.value }, ...profiles[movie.test(type)?'movie':'show'].map(Q => furnish('option', { value: Q.id }, Q.name))): '' ),( __CONFIG__.PromptLocation? - P_LOC = furnish('select.location', { index, onchange: event => data[event.target.getAttribute('index')].location = event.target.value }, ...locations[/(movie|film|cinema)/i.test(type)?'movie':'show'].map(Q => furnish('option', { value: Q.id }, Q.path))): + P_LOC = furnish('select.location', { index, onchange: event => data[event.target.getAttribute('index')].location = event.target.value }, ...locations[movie.test(type)?'movie':'show'].map(Q => furnish('option', { value: Q.id }, Q.path))): '' ) ) @@ -374,7 +512,7 @@ let configuration, init, Update; furnish('input.web-to-plex-prompt-input[type=text]', { placeholder: 'Add an item (enter to add): Title (Year) Type / ID Type', title: 'Solo: A Star Wars Story (2018) movie / tt3778644 m', onkeydown: async event => { if(event.keyCode == 13) { let title, year, type, self = event.target, R = RegExp, - movie = /^(m(?:ovies?)?|f(?:ilms?)?|c(?:inemas?)?)/i, + movie = /^(m(?:ovies?)?|f(?:ilms?)?|c(?:inemas?)?|theat[re]{2})/i, Db, IMDbID, TMDbID, TVDbID, value = self.value; self.setAttribute('disabled', self.disabled = true); @@ -420,9 +558,9 @@ let configuration, init, Update; } } } }), - furnish('button.web-to-plex-prompt-decline', { onmouseup: event => { remove(true); callback([]) }, title: 'Close' }, '\u2718'), - furnish('button.web-to-plex-prompt-accept', { onmouseup: event => { remove(true); new Prompt(prompt_type, options, callback, container) }, title: 'Reset' }, '\u21BA'), - furnish('button.web-to-plex-prompt-accept', { onmouseup: event => { remove(true); callback(data.filter(value => value !== null && value !== undefined)) }, title: 'Continue' }, '\u2714') + furnish('button.web-to-plex-prompt-decline', { onmouseup: event => { remove(true); callback([]) }, title: 'Close' }, Glyphs.exit), + furnish('button.web-to-plex-prompt-accept', { onmouseup: event => { remove(true); new Prompt(prompt_type, options, callback, container) }, title: 'Reset' }, Glyphs.restart), + furnish('button.web-to-plex-prompt-accept', { onmouseup: event => { remove(true); callback(data.filter(value => value !== null && value !== undefined)) }, title: 'Continue' }, Glyphs.ok) ) ) ); @@ -448,7 +586,7 @@ let configuration, init, Update; header.innerText = `Correction ready...`; }; - prompt = furnish('div.web-to-plex-prompt', {}, + prompt = furnish('div.web-to-plex-prompt', { type: prompt_type }, furnish('div.web-to-plex-prompt-body', { style: `background-image: url(${ IMG_URL.noise_background }), url(${ IMG_URL.background }); background-size: auto, cover;` }, // The prompt's title furnish('h1.web-to-plex-prompt-header', {}, (array.length == 1? 'Correction ready...': `Choose a correction from ${array.length} items`)), @@ -477,16 +615,21 @@ let configuration, init, Update; captured.tvdb.push(v); elements.push( - furnish('li.web-to-plex-prompt-option.mutable', { value: index, innerHTML: `

${ index + 1 }. ${ title }${ year? ` (${ year })`: '' } \u2014 ${ type }

${ i? `${i}`: '/' } \u2014 ${ t? `${t}`: '/' } \u2014 ${ v? `${v}`: '/' }` }, - furnish('button.choose', { title: `Use "${ title } (${ year })"`, onmouseup: event => { - let element = event.target.parentElement, - children = [...element.parentElement.children].filter(e => e != element); + furnish('li.web-to-plex-prompt-option.mutable.choose', { + value: index, + innerHTML: `

${ index + 1 }. ${ title }${ year? ` (${ year })`: '' } \u2014 ${ type }

${ i? `${i}`: '/' } \u2014 ${ t? `${t}`: '/' } \u2014 ${ v? `${v}`: '/' }`, + onmouseup: event => { + let self = traverse(event.target, element => element.classList.contains('mutable')), + children = [...self.parentElement.children].filter(e => e != self); children.forEach(child => { + child.classList.remove('chosen'); remove(child); - element.parentElement.appendChild(child); + self.parentElement.appendChild(child); }); - element.classList.add('chosen'); - } }) + self.classList.add('chosen'); + } + }, + furnish('i[glyph=ok]', { title: `Use "${ title } (${ year })"` }) ) ); } @@ -545,9 +688,9 @@ let configuration, init, Update; } } } }), - furnish('button.web-to-plex-prompt-decline', { onmouseup: event => { remove(true); prompt.done = true; callback([]) }, title: 'Close' }, '\u2718'), - furnish('button.web-to-plex-prompt-accept', { onmouseup: event => { remove(true); prompt.done = false; new Prompt(prompt_type, options, callback, container) }, title: 'Reset' }, '\u21BA'), - furnish('button.web-to-plex-prompt-accept', { onmouseup: event => { remove(true); prompt.done = true; callback(data.filter(value => value !== null && value !== undefined)) }, title: 'Continue' }, '\u2714') + furnish('button.web-to-plex-prompt-decline', { onmouseup: event => { remove(true); prompt.done = true; callback([]) }, title: 'Close' }, Glyphs.exit), + furnish('button.web-to-plex-prompt-accept', { onmouseup: event => { remove(true); prompt.done = false; new Prompt(prompt_type, options, callback, container) }, title: 'Reset' }, Glyphs.restart), + furnish('button.web-to-plex-prompt-accept', { onmouseup: event => { remove(true); prompt.done = true; callback(data.filter(value => value !== null && value !== undefined)) }, title: 'Continue' }, Glyphs.ok) ) ) ); @@ -569,7 +712,7 @@ let configuration, init, Update; header.innerText = 'Approve ' + counter.children.length + (counter.children.length == 1?' item': ' items'); }; - prompt = furnish('div.web-to-plex-prompt', {}, + prompt = furnish('div.web-to-plex-prompt', { type: prompt_type }, furnish('div.web-to-plex-prompt-body', { style: `background-image: url(${ IMG_URL.noise_background }), url(${ IMG_URL.background }); background-size: auto, cover;` }, // The prompt's title furnish('h1.web-to-plex-prompt-header', {}, 'Approve ' + array.length + (array.length == 1? ' item': ' items')), @@ -586,21 +729,21 @@ let configuration, init, Update; elements.push( furnish('li.web-to-plex-prompt-option.mutable', { value: index, innerHTML: `

${ index + 1 } \u00b7 ${ title }${ year? ` (${ year })`: '' } \u2014 ${ type }

` }, - furnish('button.remove', { title: `Remove "${ title }"`, onmouseup: event => { remove(event.target.parentElement); event.target.remove() } }), + furnish('i[glyph=remove]', { title: `Remove "${ title }"`, onmouseup: event => { remove(event.target.parentElement); event.target.remove() } }), ( __CONFIG__.PromptQuality? - P_QUA = furnish('select.quality', { index, onchange: event => data[event.target.getAttribute('index')].quality = event.target.value }, ...profiles[/(movie|film|cinema)/i.test(type)?'movie':'show'].map(Q => furnish('option', { value: Q.id }, Q.name))): + P_QUA = furnish('select.quality', { index, onchange: event => data[+event.target.getAttribute('index')].quality = event.target.value }, ...profiles[movie.test(type)?'movie':'show'].map(Q => furnish('option', { value: Q.id }, Q.name))): '' ),( __CONFIG__.PromptLocation? - P_LOC = furnish('select.location', { index, onchange: event => data[event.target.getAttribute('index')].location = event.target.value }, ...locations[/(movie|film|cinema)/i.test(type)?'movie':'show'].map(Q => furnish('option', { value: Q.id }, Q.path))): + P_LOC = furnish('select.location', { index, onchange: event => data[+event.target.getAttribute('index')].location = event.target.value }, ...locations[movie.test(type)?'movie':'show'].map(Q => furnish('option', { value: Q.id }, Q.path))): '' ) ) ); - if(P_QUA) P_QUA.value = defaults[type].quality; - if(P_LOC) P_LOC.value = defaults[type].location; + if(P_QUA) P_QUA.value = data[index].quality = defaults[type].quality; + if(P_LOC) P_LOC.value = data[index].location = defaults[type].location; P_QUA = P_LOC = null; } @@ -611,9 +754,9 @@ let configuration, init, Update; // The engagers furnish('div.web-to-plex-prompt-footer', {}, - furnish('button.web-to-plex-prompt-decline', { onmouseup: event => { remove(true); callback([]) }, title: 'Close' }, '\u2718'), - furnish('button.web-to-plex-prompt-accept', { onmouseup: event => { remove(true); new Prompt(prompt_type, options, callback, container) }, title: 'Reset' }, '\u21BA'), - furnish('button.web-to-plex-prompt-accept', { onmouseup: event => { remove(true); callback(data.filter(value => value !== null && value !== undefined)) }, title: 'Continue' }, '\u2714') + furnish('button.web-to-plex-prompt-decline', { onmouseup: event => { remove(true); callback([]) }, title: 'Close' }, Glyphs.exit), + furnish('button.web-to-plex-prompt-accept', { onmouseup: event => { remove(true); new Prompt(prompt_type, options, callback, container) }, title: 'Reset' }, Glyphs.restart), + furnish('button.web-to-plex-prompt-accept', { onmouseup: event => { remove(true); callback(data.filter(value => value)) }, title: 'Continue' }, Glyphs.ok) ) ) ); @@ -622,8 +765,15 @@ let configuration, init, Update; /* Allows the user to modify a single item (before being pushed) */ case 'modify': let { title, year, type, IMDbID, TMDbID, TVDbID } = options, + refined = { ...defaults[type], ...options }, + uuid = UUID.from({ type, title, IMDbID, TMDbID, TVDbID }), P_QUA, P_LOC; + if(REFINED[uuid]) + refined = REFINED[uuid]; + else + REFINED[uuid] = refined; + let i = IMDbID, t = TMDbID, v = TVDbID, @@ -644,34 +794,34 @@ let configuration, init, Update; element.remove(); }; - type = /(movie|film|cinema)/i.test(type)?'movie':'show'; + type = /(movie|film|cinema|theat[re]{2})s?/i.test(type)?'movie':'show'; - prompt = furnish('div.web-to-plex-prompt', {}, + prompt = furnish('div.web-to-plex-prompt', { type: prompt_type }, furnish('div.web-to-plex-prompt-body', { style: `background-image: url(${ IMG_URL.noise_background }), url(${ IMG_URL.background }); background-size: auto, cover;` }, // The prompt's title - furnish('h1.web-to-plex-prompt-header', { innerHTML: `${ title }${ year? ` (${ year })`: '' }` }), + furnish('h1.web-to-plex-prompt-header', { innerHTML: `${ title.length < 40? title: title.slice(0, 37) + '...' }${ parseInt(year)? ` (${ year })`: '' } \u2014 ${ movie.test(type)? 'Movie': 'TV Show' }` }), // The prompt's items furnish('div.web-to-plex-prompt-options', {}, - furnish('div.web-to-plex-prompt-option', { innerHTML: `${ type } \u2014 ${ i? `${i}`: '/' } \u2014 ${ t? `${t}`: '/' } \u2014 ${ v? `${v}`: '/' }` }), + furnish('div.web-to-plex-prompt-option', { innerHTML: `${ i? `${i}`: '?' } \u2014 ${ t? `${t}`: '?' } \u2014 ${ v? `${v}`: '?' }` }), ( __CONFIG__.PromptQuality? - P_QUA = furnish('select.quality', { onchange: event => options.quality = event.target.value }, ...profiles[type].map(Q => furnish('option', { value: Q.id }, Q.name))): + P_QUA = furnish('select.quality', { onchange: event => REFINED[uuid].quality = event.target.value }, ...profiles[type].map(Q => furnish('option', { value: Q.id }, Q.name))): '' ), furnish('br'), ( __CONFIG__.PromptLocation? - P_LOC = furnish('select.location', { onchange: event => options.location = event.target.value }, ...locations[type].map(Q => furnish('option', { value: Q.id }, Q.path))): + P_LOC = furnish('select.location', { onchange: event => REFINED[uuid].location = event.target.value }, ...locations[type].map(Q => furnish('option', { value: Q.id }, Q.path))): '' ) ), // The engagers furnish('div.web-to-plex-prompt-footer', {}, - furnish('button.web-to-plex-prompt-decline', { onmouseup: event => { remove(true); callback([]) }, title: 'Close' }, '\u2718'), - furnish('button.web-to-plex-prompt-accept', { onmouseup: event => { remove(true); new Prompt(prompt_type, options, callback, container) }, title: 'Reset' }, '\u21BA'), - furnish('button.web-to-plex-prompt-accept', { onmouseup: event => { remove(true); callback(options) }, title: 'Continue' }, '\u2714'), + furnish('button.web-to-plex-prompt-decline', { onmouseup: event => { remove(true); callback([]) }, title: 'Close' }, Glyphs.exit), + furnish('button.web-to-plex-prompt-accept', { onmouseup: event => { remove(true); new Prompt(prompt_type, REFINED[uuid], callback, container) }, title: 'Reset' }, Glyphs.restart), + furnish('button.web-to-plex-prompt-accept', { onmouseup: event => { remove(true); callback(REFINED[uuid]) }, title: 'Continue' }, Glyphs.ok), ( (!__CONFIG__.UseLowCache || (__CONFIG__.UseLowCache && CAUGHT.has({ imdb: i, tmdb: t, tvdb: v })))? furnish('button.web-to-plex-prompt-accept', { onmouseup: event => { let self = event.target; open(self.getAttribute('href'), '_blank') }, href: slugify(type), title: `Open on ${ manager[type] }` }, manager[type]): @@ -722,14 +872,14 @@ let configuration, init, Update; (init && !RUNNING? (init(), RUNNING = true): RUNNING = false); }; - prompt = furnish('div.web-to-plex-prompt', {}, + prompt = furnish('div.web-to-plex-prompt', { type: prompt_type }, furnish('div.web-to-plex-prompt-body', { style: `background-image: url(${ IMG_URL.noise_background }), url(${ IMG_URL.background }); background-size: auto, cover;` }, // The prompt's title furnish('h1.web-to-plex-prompt-header', { innerHTML: `"${ alias || name }" would like:` }), // The prompt's items furnish('div.web-to-plex-prompt-options', {}, - ...((permissions = permission.split(/\s*,\s*/).filter(v=>v&&v.length)).map( + ...((permissions = permission.split(/\s*,\s*/).filter((v,i,a)=>v&&v.length&&a.indexOf(v)==i)).map( __permission => furnish('div.web-to-plex-prompt-option.web-to-plex-permission', { innerHTML: `Access to your ${ __permission.replace(/(y)?s?$/, ($0, $1, $$, $_) => ($1? 'ies': 's')) } — ` + (p => { let R = RegExp, @@ -773,8 +923,8 @@ let configuration, init, Update; // The engagers furnish('div.web-to-plex-prompt-footer', {}, - furnish('button.web-to-plex-prompt-decline', { onmouseup: event => { if(!event.isTrusted) throw alert('The script for this site is trying to decline its own permissions!'), 'Malicious script. Decline permissions'; remove(true); callback(false, {}) }, title: 'Deny all permissions' }, '\u2718'), - furnish('button.web-to-plex-prompt-accept', { onmouseup: async event => { if(!event.isTrusted) throw alert('The script for this site is trying to grant its own permissions!'), 'Malicious script. Grant permissions'; remove(true); await callback(true, permissions); top.open(top.location.href, '_top'); }, title: 'Allow all permissions' }, '\u2714') + furnish('button.web-to-plex-prompt-decline', { onmouseup: event => { if(!event.isTrusted) throw alert('The script for this site is trying to decline its own permissions!'), 'Malicious script. Decline permissions'; remove(true); callback(false, {}) }, title: 'Deny all permissions' }, Glyphs.ban), + furnish('button.web-to-plex-prompt-accept', { onmouseup: async event => { if(!event.isTrusted) throw alert('The script for this site is trying to grant its own permissions!'), 'Malicious script. Grant permissions'; remove(true); await callback(true, permissions); top.open(top.location.href, '_top'); }, title: 'Allow all permissions' }, Glyphs.ok) ) ) ); @@ -806,7 +956,7 @@ let configuration, init, Update; src: url, style: ` display: none !important; - opacity: 0 !important; + opacity: 0 !important; visibility: hidden !important; `, @@ -824,6 +974,8 @@ let configuration, init, Update; // Send an update query to background.js Update = (type, options = {}, postToo) => { + Update.running = options.script || options.plugin || null; + if(configuration) console.log(`Requesting update (${ type } [post-to-top=${ !!postToo }])`, options); else if(!Update.retry) @@ -1006,7 +1158,7 @@ let configuration, init, Update; UTILS_STORAGE.get(null, options => { if(chrome.runtime.lastError) - chrome.storage.local.get(null, handleOptions); + UTILS_STORAGE.get(null, handleOptions); else handleOptions(options); }); @@ -1017,13 +1169,17 @@ let configuration, init, Update; async function ParsedOptions() { return await options() .then( - options => { + async options => { configuration = {}; + let { running } = Update, + allowed = await load(`has/${ running }`), + permiss = await load(`get/${ running }`); + /* Don't expose the user's authentication information to sites */ for(let key in options) if(/username|password|token|api|server|url|storage|cache|proxy|client|builtin|plugin|qualit/i.test(key)) - if(ALLOWED && RegExp(PERMISS.join('|'),'i').test(key)) + if(allowed && RegExp(permiss.join('|'),'i').test(key)) configuration[key] = options[key]; else /* Do nothing */; @@ -1439,6 +1595,8 @@ let configuration, init, Update; .replace(/@\{b(ase-?)?64-url\}/gi, btoa(URL)) .replace(/@\{enc(ode)?-url\}/gi, encodeURIComponent(URL)) .replace(/@\{(raw-)?url\}/gi, URL); + + headers[$1] = $2; } }); @@ -1464,7 +1622,7 @@ let configuration, init, Update; rqut = apit, // request type: tmdb, imdb, or tvdb manable = __CONFIG__.ManagerSearch && !(rerun & 0b1000), // is the user's "Manager Searches" option enabled? UTF_16 = /[^0\u0020-\u007e, 1\u00a1\u00bf-\u00ff, 2\u0100-\u017f, 3\u0180-\u024f, 4\u0300-\u036f, 5\u0370-\u03ff, 6\u0400-\u04ff, 7\u0500-\u052f, 8\u20a0-\u20bf]+/g, - MV = /^(movies?|films?|cinemas?)$/i.test(apit), + MV = /^(movies?|films?|cinemas?|theat[re]{2}s?)$/i.test(apit), TV = /^(tv[\s\-]*(?:shows?|series)?)$/i.test(apit); iid = iid == 'tt'? null: iid; @@ -1473,7 +1631,7 @@ let configuration, init, Update; rqut = /(tv|show|series)/i.test(rqut)? 'tvdb': - /(movie|film|cinema)s?/i.test(rqut)? + /(movie|film|cinema|theat[re])s?/i.test(rqut)? 'tmdb': rqut || '*'; manable = manable && (__CONFIG__.usingOmbi || (__CONFIG__.usingRadarr && rqut == 'tmdb') || ((__CONFIG__.usingSonarr || __CONFIG__.usingMedusa /*|| __CONFIG__.usingSickBeard*/) && rqut == 'tvdb')); @@ -1570,7 +1728,7 @@ let configuration, init, Update; cors = proxy.url, // if cors is requried and not uspported, proxy through this URL headers = HandleProxyHeaders(proxy.headers, url); - if(proxy.enabled && /(^http:\/\/)(?!localhost|127\.0\.0\.1(?:\/8)?|::1(?:\/128)?|:\d+)\b/i.test(url)) { + if(proxy.enabled && /(^https?:\/\/)(?!localhost|127\.0\.0\.1(?:\/8)?|::1(?:\/128)?|:\d+)\b/i.test(url)) { url = cors .replace(/\{b(ase-?)?64-url\}/gi, btoa(url)) .replace(/\{enc(ode)?-url\}/gi, encodeURIComponent(url)) @@ -2025,7 +2183,7 @@ let configuration, init, Update; ); } - let contentType = (/movies?|film/i.test(options.type)? 'movie': 'tv'); + let contentType = (/(movie|film|cinema|theat[re]{2})s?/i.test(options.type)? 'movie': 'tv'); chrome.runtime.sendMessage({ type: 'PUSH_OMBI', @@ -2156,10 +2314,12 @@ let configuration, init, Update; if(!prompted && (PromptQuality || PromptLocation)) return new Prompt('modify', options, refined => Request_Radarr(refined, true)); + let parsePath = id => JSON.parse(__CONFIG__.radarrStoragePaths).map(item => item.id == id? item: null).filter(n => n)[0].path.replace(/\\/g, '\\\\'); + if(PromptQuality && +options.quality > 0) PromptValues.QualityID = +options.quality; - if(PromptLocation && options.location) - PromptValues.StoragePath = JSON.parse(__CONFIG__.radarrStoragePaths).map(item => item.id == options.location? item: null).filter(n => n)[0].path.replace(/\\/g, '\\\\'); + if(PromptLocation && +options.location > 0) + PromptValues.StoragePath = parsePath(options.location); new Notification('info', `Sending "${ options.title }" to Radarr`, 3000); @@ -2167,7 +2327,7 @@ let configuration, init, Update; type: 'PUSH_RADARR', url: `${ __CONFIG__.radarrURL }api/movie/`, token: __CONFIG__.radarrToken, - StoragePath: __CONFIG__.radarrStoragePath, + StoragePath: parsePath(__CONFIG__.radarrStoragePath), QualityID: __CONFIG__.radarrQualityProfileId, basicAuth: __CONFIG__.radarrBasicAuth, title: options.title, @@ -2213,10 +2373,12 @@ let configuration, init, Update; if(!prompted && (PromptQuality || PromptLocation)) return new Prompt('modify', options, refined => Request_Sonarr(refined, true)); + let parsePath = id => JSON.parse(__CONFIG__.sonarrStoragePaths).map(item => item.id == id? item: null).filter(n => n)[0].path.replace(/\\/g, '\\\\'); + if(PromptQuality && +options.quality > 0) PromptValues.QualityID = +options.quality; - if(PromptLocation && options.location) - PromptValues.StoragePath = JSON.parse(__CONFIG__.sonarrStoragePaths).map(item => item.id == options.location? item: null).filter(n => n)[0].path.replace(/\\/g, '\\\\'); + if(PromptLocation && +options.location > 0) + PromptValues.StoragePath = parsePath(options.location); new Notification('info', `Sending "${ options.title }" to Sonarr`, 3000); @@ -2224,7 +2386,7 @@ let configuration, init, Update; type: 'PUSH_SONARR', url: `${ __CONFIG__.sonarrURL }api/series/`, token: __CONFIG__.sonarrToken, - StoragePath: __CONFIG__.sonarrStoragePath, + StoragePath: parsePath(__CONFIG__.sonarrStoragePath), QualityID: __CONFIG__.sonarrQualityProfileId, basicAuth: __CONFIG__.sonarrBasicAuth, title: options.title, @@ -2382,43 +2544,39 @@ let configuration, init, Update; else if(persistent && firstButton !== null && firstButton !== undefined) return firstButton; - let { __theme } = __CONFIG__; + let { __theme } = __CONFIG__; - let ThemeClasses = JSON.parse(COMPRESS? iBWT(unzip(decompress(__theme))): __theme), - HeaderClasses = [], - ParsedAttributes = {}; + __theme = JSON.parse(COMPRESS? iBWT(unzip(decompress(__theme))): __theme); - // Theme(s) - if(!ThemeClasses.length) - ThemeClasses = ''; - else - ThemeClasses = '.' + ThemeClasses.join('.'); - - ThemeClasses = ThemeClasses.split('.').filter(v => { - let R = RegExp; - - if(/([^=]+?)=([^.]+?)/.test(v)) { - ParsedAttributes[R.$1] = R.$2; + let ThemeClasses = [], + HeaderClasses = [], + ParsedAttributes = {}; - return false; - } + // Theme(s) + for(let theme in __theme) + if((typeof __theme[theme]) == 'boolean') + ThemeClasses.push(theme); + else + ParsedAttributes[theme] = __theme[theme]; - return true; - }).join('.'); + if(!ThemeClasses.length) + ThemeClasses = ''; + else + ThemeClasses = '.' + ThemeClasses.join('.'); - // Header(s) - for(let header in headers) - if(headers[header]) - HeaderClasses.push( header ); + // Header(s) + for(let header in headers) + if(headers[header]) + HeaderClasses.push( header ); - if(!HeaderClasses.length) - HeaderClasses = ''; - else - HeaderClasses = '.' + HeaderClasses.join('.'); + if(!HeaderClasses.length) + HeaderClasses = ''; + else + HeaderClasses = '.' + HeaderClasses.join('.'); // +
- +
- Advance + Advanced

Plex Server Options

- +
Use this to communicate directly with your Plex server.
Such as http://localhost:32400/ or http://192.168.1.100:32400/ @@ -111,14 +112,14 @@

Plex Server Options

Ombi (Movies/TV Shows) - +

Connection Settings

- +
Such as https://example.com/ombi or http://192.168.1.100:5000
@@ -126,35 +127,38 @@

Connection Settings

- +
- 1. Go to Ombi | Settings | Ombi
- 2. Copy/Paste the "API Key."
- Such as aa756d33242f6g8ffbca2b3963586f21 + 1. Go to OmbiSettingsOmbi
+ 2. Copy/Paste the API Key
+ — Such as aa756d33242f6a8ffbca2b3963586f21
- +


- +
@@ -163,14 +167,14 @@

Login (saved)

Watcher (Movies) - +

Connection Settings

- +
Such as https://example.com/watcher or http://192.168.1.100:9090
@@ -178,63 +182,68 @@

Connection Settings

- +
- 1. Go to Watcher | Settings | Server
- 2. Copy/Paste the "API Key."
- Such as aa756d33242f6g8ffbca2b3963586f21 + 1. Go to WatcherSettingsServer
+ 2. Copy/Paste the API Key
+ — Such as aa756d33242f6a8ffbca2b3963586f21
-

Login (saved)

+

Login (saved)

The default username is watcher. The default password is rehctaw.

- +
Only use this if you setup a Watcher username.
- +
Only use this if you setup a Watcher password.
Your password will be hidden once saved.

-
This information will be used for Basic Access Authentication only. This will allow the extension to ask Watcher for your list of films, or to add to your list of films.
+
+ This information will be used for Basic Access Authentication only.
+ This will allow the extension to ask Watcher for your list of films, or to add to your list of films. +
- +
- +
- +


- +
Radarr (Movies) - +

Connection Settings

- +
Such as https://example.com/radarr or http://192.168.1.100:7878
@@ -242,37 +251,40 @@

Connection Settings

- +
- 1. Go to Radarr | Settings | General
- 2. Click on "Show advance," then copy/paste the "API Key."
- Such as aa756d33242f6g8ffbca2b3963586f21 + 1. Go to RadarrSettingsGeneral
+ 2. Click on Show Advanced then copy/paste the API Key
+ — Such as aa756d33242f6a8ffbca2b3963586f21
-

Login (saved)

+

Login (saved)

- +
Only use this if you setup a Radarr username.
- +
Only use this if you setup a Radarr password.
Your password will be hidden once saved.

-
This information will be used for Basic Access Authentication only. This will allow the extension to ask Radarr for your list of films, or to add to your list of films.
+
+ This information will be used for Basic Access Authentication only.
+ This will allow the extension to ask Radarr for your list of films, or to add to your list of films. +
- +
- +
This should be the same path (verbatim) used in Radarr. @@ -280,27 +292,27 @@

Login (saved)

- +


- +
CouchPotato (Movies) - +

Connection Settings

- +
Such as https://example.com/couchpotato or http://192.168.1.100:5050
@@ -308,38 +320,41 @@

Connection Settings

- +
- 1. Go to CouchPotato | Settings
- 2. Copy/Paste the "API Key."
- Such as aa756d33242f6g8ffbca2b3963586f21 + 1. Go to CouchPotatoSettings
+ 2. Copy/Paste the API Key
+ — Such as aa756d33242f6a8ffbca2b3963586f21
-

Login (saved)

+

Login (saved)

- +
Only use this if you setup a CouchPotato username.
- +
Only use this if you setup a CouchPotato password.
Your password will be hidden once saved.
-
This information will be used for Basic Access Authentication only. This will allow the extension to ask Couchpotato for your list of films, or to add to your list of films.
+
+ This information will be used for Basic Access Authentication only.
+ This will allow the extension to ask Couchpotato for your list of films, or to add to your list of films. +
@@ -348,7 +363,7 @@

Login (saved)



- +
@@ -357,14 +372,14 @@

Login (saved)

Medusa (TV Shows) - +

Connection Settings

- +
Such as https://example.com/medusa or http://192.168.1.100:8081
@@ -372,35 +387,38 @@

Connection Settings

- +
- 1. Go to Medusa | Settings | General | Interface | Web Interface
- 2. Copy/Paste the "API key."
- Such as aa756d33242f6g8ffbca2b3963586f21 + 1. Go to MedusaSettingsGeneralInterfaceWeb Interface
+ 2. Copy/Paste the API key
+ — Such as aa756d33242f6a8ffbca2b3963586f21
-

Login (saved)

+

Login (saved)

- +
- +
Your password will be hidden once saved.

-
This information will be used for Basic Access Authentication only. This will allow the extension to ask Medusa for your list of TV shows, or to add to your list of TV shows.
+
+ This information will be used for Basic Access Authentication only.
+ This will allow the extension to ask Medusa for your list of TV shows, or to add to your list of TV shows. +
- +
- +
This should be the same path (verbatim) used in Medusa. @@ -408,33 +426,33 @@

Login (saved)

- - +


- +
Sonarr (TV Shows) - +

Connection Settings

- +
Such as https://example.com/sonarr or http://192.168.1.100:8989
@@ -442,37 +460,40 @@

Connection Settings

- +
- 1. Go to Sonarr | Settings | General
- 2. Click on "Show advance," then copy/paste the "API Key."
- Such as aa756d33242f6g8ffbca2b3963586f21 + 1. Go to SonarrSettingsGeneral
+ 2. Click on Show Advanced then copy/paste the API Key
+ — Such as aa756d33242f6a8ffbca2b3963586f21
-

Login (saved)

+

Login (saved)

- +
Only use this if you setup a Sonarr username.
- +
Only use this if you setup a Sonarr password.
Your password will be hidden once saved.

-
This information will be used for Basic Access Authentication only. This will allow the extension to ask Sonarr for your list of TV shows, or to add to your list of TV shows.
+
+ This information will be used for Basic Access Authentication only.
+ This will allow the extension to ask Sonarr for your list of TV shows, or to add to your list of TV shows. +
- +
- +
This should be the same path (verbatim) used in Sonarr. @@ -480,27 +501,27 @@

Login (saved)

- +


- +
Sick Beard (TV Shows) - +

Connection Settings

- +
Such as https://example.com/sickBeard or http://192.168.1.100:8081
@@ -508,37 +529,40 @@

Connection Settings

- +
- 1. Go to Sick Beard | Config | General | API
- — a. Ensure the checkbox "Enable API" is enabled
- — b. Press the "Generate" button
- 2. Copy/Paste the "API Key."
- Such as aa756d33242f6g8ffbca2b3963586f21 + 1. Go to Sick BeardConfigGeneralAPI
+ — a. Ensure the checkbox Enable API is checked
+ — b. Press the Generate button
+ 2. Copy/Paste the API Key
+ — Such as aa756d33242f6a8ffbca2b3963586f21
-

Login (saved)

+

Login (saved)

- +
- +
Your password will be hidden once saved.

-
This information will be used for Basic Access Authentication only. This will allow the extension to ask Sick Beard for your list of TV shows, or to add to your list of TV shows.
+
+ This information will be used for Basic Access Authentication only.
+ This will allow the extension to ask Sick Beard for your list of TV shows, or to add to your list of TV shows. +
- +
- +
This should be the same path (verbatim) used in Sick Beard. @@ -546,13 +570,13 @@

Login (saved)

- - + - + +

+
+ — a minimalist, rectangular button (top center of page) +
+
+ — a normal, circular button (bottom left or right of page) +
- Which direction should the button open (◯ only)? + Which direction should the master button open (◯ only)?
- +
- How transparent should the button be when hidden? + How transparent should the master button be when hidden? +
+ + +
+
+

The Minion Button(s)

+
+ Allow the use of custom (minion) buttons?
- - + + + + +
@@ -607,14 +649,14 @@

The Button

Experimental Sites -
+


Default Sites -
+

@@ -633,18 +675,18 @@

Proxy Settings

Force Secure Connections - +

- If enabled, all insecure (HTTP) requests will be through an HTTPS proxy. + If enabled, all requests sent by Web to Plex will be proxied.

Proxy URL & Syntax

- +
Please provide the URL of your proxy.
@@ -660,9 +702,9 @@

Proxy URL & Syntax

Proxy Headers

-
+
- If your proxy requires special headers, enter that information in here. + If your proxy requires special headers, enter that information in here.
  • @{raw-url} OR @{url} — the raw, uneditied URL
  • @@ -673,11 +715,23 @@

    Proxy Headers

+
+ +
    +
  • + IP Address + + + +
  • +
+
+

Native Proxy Settings

@@ -691,20 +745,20 @@

Native Proxy Settings

Auto Grab - +

- When the user presses the Grab button, the extension should: + When you press the Grab button, the extension should:
  • Grab ALL — Find items not on Plex, and grab them
  • -
  • ASK user — Find items not on Plex, and grab what the user approves
  • +
  • ASK you — Find items not on Plex, and grab what you approve

Maximum Auto Grabs

- - + +
How many items can be automatically handled before requiring permission to continue?
@@ -714,12 +768,12 @@

Maximum Auto Grabs

Prompt for Save Location - +

- When the user presses the Grab button should the save location be asked for? + When you press the Grab button, should the save location be asked for?
Only supports Medusa, Radarr, and Sonarr.
@@ -729,12 +783,12 @@

Prompt for Quality - +

- When the user presses the Grab button should the quality be asked for? + When you press the Grab button, should the quality be asked for?
Only supports Medusa, Radarr, and Sonarr.
@@ -748,23 +802,23 @@

Ignore Found Items - +

- When the user presses the Grab button and an item already exists, should the notification be ignored or not? + When you press the Grab button and the item already exists, should the notification be ignored or not?

Ignore Repetitive Notifications - +

- When the user presses the Grab button and there are several incoming notifications of similar information, should the notifications be ignored (after the first one) or not? + When you press the Grab button and there are several incoming notifications of similar information, should the notifications be ignored (after the first one) or not?
@@ -776,14 +830,14 @@

Loose Searching - +


- - + +
Allows the extension to search for non-English titles using pattern matching (as a last resort).
@@ -795,13 +849,13 @@

Manager Searching - +

Allows the extension to use your manager(s) to find media.
- Currently supports: Medusa, Ombi, Radarr, and Sonarr. + Currently supports Medusa, Ombi, Radarr, and Sonarr.

@@ -809,7 +863,7 @@

ID Fetching Mode - +

@@ -823,13 +877,13 @@

- Advance Settings + Advanced Settings BETA

API Keys

- +
You can sign up for an API key, or have OMDb used as a last resort.
@@ -837,17 +891,29 @@

API Keys

- +
You can learn more on how to obtain an API key.

Data Handling

+
+

Settings' Data

+ +
+ +
+ WARNING — This will remove all of your data. +
+ If you don't press Save, this action will be ignored. +
+
+

Data Compression

- +
@@ -886,13 +952,13 @@

Developer Options

Developer Mode - +

WARNING — may cause data loss.
- Enables developer (debugging) mode, showing advance errors and logging to the console when possible. + Enables developer (debugging) mode, showing Advanced errors and logging to the console when possible.
@@ -905,12 +971,17 @@

External Links

- +
Browse the Web to Plex Wiki for help setting up
+
Browse issues if you're having trouble
+
+ +
+ Download Plex Media Server. +
+
+
@@ -994,9 +1065,10 @@

External Links

+
- ... + ... diff --git a/src/options/index.js b/src/options/index.js index bf9aa51..2fa2e81 100644 --- a/src/options/index.js +++ b/src/options/index.js @@ -14,7 +14,7 @@ if(chrome.runtime.lastError) // FireFox doesn't support sync storage. const storage = (chrome.storage.sync || chrome.storage.local), - $ = top.$ = (selector, all) => (all? [...document.querySelectorAll(selector)]: document.querySelector(selector)), + $ = top.$ = (selector, all = false, container = document) => (all? [...container.querySelectorAll(selector)]: container.querySelector(selector)), $$ = top.$$ = (selector, all) => (all? [...$('display').querySelectorAll(selector)]: $('display').querySelector(selector)), __servers__ = $('[data-option="preferredServer"]'), __sickBeard_qualityProfile__ = $(`[data-option="sickBeardQualityProfileId"]`), @@ -117,7 +117,7 @@ const storage = (chrome.storage.sync || chrome.storage.local), 'ManagerSearch', 'UseLowCache', - // Advance Settings + // Advanced Settings 'OMDbAPI', 'TMDbAPI', 'UseLZW', @@ -179,7 +179,6 @@ const storage = (chrome.storage.sync || chrome.storage.local), 'builtin_gostream', 'builtin_tubi', 'builtin_webtoplex', - 'builtin_shanaproject', // Plugins - End of file, "let plugins =" 'plugin_toloka', @@ -196,7 +195,11 @@ const storage = (chrome.storage.sync || chrome.storage.local), 'plugin_metacritic', // Theme Settings - ...(() => [...$('[data-option^="theme:"i]', true)].map(e => e.dataset.option))() + 'UseMinions', + ...(() => [...$('[data-option^="theme:"i]', true)].map(e => e.dataset.option))(), + + // Other Settings + '__defaults', ]; let PlexServers = [], @@ -218,7 +221,23 @@ let __caught = { tvdb: [], }, // The theme classes - __theme = []; + __theme = {}; + +// Icon Markers +let MARKERS = [ + // yes + ``, + // no + ``, + // maybe + `` +]; + +MARKERS.yes = MARKERS[0]; +MARKERS.no = MARKERS[1]; +MARKERS.maybe = MARKERS[2]; + +let RESETTING_SETTINGS = false; // create and/or queue a notification // state = "error" - red @@ -522,6 +541,7 @@ function performPlexLogin({ event }) { s.title = ''; __servers__.innerHTML = ''; __save__.disabled = true; + __save__.innerHTML = 'Save ' + MARKERS.maybe; LoadingAnimation(true); tryPlexLogin(u, p) @@ -538,7 +558,8 @@ function performPlexLogin({ event }) { return performPlexTest({}); } - }); + }) + .catch(error => { LoadingAnimation(); new Notification('error', error); __save__.innerHTML = 'Save ' + MARKERS.no; }); } @@ -548,8 +569,9 @@ function performPlexTest({ ServerID, event }) { inusestatus = [...$('[using="plex"]', true)]; __save__.disabled = true; + __save__.innerHTML = 'Save ' + MARKERS.maybe; __servers__.innerHTML = ''; - teststatus.textContent = '?'; + teststatus.innerHTML = MARKERS.maybe; inusestatus.map(e => e.setAttribute('in-use', false)); LoadingAnimation(true); @@ -557,7 +579,7 @@ function performPlexTest({ ServerID, event }) { LoadingAnimation(); PlexServers = servers; - teststatus.textContent = '!'; + teststatus.innerHTML = MARKERS[+!servers]; inusestatus.map(e => e.setAttribute('in-use', false)); if(!servers) @@ -565,6 +587,7 @@ function performPlexTest({ ServerID, event }) { inusestatus.map(e => e.setAttribute('in-use', true)); __save__.disabled = false; + __save__.innerHTML = 'Save ' + MARKERS.yes; teststatus.classList = true; (servers = [{ sourceTitle: 'GitHub', clientIdentifier: '', name: 'No Server', notice: 'This will not connect to any Plex servers' }, ...servers]).forEach(server => { @@ -580,7 +603,8 @@ function performPlexTest({ ServerID, event }) { if(ServerID) { __servers__.value = ServerID; } - }); + }) + .catch(error => { LoadingAnimation(); new Notification('error', error); teststatus.innerHTML = MARKERS.no; __save__.innerHTML = 'Save ' + MARKERS.no; }); } function getPlexConnections(server) { @@ -612,12 +636,16 @@ function getOptionValues() { } }); - let COM = options.UseLZW; + let COM = options.UseLZW, + DEF = options.__defaults == 'true'; for(let key in __caught) __caught[key] = __caught[key].filter(id => id).slice(0, (COM? 200: 100)).sort(); - __theme = __theme.filter(v => v); + // if(options.__theme) + // __theme = JSON.parse(options.__theme); + // + // __theme = __theme.filter(v => v); let _c = JSON.stringify(__caught), _t = JSON.stringify(__theme); @@ -640,6 +668,7 @@ function performOmbiLogin({ event }) { s.title = ''; __servers__.innerHTML = ''; __save__.disabled = true; + __save__.innerHTML = 'Save ' + MARKERS.maybe; LoadingAnimation(true); let APIURL = `${ l }api/v1/`, @@ -748,12 +777,14 @@ function performOmbiLogin({ event }) { } __save__.disabled = false; + __save__.innerHTML = 'Save ' + MARKERS.yes; } else { /* Plex either doesn't exist, or is disabled */ new Notification('error', 'Error getting Plex details from Ombi'); + __save__.innerHTML = 'Save ' + MARKERS.no; } } ) - .catch( error => { new Notification('error', error); throw error } ); + .catch(error => { LoadingAnimation(); new Notification('error', error); __save__.innerHTML = 'Save ' + MARKERS.no; }); } function performOmbiTest({ refreshing = false, event }) { @@ -765,7 +796,7 @@ function performOmbiTest({ refreshing = false, event }) { enabled = refreshing? $('#using-ombi'): $$('#using-ombi'), inusestatus = [...$('[using="ombi"]', true)]; - teststatus.textContent = '?'; + teststatus.innerHTML = MARKERS.maybe; options.ombiURLRoot = url = path.value = options.ombiURLRoot.replace(/^(\:\d+)/, 'localhost$1').replace(/^(?!^http(s)?:)/, 'http$1://').replace(/\/+$/, ''); inusestatus.map(e => e.setAttribute('in-use', false)); LoadingAnimation(true); @@ -779,38 +810,40 @@ function performOmbiTest({ refreshing = false, event }) { __caught.imdb.push(item.imdbId); __caught.tmdb.push(item.theMovieDbId); }); - }); - - fetch(`${ url }/api/v1/Request/tv`) - .then(r => r.json()) - .then(json => { - json.map(item => { - __caught.imdb.push(item.imdbId); - __caught.tvdb.push(item.tvDbId); - }); - }); - fetch(`${ url }/api/v1/Status`, headers) - .then( response => response.text() ) - .then( status => { - LoadingAnimation(); - if (!status || !status.length) throw new Error('Unable to communicate with Ombi'); - - if ((status = +status) >= 200 && status < 400) { - teststatus.textContent = '!'; - enabled.checked = teststatus.classList = true; - enabled.parentElement.removeAttribute('disabled'); - inusestatus.map(e => e.setAttribute('in-use', true)); - } else { - teststatus.textContent = '!'; - enabled.checked = teststatus.classList = false; - enabled.parentElement.setAttribute('disabled'); - inusestatus.map(e => e.setAttribute('in-use', false)); - - throw new Error(`Ombi error [${ status }]`); - } - } ) - .catch( error => { new Notification('error', error) } ); + fetch(`${ url }/api/v1/Request/tv`) + .then(r => r.json()) + .then(json => { + json.map(item => { + __caught.imdb.push(item.imdbId); + __caught.tvdb.push(item.tvDbId); + }); + }) + .catch(error => { LoadingAnimation(); new Notification('error', error); teststatus.innerHTML = MARKERS.no }); + + fetch(`${ url }/api/v1/Status`, headers) + .then( response => response.text() ) + .then( status => { + LoadingAnimation(); + if (!status || !status.length) throw new Error('Unable to communicate with Ombi'); + + if ((status = +status) >= 200 && status < 400) { + teststatus.innerHTML = MARKERS.yes; + enabled.checked = teststatus.classList = true; + enabled.parentElement.removeAttribute('disabled'); + inusestatus.map(e => e.setAttribute('in-use', true)); + } else { + teststatus.innerHTML = MARKERS.no; + enabled.checked = teststatus.classList = false; + enabled.parentElement.setAttribute('disabled'); + inusestatus.map(e => e.setAttribute('in-use', false)); + + throw new Error(`Ombi error [${ status }]`); + } + }) + .catch(error => { LoadingAnimation(); new Notification('error', error); teststatus.innerHTML = MARKERS.no }); + }) + .catch(error => { LoadingAnimation(); new Notification('error', error); teststatus.innerHTML = MARKERS.no }); } catch(error) { LoadingAnimation(); @@ -860,7 +893,7 @@ function performWatcherTest({ QualityProfileID = 'Default', refreshing = false, inusestatus = [...$('[using="watcher"]', true)]; quality.innerHTML = ''; - teststatus.textContent = '?'; + teststatus.innerHTML = MARKERS.maybe; storagepath.value = '[Empty]'; options.watcherURLRoot = url = path.value = options.watcherURLRoot.replace(/^(\:\d+)/, 'localhost$1').replace(/^(?!^http(s)?:)/, 'http$1://').replace(/\/+$/, ''); inusestatus.map(e => e.setAttribute('in-use', false)); @@ -873,7 +906,8 @@ function performWatcherTest({ QualityProfileID = 'Default', refreshing = false, __caught.imdb.push(item.movies.imdbid); __caught.tmdb.push(item.movies.tmdbid); }); - }); + }) + .catch(error => { LoadingAnimation(); new Notification('error', error); teststatus.innerHTML = MARKERS.no }); getWatcher(options, 'getconfig').then(configuration => { LoadingAnimation(); @@ -892,8 +926,7 @@ function performWatcherTest({ QualityProfileID = 'Default', refreshing = false, name }); - teststatus.textContent = '!'; - teststatus.classList = enabled.checked = !!profiles.length; + teststatus.innerHTML = MARKERS[+!(teststatus.classList = enabled.checked = !!profiles.length)]; inusestatus.map(e => e.setAttribute('in-use', enabled.checked)); if(!profiles.length) @@ -920,11 +953,11 @@ function performWatcherTest({ QualityProfileID = 'Default', refreshing = false, storagepath.value = path || '[Default Location]'; $('[data-option="watcherStoragePaths"i]').value = JSON.stringify(path || { path: '[Default Location]', id: 0 }); - }); + }) + .catch(error => { LoadingAnimation(); new Notification('error', error); teststatus.innerHTML = MARKERS.no }); } catch(error) { LoadingAnimation(); - - throw error; + new Notification('error', error); } } @@ -970,7 +1003,7 @@ function performRadarrTest({ QualityProfileID, StoragePath, refreshing = false, inusestatus = [...$('[using="radarr"]', true)]; quality.innerHTML = ''; - teststatus.textContent = '?'; + teststatus.innerHTML = MARKERS.maybe; storagepath.textContent = ''; options.radarrURLRoot = url = path.value = options.radarrURLRoot.replace(/^(\:\d+)/, 'localhost$1').replace(/^(?!^http(s)?:)/, 'http$1://').replace(/\/+$/, ''); inusestatus.map(e => e.setAttribute('in-use', false)); @@ -983,14 +1016,14 @@ function performRadarrTest({ QualityProfileID, StoragePath, refreshing = false, __caught.imdb.push(movie.imdbId); __caught.tmdb.push(movie.tmdbId); }); - }); + }) + .catch(error => { LoadingAnimation(); new Notification('error', error); teststatus.innerHTML = MARKERS.no }); getRadarr(options, 'profile').then(profiles => { LoadingAnimation(); if(!profiles) return new Notification('error', 'Failed to get Radarr configuration'); - teststatus.textContent = '!'; - teststatus.classList = enabled.checked = !!profiles.length; + teststatus.innerHTML = MARKERS[+!(teststatus.classList = enabled.checked = !!profiles.length)]; inusestatus.map(e => e.setAttribute('in-use', enabled.checked)); if(!profiles.length) @@ -1013,29 +1046,42 @@ function performRadarrTest({ QualityProfileID, StoragePath, refreshing = false, // Because the was reset, the original value is lost. - if(StoragePath) { - storagepath.value = StoragePath; - $('[data-option="__radarrStoragePath"i]').value = StoragePaths.indexOf(StoragePath.replace(/\\/g, '/')) + 1; - } - }); + let paths = []; + storagepaths.forEach(folder => { + let option = document.createElement('option'); + let { id, path } = folder; + + option.value = id; + option.textContent = path; + paths.push({ id, path }); + storagepath.appendChild(option); + }); + + $('[data-option="radarrStoragePaths"i]').value = JSON.stringify(paths); + + // Because the was reset, the original value is lost. if(QualityProfileID) $('[data-option="__sonarrQuality"i]').value = quality.value = QualityProfileID; - }); + }) + .catch(error => { LoadingAnimation(); new Notification('error', error); teststatus.innerHTML = MARKERS.no }); let StoragePaths = []; getSonarr(options, 'rootfolder').then(storagepaths => { - storagepaths.forEach(path => { + LoadingAnimation(); + if(!storagepaths) return new Notification('error', 'Failed to get Sonarr configuration'); + + teststatus.innerHTML = MARKERS[+!(teststatus.classList = enabled.checked = !!storagepaths.length)]; + inusestatus.map(e => e.setAttribute('in-use', enabled.checked)); + + if(!storagepaths.length) + return teststatus.title = 'Failed to communicate with Sonarr'; + enabled.parentElement.removeAttribute('disabled'); + + let paths = []; + storagepaths.forEach(folder => { let option = document.createElement('option'); + let { id, path } = folder; - StoragePaths.push((option.value = option.textContent = path.path).replace(/\\/g, '/')); + option.value = id; + option.textContent = path; + paths.push({ id, path }); storagepath.appendChild(option); }); - $('[data-option="sonarrStoragePaths"i]').value = JSON.stringify(storagepaths); + $('[data-option="sonarrStoragePaths"i]').value = JSON.stringify(paths); // Because the was reset, the original value is lost. if(QualityProfileID) $('[data-option="__medusaQuality"i]').value = quality.value = QualityProfileID; - }); + }) + .catch(error => { LoadingAnimation(); new Notification('error', error); teststatus.innerHTML = MARKERS.no }); let StoragePaths = []; getMedusa(options, 'config').then(configuration => { @@ -1260,11 +1320,11 @@ function performMedusaTest({ QualityProfileID, StoragePath, refreshing = false, $('[data-option="__medusaStoragePath"i]').value = StoragePath; storagepath.selectedIndex = StoragePaths.indexOf(StoragePath.replace(/\\/g, '/').replace(/\/+$/, '')); } - }); + }) + .catch(error => { LoadingAnimation(); new Notification('error', error); teststatus.innerHTML = MARKERS.no }); } catch(error) { LoadingAnimation(); - - throw error; + new Notification('error', error); } }; @@ -1310,7 +1370,7 @@ function performSickBeardTest({ QualityProfileID, StoragePath, refreshing = fals inusestatus = [...$('[using="sickbeard"]', true)]; quality.innerHTML = ''; - teststatus.textContent = '?'; + teststatus.innerHTML = MARKERS.maybe; storagepath.textContent = ''; options.sickBeardURLRoot = url = path.value = options.sickBeardURLRoot.replace(/^(\:\d+)/, 'localhost$1').replace(/^(?!^http(s)?:)/, 'http$1://').replace(/\/+$/, ''); inusestatus.map(e => e.setAttribute('in-use', false)); @@ -1329,7 +1389,8 @@ function performSickBeardTest({ QualityProfileID, StoragePath, refreshing = fals shows.map(show => { __caught.tvdb.push(show.tvdbid); }); - }); + }) + .catch(error => { LoadingAnimation(); new Notification('error', error); teststatus.innerHTML = MARKERS.no }); getSickBeard(options, 'sb.getdefaults').then(configuration => { LoadingAnimation(); @@ -1341,8 +1402,7 @@ function performSickBeardTest({ QualityProfileID, StoragePath, refreshing = fals profiles = qualities[profiles]; - teststatus.textContent = '!'; - teststatus.classList = enabled.checked = !!profiles.length; + teststatus.innerHTML = MARKERS[+!(teststatus.classList = enabled.checked = !!profiles.length)]; inusestatus = [...$('[using="sickbeard"]', true)]; if(!profiles.length) @@ -1364,7 +1424,8 @@ function performSickBeardTest({ QualityProfileID, StoragePath, refreshing = fals // Because the +

@@ -2009,7 +2135,7 @@ for(let index = 0, length = builtin_array.length; builtinElement && index < leng `

${ title }

- +
@@ -2100,7 +2226,7 @@ for(let index = 0, length = plugin_array.length; pluginElement && index < length `

${ title }

- +
@@ -2124,7 +2250,7 @@ for(let index = 0, length = plugin_array.length; pluginElement && index < length `

${ title }

- +
@@ -2238,6 +2364,29 @@ addListener($('#erase_cache'), 'mouseup', event => { saveOptions(); }); +/* Erase ALL Settings */ +addListener($('#reset_settings'), 'mouseup', event => { + let options = JSON.stringify(getOptionValues()); + + $('[default]', true) + .forEach(el => { + let de = el.getAttribute('default'); + + if(el.type == 'checkbox') + el.checked = de === 'true'; + else if('contenteditable' in el.attributes) + el.innerHTML = de || ''; + else + el.value = de || ''; + }); + + Recall.CountEnabledSites(); + + new Notification('update', 'All settings have been reset'); + + RESETTING_SETTINGS = true; +}); + $('[type="range"]', true) .forEach((element, index, array) => { let sibling = element.nextElementSibling, @@ -2262,13 +2411,17 @@ $('.checkbox', true) switch(self.id.toLowerCase()) { /* Update the database when the option is toggled */ case 'use-lzw': - if(!self.checked) + let enabled; + + if(enabled = !self.checked) new Notification('update', 'Compressing data...', 3000, () => new Notification('update', 'Compressed', 3000), false); else new Notification('update', 'Decompressing data...', 3000, () => new Notification('update', 'Decompressed', 3000), false); let options = getOptionValues(); + Recall.ToggleConfigurationAvailability(enabled); + for(let name in options) if(/^__/.test(name)) { if(!self.checked) @@ -2311,26 +2464,27 @@ $('.test', true) $('[data-option^="theme:"i], [data-option^="theme:"i] + label', true) .forEach((element, index, array) => { - addListener(element, 'mouseup', async event => { + let UpdateTheme; + + addListener(element, 'mouseup', UpdateTheme = async event => { let self = traverse(event.target, element => /^theme:/i.test(element.dataset.option), true), R = RegExp; - let [a, b] = self.getAttribute('theme').split(/\s*:\s*/).filter(v => v), + let [a, b] = self.getAttribute('theme').split(/^([^]+):([^]+?)$/).filter(v => v), value = `${self.dataset.option.replace(/^theme:/i, '')}-${b}`; if(/^(get|read|for)$/i.test(a)) - __theme.push(`${ value }=${ self.value }`) + __theme[value] = (self.value == 'true'? true: self.value == 'false'? false: self.value); else if(/^(checkbox)$/i.test(self.type) && (self.checked + '') != a) // backwards; fires late - __theme.push(value); - else if(/^(text|input|button|\B)$/i.test(self.type) && R(self.value + '', 'i').test(a)) - __theme.push(value); + __theme[value] = JSON.parse(a); + else if(/^(text|input|button|\B)$/i.test(self.type) && R(a, 'i').test(self.value)) + __theme[value] = self.value; else - __theme = __theme.filter(v => v != value); - - /* Get rid of repeats */ - // __theme = __theme.join('\u0000').replace(/([\w\-]+\=)([^\u0000]+?)\u0000\1[^\u0000]+?/g, ($0, $1, $2, $$, $_) => $1 + $2); + delete __theme[value]; }); + + setTimeout(() => UpdateTheme({ target: element }), 1000); }); let hold = document.createElement('summary'), @@ -2473,6 +2627,11 @@ $('[href^="#!/"]', true) }; }); +$('[id$="_test_status"]', true) + .forEach(element => { + element.innerHTML = MARKERS.maybe; + }); + // CORS exception: SecurityError // MUST be { window }, never { top } let { hash } = window.location; @@ -2493,7 +2652,7 @@ if(hash.length > 1) /* #!/SETTING[/SUB-SETTING] * #!/radarr - * #!/advance-settings/api-keys + * #!/advanced-settings/api-keys */ case '': break; @@ -2505,7 +2664,7 @@ if(hash.length > 1) /* Functions that require some time */ let Recall = { - '@auto': {}, // run at 100ms, and be recallable + '@auto': {}, // run at 100ms '@0sec': {}, // run at 1ms '@1sec': {}, // run at 1000ms }; @@ -2574,15 +2733,125 @@ Recall['@0sec'].SetVersionInfo = async() => { } if(DM) - await fetch('https://api.github.com/repos/SpaceK33z/web-to-plex/releases') + await fetch('https://api.github.com/repos/webtoplex/browser-extension/releases') .then(response => response.json()) .then(versions => useVer(versions[0])); else - await fetch('https://api.github.com/repos/SpaceK33z/web-to-plex/releases/latest') + await fetch('https://api.github.com/repos/webtoplex/browser-extension/releases/latest') .then(response => response.json()) .then(version => useVer(version)); }; +/* Get the user's IP address */ +Recall['@auto'].GetIPAddress = async() => { + let self = $('#ip-address'), + teststatus = $('#proxy_test_status'), + options = getOptionValues(); + + + self.innerHTML = 'Loading...'; + teststatus.innerHTML = MARKERS.maybe; + + let proxy = HandleProxySettings(options); + + if(proxy.enabled) { + let { url, headers } = proxy, + tor = 'https://check.torproject.org'; + + headers = HandleProxyHeaders(headers, tor); + + if(/(^https?:\/\/)(?!localhost|127\.0\.0\.1(?:\/8)?|::1(?:\/128)?|:\d+)\b/i.test(url)) { + url = url + .replace(/\{b(ase-?)?64-url\}/gi, btoa(tor)) + .replace(/\{enc(ode)?-url\}/gi, encodeURIComponent(tor)) + .replace(/\{(raw-)?url\}/gi, tor); + } else { + self.innerHTML = ''; + teststatus.innerHTML = MARKERS.no; + + throw new SyntaxError('Unable to proxy: The URL must be a public (HTTPS/HTTP) address'); + } + + await fetch(url, { mode: 'cors', headers }) + .then(results => results.text()) + .then(text => { + let DOM = new DOMParser, + html = DOM.parseFromString(text, 'text/html'), + strong = $('.content strong', false, html), + IPAddress; + + if(strong) + IPAddress = strong.textContent; + else if(/([\d\.]{7,15})/.test(html.body.textContent)) + IPAddress = RegExp.$1; + else + IPAddress = ''; + + self.innerHTML = IPAddress; + teststatus.innerHTML = MARKERS[+!IPAddress]; + }) + .catch(error => { + self.innerHTML = ''; + teststatus.innerHTML = MARKERS.no; + + new Notification('error', error); + }); + } else { + await fetch('https://check.torproject.org', { mode: 'cors' }) + .then(results => results.text()) + .then(text => { + let DOM = new DOMParser, + html = DOM.parseFromString(text, 'text/html'), + strong = $('.content strong', false, html), + IPAddress; + + if(strong) + IPAddress = strong.textContent; + else if(/([\d\.]{7,15})/.test(html.body.textContent)) + IPAddress = RegExp.$1; + else + IPAddress = ''; + + self.innerHTML = IPAddress; + teststatus.innerHTML = MARKERS[+!IPAddress]; + }) + .catch(error => { + self.innerHTML = ''; + teststatus.innerHTML = MARKERS.no; + + new Notification('error', error); + }); + } + + self.setAttribute('notice', `Public${proxy.enabled?' (Proxy)':''}`); +}; + +let ToggleConfigurationAvailabilityListener = false; +/* Setting the Configuration Data disabled state */ +Recall['@1sec'].ToggleConfigurationAvailability = (enabled = null) => { + if(enabled === null) + enabled = $('#use-lzw').checked; + + if(enabled) { + let parent = $('#json_data').parentElement; + + parent.setAttribute('disabled', ''); + + if(!ToggleConfigurationAvailabilityListener) + $('*:not([data-option])', true, parent).forEach(element => { + addListener(element, 'mousedown', event => { + let disabled = traverse(element, () => 'disabled' in element.attributes, false); + + if(disabled) + return event.preventDefault(); + }); + }); + ToggleConfigurationAvailabilityListener = true; + } else { + $('#json_data').parentElement.removeAttribute('disabled'); + } +}; + for(let func in Recall) { if(/^@/.test(func)) { let f; @@ -2601,15 +2870,21 @@ for(let func in Recall) { for(let fn in Recall[func]) { f = Recall[func][fn]; + Recall[fn] = f; setTimeout(f, 1); } break; - case '@1sec': + default: + /^@(\d+)sec$/i.test(func); + + let time = +RegExp.$1; + for(let fn in Recall[func]) { f = Recall[func][fn]; - setTimeout(f, 1000); + Recall[fn] = f; + setTimeout(f, time * 1000); } break; } @@ -2949,3 +3224,5 @@ function restoreSelection({ anchor, focus }, editor, key) { selection.setBaseAndExtent(nodes.anchor, index.anchor, nodes.focus, index.focus); } + +addListener($('#test-proxy-settings'), 'click', Recall.GetIPAddress); diff --git a/src/plugn.js b/src/plugn.js index d3aea88..70062b9 100644 --- a/src/plugn.js +++ b/src/plugn.js @@ -257,7 +257,15 @@ async function prepare({ code, alias, type, allowed, url }) { Type = type.replace(/^\w/, ($0, $$, $_) => $0.toUpperCase()); let org = url.origin, - ali = TLDHost(url.host); + ali = TLDHost(url.host), + runOnInit = ['(null)']; + + let funcs = { + minions: PLUGN_CONFIGURATION.UseMinions, + }; + for(let func in funcs) + runOnInit.push(`(${ funcs[func] } && ${ type }.${ func } instanceof Function? ${ type }.${ func }(): null)`); + runOnInit = runOnInit.join(','); let { authorized, ...A } = await GetAuthorization(alias); @@ -294,7 +302,7 @@ ${ ${ code .replace(/\/\/+\s*"([^\"\n\f\r\v]+?)"\s*requires?\:?\s*(.+)/i, ($0, $1, $2, $$, $_) => - `;(async() => await Require("${ $2 }", "${ alias }", "${ $1 }", "${ instance }"))();` + `;(async() => await Require("cache,${ $2 }", "${ alias }", "${ $1 }", "${ instance }"))();` ) .replace(/\b(chrome|browser)\.storage\.(sync|local|managed)\.?/g, ($0, $1, $2, $$, $_) => `;console.warn("This ${ type } attempted to access <${ $1 }.storage.${ $2 }>; use , , and instead.");` @@ -334,11 +342,17 @@ return ( /* "ready" is a sync (normal) function */ ${ type }.ready() )? - ${ type }.init( ${ Type }ReadyState ): + ( + ${ runOnInit }, + ${ type }.init( ${ Type }ReadyState ) + ): /* Injected ${ type } isn't ready */ (${ type }.timeout || 1000): /* Injected ${ type } doesn't have the "ready" property */ - ${ type }.init(): + ( + ${ runOnInit }, + ${ type }.init() + ): /* Injected ${ type } isn't properly structured */ (console.warn("The ${ type } (${ alias }) is incorrectly structured. Could not find required function ${ type }.init"), -1): /* URL doesn't match pattern */ @@ -358,8 +372,13 @@ let handle = async(results, tabID, instance, script, type) => { results = await results; + let extURL = url => chrome.extension.getURL(url); + /* Always display a pretty button */ - chrome.tabs.insertCSS(tabID, { file: 'sites/common.css' }); + chrome.tabs.insertCSS(tabID, { cssOrigin: 'user', file: 'sites/common.css' }); + chrome.tabs.insertCSS(tabID, { cssOrigin: 'user', file: 'sites/theme.css' }); + chrome.tabs.insertCSS(tabID, { cssOrigin: 'user', file: 'sites/glyphs.css' }); + chrome.tabs.insertCSS(tabID, { cssOrigin: 'user', file: 'sites/colors.css' }); if((!results || !results[0] || !instance) && !FOUND[instance]) try { @@ -483,6 +502,10 @@ let tabchange = async tabs => { chrome.runtime.getURL(`cloud/plugin/${ js }.js`): `https://webtoplex.github.io/web/${ type }s/${ js }.js`; + let style = (PLUGN_DEVELOPER)? + chrome.runtime.getURL(`sites/${ js }/index.css`): + `https://webtoplex.github.io/web/styles/${ js }.css`; + await fetch(file, { mode: 'cors' }) .then(response => response.text()) .then(async code => { @@ -495,6 +518,10 @@ let tabchange = async tabs => { }) .then(() => running.push(id, instance)) .catch(error => { throw error }); + + await fetch(style, { mode: 'cors' }) + .then(response => response.text()) + .then(async code => chrome.tabs.insertCSS({ code })); }; // listen for message event @@ -540,6 +567,10 @@ chrome.runtime.onMessage.addListener(processMessage = async(request = {}, sender chrome.runtime.getURL(`cloud/plugin/${ plugin }.js`): `https://webtoplex.github.io/web/${ _type }s/${ options[_type] }.js`; + let style = (PLUGN_DEVELOPER)? + chrome.runtime.getURL(`sites/${ options[_type] }/index.css`): + `https://webtoplex.github.io/web/styles/${ options[_type] }.css`; + let { authorized, ...A } = await GetAuthorization(options[_type]); try { @@ -602,6 +633,9 @@ chrome.runtime.onMessage.addListener(processMessage = async(request = {}, sender break; case 'GRANT_PERMISSION': + if(!options[_type]) + return false; + await Save(`has/${ options[_type] }`, options.allowed); await Save(`get/${ options[_type] }`, options.permissions); break; @@ -629,6 +663,10 @@ chrome.runtime.onMessage.addListener(processMessage = async(request = {}, sender return false; } + await fetch(style, { mode: 'cors' }) + .then(response => response.text()) + .then(async code => browser.tabs.insertCSS({ code })); + return true; } catch(error) { PLUGN_TERMINAL.error(error); diff --git a/src/sites/amazon/index.css b/src/sites/amazon/index.css index e69de29..dac83ab 100644 --- a/src/sites/amazon/index.css +++ b/src/sites/amazon/index.css @@ -0,0 +1,32 @@ +.web-to-plex-minion { + margin-right: 8px; + color: #fff!important; + margin-bottom: 8px!important; + line-height: 2!important; + text-decoration: none!important; + width: auto!important; +} + +.web-to-plex-minion.wtp--download { + background: linear-gradient(180deg, #f67e56, #f45a26)!important; +} + +.web-to-plex-minion.wtp--download:hover { + background: linear-gradient(180deg, #ff8960, #fd6430)!important; +} + +.web-to-plex-minion.wtp--found { + background: linear-gradient(180deg, #f7dfa5, #ca7c1f)!important; +} + +.web-to-plex-minion.wtp--found:hover { + background: linear-gradient(180deg, #f7dfa5, #f8c022)!important; +} + +#tt--0-0 { + background: linear-gradient(180deg, #70767d 0, #696f78 0, #3d444e 100%)!important; +} + +#tt--0-0:hover { + background: linear-gradient(180deg, #70767d 0, #696f78 0, #3d444e 100%)!important; +} diff --git a/src/sites/colors.css b/src/sites/colors.css new file mode 100644 index 0000000..5c66061 --- /dev/null +++ b/src/sites/colors.css @@ -0,0 +1,572 @@ +:root { + --light-black: #222; + --black: #282828; + --dark-black: #000; + + --light-blue: #197bcc; + --blue: #265af4; + --dark-blue: #2a2aff; + + --light-grey: #999; + --grey: #666; + --dark-grey: #3f4245; + + --green: #80f080; + + --light-orange: #e59029; + --orange: #cc7b19; + --dark-orange: #f45a26; + + --light-red: #f44; + --red: #ff2a2a; + --dark-red: #d11; + + --light-white: #fff; + --white: #eee; + + --disabled: #909090ee; + --half-black: #0008; + --half-green: #80f08088; + --half-grey: #8888; + --half-white: #fff8; + --quarter-black: #0004; + --quarter-grey: #8884; + --quarter-white: #fff4; + --transparent: #0000; +} + +/* Text Color */ +[light-black]:not([gradient]) { + color: var(--light-black) +} + +[black]:not([gradient]) { + color: var(--black) +} + +[dark-black]:not([gradient]) { + color: var(--dark-black) +} + +[light-blue]:not([gradient]) { + color: var(--light-blue) +} + +[blue]:not([gradient]) { + color: var(--blue) +} + +[dark-blue]:not([gradient]) { + color: var(--dark-blue) +} + +[light-grey]:not([gradient]) { + color: var(--light-grey) +} + +[grey]:not([gradient]) { + color: var(--grey) +} + +[dark-grey]:not([gradient]) { + color: var(--dark-grey) +} + +[green]:not([gradient]) { + color: var(--green) +} + +[light-orange]:not([gradient]) { + color: var(--light-orange) +} + +[orange]:not([gradient]) { + color: var(--orange) +} + +[dark-orange]:not([gradient]) { + color: var(--dark-orange) +} + +[light-red]:not([gradient]) { + color: var(--light-red) +} + +[red]:not([gradient]) { + color: var(--red) +} + +[dark-red]:not([gradient]) { + color: var(--dark-red) +} + +[light-white]:not([gradient]) { + color: var(--light-white) +} + +[white]:not([gradient]) { + color: var(--white) +} + +[disabled]:not([gradient]) { + color: var(--disabled) +} + +[half-black]:not([gradient]) { + color: var(--half-black) +} + +[half-green]:not([gradient]) { + color: var(--half-green) +} + +[half-grey]:not([gradient]) { + color: var(--half-grey) +} + +[half-white]:not([gradient]) { + color: var(--half-white) +} + +[quarter-black]:not([gradient]) { + color: var(--quarter-black) +} + +[quarter-grey]:not([gradient]) { + color: var(--quarter-grey) +} + +[quarter-white]:not([gradient]) { + color: var(--quarter-white) +} + +[transparent]:not([gradient]) { + color: var(--transparent) +} + +/* Text Color (Hover) */ +[light-black\$]:hover { + color: var(--light-black) +} + +[black\$]:hover { + color: var(--black) +} + +[dark-black\$]:hover { + color: var(--dark-black) +} + +[light-blue\$]:hover { + color: var(--light-blue) +} + +[blue\$]:hover { + color: var(--blue) +} + +[dark-blue\$]:hover { + color: var(--dark-blue) +} + +[light-grey\$]:hover { + color: var(--light-grey) +} + +[grey\$]:hover { + color: var(--grey) +} + +[dark-grey\$]:hover { + color: var(--dark-grey) +} + +[green\$]:hover { + color: var(--green) +} + +[light-orange\$]:hover { + color: var(--light-orange) +} + +[orange\$]:hover { + color: var(--orange) +} + +[dark-orange\$]:hover { + color: var(--dark-orange) +} + +[light-red\$]:hover { + color: var(--light-red) +} + +[red\$]:hover { + color: var(--red) +} + +[dark-red\$]:hover { + color: var(--dark-red) +} + +[light-white\$]:hover { + color: var(--light-white) +} + +[white\$]:hover { + color: var(--white) +} + +[disabled\$]:hover { + color: var(--disabled) +} + +[half-black\$]:hover { + color: var(--half-black) +} + +[half-green\$]:hover { + color: var(--half-green) +} + +[half-grey\$]:hover { + color: var(--half-grey) +} + +[half-white\$]:hover { + color: var(--half-white) +} + +[quarter-black\$]:hover { + color: var(--quarter-black) +} + +[quarter-grey\$]:hover { + color: var(--quarter-grey) +} + +[quarter-white\$]:hover { + color: var(--quarter-white) +} + +[transparent\$]:hover { + color: var(--transparent) +} + +/* Background Color */ +[light-black-bg], [light-black][gradient] { + background: var(--light-black) +} + +[black-bg], [black][gradient] { + background: var(--black) +} + +[dark-black-bg], [dark-black][gradient] { + background: var(--dark-black) +} + +[light-blue-bg], [light-blue][gradient] { + background: var(--light-blue) +} + +[blue-bg], [blue][gradient] { + background: var(--blue) +} + +[dark-blue-bg], [dark-blue][gradient] { + background: var(--dark-blue) +} + +[light-grey-bg], [light-grey][gradient] { + background: var(--light-grey) +} + +[grey-bg], [grey][gradient] { + background: var(--grey) +} + +[dark-grey-bg], [dark-grey][gradient] { + background: var(--dark-grey) +} + +[green], [green][gradient] { + background: var(--green) +} + +[light-orange-bg], [light-orange][gradient] { + background: var(--light-orange) +} + +[orange-bg], [orange][gradient] { + background: var(--orange) +} + +[dark-orange-bg], [dark-orange][gradient] { + background: var(--dark-orange) +} + +[light-red-bg], [light-red][gradient] { + background: var(--light-red) +} + +[red-bg], [red][gradient] { + background: var(--red) +} + +[dark-red-bg], [dark-red][gradient] { + background: var(--dark-red) +} + +[light-white-bg], [light-white][gradient] { + background: var(--light-white) +} + +[white-bg], [white][gradient] { + background: var(--white) +} + +[disabled-bg] { + background: var(--disabled) +} + +[half-black-bg] { + background: var(--half-black) +} + +[half-green-bg] { + background: var(--half-green) +} + +[half-grey-bg] { + background: var(--half-grey) +} + +[half-white-bg] { + background: var(--half-white) +} + +[quarter-black-bg] { + background: var(--quarter-black) +} + +[quarter-grey-bg] { + background: var(--quarter-grey) +} + +[quarter-white-bg] { + background: var(--quarter-white) +} + +[transparent-bg] { + background: var(--transparent) +} + +/* Background Color (Hover) */ +[light-black-bg\$]:hover { + background: var(--light-black) +} + +[black-bg\$]:hover { + background: var(--black) +} + +[dark-black-bg\$]:hover { + background: var(--dark-black) +} + +[light-blue-bg\$]:hover { + background: var(--light-blue) +} + +[blue-bg\$]:hover { + background: var(--blue) +} + +[dark-blue-bg\$]:hover { + background: var(--dark-blue) +} + +[light-grey-bg\$]:hover { + background: var(--light-grey) +} + +[grey-bg\$]:hover { + background: var(--grey) +} + +[dark-grey-bg\$]:hover { + background: var(--dark-grey) +} + +[green-bg\$]:hover { + background: var(--green) +} + +[light-orange-bg\$]:hover { + background: var(--light-orange) +} + +[orange-bg\$]:hover { + background: var(--orange) +} + +[dark-orange-bg\$]:hover { + background: var(--dark-orange) +} + +[light-red-bg\$]:hover { + background: var(--light-red) +} + +[red-bg\$]:hover { + background: var(--red) +} + +[dark-red-bg\$]:hover { + background: var(--dark-red) +} + +[light-white-bg\$]:hover { + background: var(--light-white) +} + +[white-bg\$]:hover { + background: var(--white) +} + +[disabled-bg\$]:hover { + background: var(--disabled) +} + +[half-black-bg\$]:hover { + background: var(--half-black) +} + +[half-green-bg\$]:hover { + background: var(--half-green) +} + +[half-grey-bg\$]:hover { + background: var(--half-grey) +} + +[half-white-bg\$]:hover { + background: var(--half-white) +} + +[quarter-black-bg\$]:hover { + background: var(--quarter-black) +} + +[quarter-grey-bg\$]:hover { + background: var(--quarter-grey) +} + +[quarter-white-bg\$]:hover { + background: var(--quarter-white) +} + +[transparent-bg\$]:hover { + background: var(--transparent) +} + +/* Gradient Text (Darken) */ +[light-black][gradient="darken"i] { + background: linear-gradient(var(--light-black), var(--black)) +} + +[black][gradient="darken"i] { + background: linear-gradient(var(--black), var(--dark-black)) +} + +[light-blue][gradient="darken"i] { + background: linear-gradient(var(--light-blue), var(--blue)) +} + +[blue][gradient="darken"i] { + background: linear-gradient(var(--blue), var(--dark-blue)) +} + +[light-grey][gradient="darken"i] { + background: linear-gradient(var(--light-grey), var(--grey)) +} + +[grey][gradient="darken"i] { + background: linear-gradient(var(--grey), var(--dark-grey)) +} + +[light-orange][gradient="darken"i] { + background: linear-gradient(var(--light-orange), var(--orange)) +} + +[orange][gradient="darken"i] { + background: linear-gradient(var(--orange), var(--dark-orange)) +} + +[light-red][gradient="darken"i] { + background: linear-gradient(var(--light-red), var(--red)) +} + +[red][gradient="darken"i] { + background: linear-gradient(var(--red), var(--dark-red)) +} + +[light-white][gradient="darken"i] { + background: linear-gradient(var(--light-white), var(--white)) +} + +/* Gradient Text (Lighten) */ +[black][gradient="lighten"i] { + background: linear-gradient(var(--black), var(--light-black)) +} + +[dark-black][gradient="lighten"i] { + background: linear-gradient(var(--dark-black), var(--black)) +} + +[blue][gradient="lighten"i] { + background: linear-gradient(var(--blue), var(--light-blue)) +} + +[dark-blue][gradient="lighten"i] { + background: linear-gradient(var(--dark-blue), var(--blue)) +} + +[grey][gradient="lighten"i] { + background: linear-gradient(var(--grey), var(--light-grey)) +} + +[dark-grey][gradient="lighten"i] { + background: linear-gradient(var(--dark-grey), var(--grey)) +} + +[orange][gradient="lighten"i] { + background: linear-gradient(var(--orange), var(--light-orange)) +} + +[dark-orange][gradient="lighten"i] { + background: linear-gradient(var(--dark-orange), var(--orange)) +} + +[red][gradient="lighten"i] { + background: linear-gradient(var(--red), var(--light-red)) +} + +[dark-red][gradient="lighten"i] { + background: linear-gradient(var(--dark-red), var(--red)) +} + +[white][gradient="lighten"i] { + background: linear-gradient(var(--white), var(--light-white)) +} + +/* All Gradient Properties */ +[gradient] { + background-clip: text; + -moz-background-clip: text; + -webkit-background-clip: text; + + color: #0000; + -webkit-text-fill-color: #0000; +} diff --git a/src/sites/common.css b/src/sites/common.css index 5657b4f..d493cb5 100644 --- a/src/sites/common.css +++ b/src/sites/common.css @@ -1,98 +1,123 @@ /** Common CSS - * Web to Plex - */ - /* Basic/Global Styling */ - [class*="web-to-plex"]::-webkit-scrollbar, [class*="web-to-plex"]::-moz-scrollbar { - width: 10px !important; - } - - [class*="web-to-plex"]::-webkit-scrollbar-thumb, [class*="web-to-plex"]::-moz-scrollbar-thumb { - min-height: 50px !important; - background: rgba(255, 255, 255, 0.15) !important; - border: 2px solid rgba(0, 0, 0, 0) !important; - border-radius: 8px !important; - background-clip: padding-box !important; - } - - [class*="web-to-plex"]::-webkit-scrollbar-track, [class*="web-to-plex"]::-moz-scrollbar-track { - background: #0000 !important; - } - - [class*="web-to-plex"]::-webkit-input-placeholder, [class*="web-to-plex"]::-moz-placeholder, [class*="web-to-plex"]:-moz-placeholder { - color: #999 !important; - } - - [class*="web-to-plex"] input[type="text"], [class*="web-to-plex"] input[type="password"], [class*="web-to-plex"] select { - width: 30vw !important; - line-height: 1.5em !important; - transition: background 0.2s !important; - display: block !important; - height: 38px !important; - padding: 6px 12px !important; - font-size: 16px !important; - color: #eee !important; - vertical-align: middle; - background: rgba(255, 255, 255, 0.25) !important; - border: 3px solid rgba(0, 0, 0, 0) !important; - border-radius: 3px !important; - font-family: inherit !important; - margin: 0 !important; - } - - [class*="web-to-plex"] select { - margin-left: 10px !important; - font-size: 16px !important; - line-height: inherit !important; - text-transform: none !important; - } - - [class*="web-to-plex"] option { - background: #3f4245 !important; - } - - [class*="web-to-plex"] button { - padding: 10px 18px !important; - font-size: 16px !important; - line-height: 1.33 !important; - border-radius: 3px !important; - font-family: inherit !important; - text-transform: uppercase !important; - border: 0 !important; - box-shadow: none !important; - position: relative !important; - overflow: hidden !important; - color: #fff; - background: #cc7b19; - margin-bottom: 0 !important; - font-weight: 400 !important; - vertical-align: middle; - cursor: pointer !important; - white-space: nowrap; - user-select: none; - transition: all 0.1s !important; - } +* Web to Plex +*/ + +@charset "utf-8"; +@font-face { + font-family: "Plex"; + src: local(Plex), + url(//webtoplex.github.io/font/Plex.woff2) format('woff2'), + url(//webtoplex.github.io/font/Plex.woff) format('woff'); + font-weight: 400; + font-style: normal; +} + +@font-face { + font-family: "Plex-bold"; + src: local(Plex-bold), + url(//webtoplex.github.io/font/Plex.bold.woff2) format('woff2'), + url(//webtoplex.github.io/font/Plex.bold.woff) format('woff'); + font-weight: 400; + font-style: normal; +} + +/* Basic/Global Styling */ +*::-webkit-scrollbar { + width: 10px; +} + +*::-webkit-scrollbar-thumb { + min-height: 50px; + background: rgba(255, 255, 255, 0.15); + border: 2px solid rgba(0, 0, 0, 0); + border-radius: 8px; + background-clip: padding-box; +} - [class*="web-to-plex"] button:hover { - background: #e59029; - } +*::-webkit-scrollbar-track { + /* background: url(../img/noise.png) fixed, #3f4245 !important; */ +} + +*::placeholder { + color: #999 !important; +} - [class*="web-to-plex"] input::placeholder, [class*="web-to-plex"] input:placeholder { +*::-webkit-input-placeholder { color: #999 !important; - } +} + +/* Web to Plex */ +[class*="web-to-plex"] input[type="text"], [class*="web-to-plex"] input[type="password"], [class*="web-to-plex"] select { + width: 30vw !important; + line-height: 1.5em !important; + transition: background 0.2s !important; + display: block !important; + height: 38px !important; + padding: 6px 12px !important; + font-size: 16px !important; + color: var(--white) !important; + vertical-align: middle; + background: rgba(255, 255, 255, 0.25) !important; + border: 3px solid rgba(0, 0, 0, 0) !important; + border-radius: 3px !important; + font-family: inherit !important; + margin: 0 !important; +} + +[class*="web-to-plex"] select { + margin-left: 10px !important; + font-size: 16px !important; + line-height: inherit !important; + text-transform: none !important; +} + +[class*="web-to-plex"] option { + background: var(--light-grey) !important; +} + +[class*="web-to-plex"] button { + padding: 10px 18px !important; + font-size: 16px !important; + line-height: 1.33 !important; + border-radius: 3px !important; + font-family: inherit !important; + text-transform: uppercase !important; + border: 0 !important; + box-shadow: none !important; + position: relative !important; + overflow: hidden !important; + color: var(--light-white); + background: var(--orange); + margin-bottom: 0 !important; + font-weight: 400 !important; + vertical-align: middle; + cursor: pointer !important; + white-space: nowrap; + user-select: none; + transition: all 0.1s !important; +} - [class*="web-to-plex"][disabled], [class*="web-to-plex"] [disabled] { - cursor: not-allowed !important; - color: #909090EE !important; - } +[class*="web-to-plex"] button:hover { + background: var(--light-orange); +} + +[class*="web-to-plex"] input::placeholder, [class*="web-to-plex"] input:placeholder { + color: var(--dark-grey) !important; +} + +[class*="web-to-plex"][disabled], [class*="web-to-plex"] [disabled] { + cursor: not-allowed !important; + color: var(--disabled) !important; +} /* Web to Plex notifications */ .web-to-plex-notification { - background: #F45A26 !important; + background: var(--dark-orange) !important; border-radius: 4px !important; - color: #FFF !important; + color: var(--light-white) !important; cursor: pointer !important; display: block !important; - font-family: arial, verdana, sans-serif !important; + font-family: "Plex", "Open Sans Regular", "Helvetica Neue", Helvetica, Arial, sans-serif, system !important; font-size: 20px !important; text-align: center !important; @@ -108,31 +133,31 @@ /* Web to Plex general information notifications */ .web-to-plex-notification.info { - background: #666 !important; + background: var(--grey) !important; } /* Web to Plex update notifications */ .web-to-plex-notification.update { - background: #2A2AFF !important; + background: var(--dark-blue) !important; } /* Web to Plex success notifications */ .web-to-plex-notification.success { - background: #03BDF9 !important; + background: var(--light-blue) !important; } /* Web to Plex error/warning notifications */ .web-to-plex-notification.warning, .web-to-plex-notification.error { - background: #FF2A2A !important; + background: var(--red) !important; } /* Web to Plex prompts */ .web-to-plex-prompt { - background: #0008 !important; + background: var(--half-black) !important; box-sizing: border-box !important; - color: #eee !important; + color: var(--white) !important; display: block !important; - font-family: Open Sans Regular, Helvetica Neue, Helvetica, Arial, sans-serif !important; + font-family: "Plex", "Open Sans Regular", "Helvetica Neue", Helvetica, Arial, sans-serif, system !important; font-size: 14px !important; line-height: 24px !important; overflow: auto !important; @@ -149,10 +174,11 @@ } .web-to-plex-prompt-body { - background: #282828; + background: var(--black); border: 0 !important; border-radius: 3px !important; box-shadow: 0 5px 15px #0008 !important; + box-sizing: content-box !important; display: block !important; left: 20% !important; @@ -166,10 +192,10 @@ } .web-to-plex-prompt-header, .web-to-plex-prompt-footer { - background: #32323240 !important; + background: var(--quarter-grey) !important; border: 1px solid #0000 !important; box-sizing: border-box !important; - color: #eee !important; + color: var(--white) !important; font: inherit !important; font-size: 2em !important; line-height: initial !important; @@ -182,11 +208,11 @@ height: 65px !important; width: 100% !important; - -webkit-tap-highlight-color: #0000; + -webkit-tap-highlight-color: var(--transparent); } .web-to-plex-prompt-header { - border-bottom-color: #222 !important; + border-bottom-color: var(--light-black) !important; border-bottom-width: 1px !important; border-top-left-radius: 3px !important; border-top-right-radius: 3px !important; @@ -203,7 +229,7 @@ overflow-x: hidden !important; overflow-y: auto !important; - padding: 12px !important; + padding: 0 12px 5px !important; position: relative !important; top: 65px !important; @@ -211,10 +237,10 @@ } .web-to-plex-prompt-option { - background: #32323240 !important; + background: var(--quarter-grey) !important; border: 1px solid #202020 !important; border-radius: 3px !important; - color: #999 !important; + color: var(--light-grey) !important; display: block !important; text-align: left !important; @@ -225,22 +251,21 @@ } .web-to-plex-prompt-option.mutable { + cursor: pointer; max-width: 60% !important; } .web-to-plex-prompt-option.mutable > h2 { - background: #0000 !important; - color: inherit !important; - font-family: inherit !important; - font-size: initial !important; - text-align: inherit !important; + background: var(--transparent) !important; + color: inherit !important; + font-family: inherit !important; + font-size: initial !important; + text-align: inherit !important; - margin: inherit !important; + margin: inherit !important; } -.web-to-plex-prompt-option.mutable > .remove, .web-to-plex-prompt-option.mutable > .choose { - background: #ffffff40 !important; - border-radius: 3px !important; +.web-to-plex-prompt-option.mutable [glyph] { transition: all 0.1s !important; height: 30px !important; @@ -248,67 +273,67 @@ float: right !important; margin-right: -2% !important; - margin-top: -8% !important; + margin-top: -5% !important; padding: 0 !important; } -.web-to-plex-prompt-option.mutable > .remove:hover, .web-to-plex-prompt-option.mutable > .choose:hover { - background: #ffffff4d !important; +.web-to-plex-prompt-option.mutable.choose [glyph] { + color: var(--grey); } -.web-to-plex-prompt-option.mutable > .remove::after { - content: '\00d7' !important; +.web-to-plex-prompt-option.mutable.chosen:first-child { + border-color: var(--green) !important; } -.web-to-plex-prompt-option.mutable > .choose::after { - content: '\2218' !important; -} - -.web-to-plex-prompt-option.mutable:first-child:last-child > .choose, .web-to-plex-prompt-option.mutable.chosen > .choose { - background: #80F08088 !important; +.web-to-plex-prompt-option.mutable.chosen:first-child [glyph] { + color: var(--green); } .web-to-plex-prompt-option.mutable > .quality { - width: 50% !important; + width: 50% !important; } .web-to-plex-prompt-option.mutable > .location { - width: 90% !important; + width: 90% !important; } .web-to-plex-prompt-option.mutable > .location:last-child:not(:first-child) { - margin-top: 5px !important; + margin-top: 5px !important; } .web-to-plex-permission:after { - background: #0000; - border-radius: 3px; - border: 0; - color: #cc7b19; - content: "\29eb"; - display: inline-block; - font-size: 150%; - padding: 0; - text-align: center; + background: var(--transparent); + border-radius: 3px; + border: 0; + color: var(--orange); + content: "\29eb"; + display: inline-block; + font-size: 150%; + padding: 0; + text-align: center; - margin: 0; - position: absolute; - right: 3%; + margin: 0; + position: absolute; + right: 3%; - height: 2em; - width: 2em; + height: 2em; + width: 2em; } .web-to-plex-prompt-footer { text-align: right !important; border-bottom-left-radius: 3px !important; border-bottom-right-radius: 3px !important; - border-top-color: #222 !important; + border-top-color: var(--light-black) !important; border-top-width: 1px !important; bottom: 0 !important; } +.web-to-plex-prompt-footer > * { + vertical-align: text-bottom !important; +} + .web-to-plex-prompt-input { float: left !important; position: relative !important; @@ -321,338 +346,359 @@ } .web-to-plex-prompt-accept { - background: #cc7b19 !important; + background: var(--orange) !important; margin-left: 5px !important; } .web-to-plex-prompt-accept:hover { - background: #e59029 !important; + background: var(--light-orange) !important; } .web-to-plex-prompt-decline { - background: #ffffff40 !important; + background: var(--quarter-white) !important; } .web-to-plex-prompt-decline:hover { - background: #ffffff4d !important; + background: var(--half-white) !important; } /* Web to Plex buttons */ .web-to-plex-button [module] { - position: relative !important; + position: relative !important; } .web-to-plex-button * { - border: none !important; - text-transform: none !important; + border: none !important; + text-transform: none !important; } .web-to-plex-button { - background-color: #3F4245 !important; - border: none !important; - color: #FFF !important; - font-family: Open Sans Semibold, Helvetica Neue, Helvetica, Arial, sans-serif !important; - font-size: 1em !important; - font-weight: 100 !important; - text-align: center !important; - - bottom: 5px !important; - left: 5px !important; - padding: 10px !important; - position: fixed !important; - right: unset !important; - z-index: 999999 !important; - - min-height: 0 !important; - min-width: 0 !important; - height: 72px !important; - width: 180px !important; - - transition: all 0.3s ease !important; + background-color: var(--light-grey) !important; + border: none !important; + color: var(--light-white) !important; + display: block !important; + font-family: "Plex", "Open Sans Regular", "Helvetica Neue", Helvetica, Arial, sans-serif, system !important; + font-size: 1em !important; + font-weight: 100 !important; + text-align: center !important; + + bottom: 5px !important; + left: 5px !important; + padding: 10px !important; + position: fixed !important; + right: unset !important; + z-index: 999999 !important; + + min-height: 0 !important; + min-width: 0 !important; + height: 72px !important; + width: 180px !important; + + transition: all 0.3s ease !important; } .web-to-plex-button.hide { - display: initial !important; + display: initial !important; } .web-to-plex-button.hide:not(:hover), .web-to-plex-button.sleeper { - opacity: 0.1; + opacity: 0.1; } *:not(#plexit-bookmarklet-frame) ~ .web-to-plex-button { - margin-left: 0px !important; + margin-left: 0px; } #plexit-bookmarklet-frame ~ .web-to-plex-button { - margin-left: 280px !important; + margin-left: 280px !important; } *:not(#plexit-bookmarklet-frame) + .web-to-plex-button #plexit, #plexit-bookmarklet-frame + .web-to-plex-button #wtp-plexit { - display: none !important; + display: none !important; } .floating.web-to-plex-button { - border-radius: 50px !important; - box-shadow: 0 4px 10px 0 rgba(0, 0, 0, 0.3) !important; + border-radius: 50px !important; + box-shadow: 0 4px 10px 0 rgba(0, 0, 0, 0.3) !important; - height: 75px !important; - width: 75px !important; + height: 75px !important; + width: 75px !important; } .floating.web-to-plex-button::after { - content: ' ' !important; + content: ' ' !important; - background: #666 !important; - border: 1px solid #888 !important; - border-radius: 16px !important; + background: var(--grey) !important; + border: 1px solid #888 !important; + border-radius: 16px !important; - right: 0 !important; - top: 0 !important; - position: absolute !important; + right: 0 !important; + top: 0 !important; + position: absolute !important; - height: 16px !important; - width: 16px !important; + height: 16px !important; + width: 16px !important; - transition: background 0.4s linear !important; + transition: background 0.4s linear !important; } .floating.web-to-plex-button:not(.restarting):active, .floating.web-to-plex-button:not(.restarting):hover { - box-shadow: 1px 5px 20px 0 rgba(0, 0, 0, 0.6) !important; - cursor: pointer !important; + box-shadow: 1px 5px 20px 0 rgba(0, 0, 0, 0.6) !important; + cursor: pointer !important; } .floating.web-to-plex-button:focus { - outline: #0000 !important; + outline: #0000 !important; } .web-to-plex-button.wtp--download::after, .web-to-plex-button.wtp--download::before { - background: #265AF4 !important; + background: var(--blue) !important; } .web-to-plex-button.wtp--queued::after, .web-to-plex-button.wtp--queued::before { - background: #568AF4 !important; + background: var(--light-blue) !important; } .web-to-plex-button.wtp--found::after, .web-to-plex-button.wtp--found::before { - background: #F9BD03 !important; + background: var(--light-orange) !important; } .web-to-plex-button.wtp--error::after, .web-to-plex-button.wtp--error::before { - background: #FF2A2A !important; + background: var(--red) !important; } .web-to-plex-button::before { - content: ' ' !important; + content: ' ' !important; - background: #FFF6 !important; - border-radius: inherit !important; - display: none !important; + background: var(--half-white) !important; + border-radius: inherit !important; + display: none !important; - margin-top: -10px !important; - margin-left: -10px !important; + margin-top: -10px !important; + margin-left: -10px !important; - height: 75px !important; - width: 75px !important; + height: 75px !important; + width: 75px !important; - position: absolute !important; - z-index: 9999999 !important; + position: absolute !important; + z-index: 9999999 !important; } .web-to-plex-button.animate::before { - display: block !important; + display: block !important; - -webkit-transform: scale(0); - -moz-transform: scale(0); - -o-transform: scale(0); - transform: scale(0); + -webkit-transform: scale(0); + -moz-transform: scale(0); + -o-transform: scale(0); + transform: scale(0); - -webkit-animation: web-to-plex-ripple 0.5s linear; - -moz-animation: web-to-plex-ripple 0.5s linear; - -o-animation: web-to-plex-ripple 0.5s linear; - animation: web-to-plex-ripple 0.5s linear; + -webkit-animation: web-to-plex-ripple 0.5s linear; + -moz-animation: web-to-plex-ripple 0.5s linear; + -o-animation: web-to-plex-ripple 0.5s linear; + animation: web-to-plex-ripple 0.5s linear; } @-webkit-keyframes web-to-plex-ripple { - 100% { - opacity: 0; - -webkit-transform: scale(2.5); - } + 100% { + opacity: 0; + -webkit-transform: scale(2.5); + } } @-moz-keyframes web-to-plex-ripple { - 100% { - opacity: 0; - -moz-transform: scale(2.5); - } + 100% { + opacity: 0; + -moz-transform: scale(2.5); + } } @-o-keyframes web-to-plex-ripple { - 100% { - opacity: 0; - -o-transform: scale(2.5); - } + 100% { + opacity: 0; + -o-transform: scale(2.5); + } } @keyframes web-to-plex-ripple { - 100% { - opacity: 0; - transform: scale(2.5); - } + 100% { + opacity: 0; + transform: scale(2.5); + } } .web-to-plex-button.open, #plexit-bookmarklet-frame + .web-to-plex-button { - opacity: 1; + opacity: 1; - width: 350px !important; + width: 350px !important; } + .web-to-plex-button .list-name { - float: left !important; + float: left !important; } .web-to-plex-button ul { - margin: 0 !important; - padding-left: 0 !important; + margin: 0 !important; + padding-left: 0 !important; } .web-to-plex-button li { - display: inline-block !important; - list-style: none !important; + display: inline-block !important; + list-style: none !important; - margin: 0 !important; - padding: 5px !important; - vertical-align: bottom; + margin: 0 !important; + padding: 5px !important; + vertical-align: bottom; } .web-to-plex-button li > img { - display: inline !important; + display: inline !important; - margin-top: 0 !important; + margin-top: 0 !important; } .web-to-plex-button.hide.closed li:not(:first-child) { - display: none !important; + display: none !important; } *:not(#plexit-bookmarklet-frame) + .web-to-plex-button.closed .list-item { - float: left !important; - opacity: 0; - transition: opacity 0 !important; + float: left !important; + opacity: 0; + transition: opacity 0 !important; } .web-to-plex-button.open .list-item { - opacity: 1; - transition: opacity 2s !important; + opacity: 1; + transition: opacity 2s !important; } .web-to-plex-button.open li:hover [tooltip]::before, .web-to-plex-button.open [tooltip]:hover::before { - content: attr(tooltip) !important; + content: attr(tooltip) !important; + display: inline-block; - background: #3F424599 !important; - border-radius: 3px !important; - color: #fff !important; - font-family: arial, calibri, sans-serif, sans, monospace !important; - font-size: 15px !important; + background: var(--quarter-grey) !important; + border-radius: 3px !important; + color: var(--light-white) !important; + font-family: "Plex", "Open Sans Regular", "Helvetica Neue", Helvetica, Arial, sans-serif, system !important; + font-size: 15px !important; - bottom: 85px !important; - left: 35px !important; - padding: 3px 6px !important; - position: absolute !important; + bottom: 85px !important; + left: 35px !important; + padding: 3px 6px !important; + position: absolute !important; +} + +.web-to-plex-minion { + cursor: pointer; } /* bbodine @CodePen - https://codepen.io/bbodine1/pen/novBm */ [class*="web-to-plex"] .checkbox { - width: 80px; - height: 26px; - background: #000; - margin: 15px 0; - position: relative; - border-radius: 50px; - box-shadow: inset 0px 1px 1px rgba(0, 0, 0, 0.5), 0px 1px 0px rgba(255, 255, 255, 0.2); + width: 80px; + height: 26px; + background: var(--dark-black); + margin: 15px 0; + position: relative; + border-radius: 50px; + box-shadow: inset 0px 1px 1px rgba(0, 0, 0, 0.5), 0px 1px 0px rgba(255, 255, 255, 0.2); } [class*="web-to-plex"] span.checkbox { - display: inline-block; + display: inline-block; - margin: 0; - vertical-align: text-bottom; + margin: 0; + vertical-align: text-bottom; } [class*="web-to-plex"] .checkbox::after { - content: 'OFF'; - color: #666; - position: absolute; - right: 10px; - z-index: 0; - font: 12px/26px Arial, sans-serif; - font-weight: bold; - text-shadow: 1px 1px 0px rgba(255, 255, 255, 0.15); + content: 'OFF'; + color: var(--grey); + position: absolute; + right: 10px; + z-index: 0; + font: 12px/26px Arial, sans-serif; + font-weight: bold; + text-shadow: 1px 1px 0px rgba(255, 255, 255, 0.15); } [class*="web-to-plex"] .checkbox::before { - content: 'ON'; - color: #cc7b19; - position: absolute; - left: 10px; - z-index: 0; - font: 12px/26px Arial, sans-serif; - font-weight: bold; + content: 'ON'; + color: var(--orange); + position: absolute; + left: 10px; + z-index: 0; + font: 12px/26px Arial, sans-serif; + font-weight: bold; } [class*="web-to-plex"] .checkbox[prompt-yes]::before { - content: attr(prompt-yes); - text-transform: uppercase; + content: attr(prompt-yes); + text-transform: uppercase; } [class*="web-to-plex"] .checkbox[prompt-no]::after { - content: attr(prompt-no); - text-transform: uppercase; + content: attr(prompt-no); + text-transform: uppercase; } [class*="web-to-plex"] .checkbox[prompt-size="large"i]::before, .checkbox[prompt-size="large"i]::after { - font-size: 30px !important; + font-size: 30px !important; } [class*="web-to-plex"] .checkbox[prompt-size="medium"i]::before, .checkbox[prompt-size="medium"i]::after { - font-size: 21px !important; + font-size: 21px !important; } [class*="web-to-plex"] .checkbox[prompt-size="normal"i]::before, .checkbox[prompt-size="normal"i]::after { - font-size: 12px !important; + font-size: 12px !important; } [class*="web-to-plex"] .checkbox[prompt-size="small"i]::before, .checkbox[prompt-size="small"i]::after { - font-size: 6px !important; + font-size: 6px !important; } [class*="web-to-plex"] .checkbox[prompt="y/n"i]::before { - content: 'YES'; + content: 'YES'; } [class*="web-to-plex"] .checkbox[prompt="y/n"i]::after { - content: 'NO'; + content: 'NO'; } [class*="web-to-plex"] .checkbox label { - display: block; - width: 34px; - height: 20px; - cursor: pointer; - position: absolute; - top: 3px; - left: 3px; - z-index: 1; - background: #666; - border-radius: 50px; - transition: all 0.4s ease; - box-shadow: 0px 2px 5px 0px rgba(0, 0, 0, 0.3); + display: block; + width: 34px; + height: 20px; + cursor: pointer; + position: absolute; + top: 3px; + left: 3px; + z-index: 1; + background: var(--grey); + border-radius: 50px; + transition: all 0.4s ease; + box-shadow: 0px 2px 5px 0px rgba(0, 0, 0, 0.3); } [class*="web-to-plex"] .checkbox input[type=checkbox] { - visibility: hidden; + visibility: hidden; } [class*="web-to-plex"] .checkbox input[type=checkbox]:checked + label { - left: 43px; - background: #cc7b19; + left: 43px; + background: var(--orange); } [class*="web-to-plex"] .checkbox[disabled] { - opacity: 0.25 !important; + opacity: 0.25 !important; +} + +/* Minor Bugs */ +[glyph], [class*="glyphicon"] { + width: 1.1em; +} + +[glyph][gradient], [class*="glyphicon"][gradient] { + background-clip: text !important; + -moz-background-clip: text !important; + -webkit-background-clip: text !important; + + color: #0000 !important; + -webkit-text-fill-color: #0000 !important; } diff --git a/src/sites/couchpotato/index.css b/src/sites/couchpotato/index.css index e69de29..6e2a6b1 100644 --- a/src/sites/couchpotato/index.css +++ b/src/sites/couchpotato/index.css @@ -0,0 +1,23 @@ +.web-to-plex-minion.wtp--download { + color: var(--blue)!important; +} + +.web-to-plex-minion.wtp--download:hover { + color: var(--light-blue)!important; +} + +.web-to-plex-minion.wtp--found { + color: var(--orange)!important; +} + +.web-to-plex-minion.wtp--found:hover { + color: var(--light-orange)!important; +} + +#tt--0-0 { + color: var(--grey)!important; +} + +#tt--0-0:hover { + color: var(--light-grey)!important; +} diff --git a/src/sites/fandango/index.css b/src/sites/fandango/index.css index e69de29..5f5284d 100644 --- a/src/sites/fandango/index.css +++ b/src/sites/fandango/index.css @@ -0,0 +1,27 @@ +.web-to-plex-minion { + color: #727272; +} + +.web-to-plex-minion.wtp--download { + color: #f45a26!important; +} + +.web-to-plex-minion.wtp--found { + color: #e5a00d!important; +} + +.web-to-plex-wrapper:hover, .web-to-plex-wrapper:hover > *, .web-to-plex-minion.wtp--download:hover { + color: #f67e56!important; +} + +.web-to-plex-minion.wtp--found:hover { + color: #f8c022!important; +} + +#tt--0-0 { + color: #666!important; +} + +#tt--0-0:hover { + color: #888!important; +} diff --git a/src/sites/glyphs.css b/src/sites/glyphs.css new file mode 100644 index 0000000..4b29026 --- /dev/null +++ b/src/sites/glyphs.css @@ -0,0 +1,2463 @@ +/** Available Icons: + * adjust + * airplane + * alarm + * albums + * amazon + * anchor + * android + * apple + * asterisk + * ax + * badoo + * ban + * bank + * barcode + * baseball + * basketball + * bathrobe + * beer + * behance + * bell + * bicycle + * bin + * binoculars + * blacksmith + * blog + * blogger + * bluetooth + * boat + * bold + * bomb + * book + * bookmark + * bowling + * briefcase + * brush + * bug + * building + * bullets + * bullhorn + * buoy + * bus + * cake + * calculator + * calendar + * camera + * candle + * car + * cardio + * cargo + * cars + * celebration + * certificate + * charts + * chat + * check + * cleaning + * clock + * cloud + * cogwheel + * cogwheels + * coins + * collapse + * comments + * compass + * compressed + * conversation + * crop + * crown + * cup + * cutlery + * dashboard + * delete + * deviantart + * direction + * dislikes + * display + * divide + * dog + * download + * dress + * dribbble + * drink + * dropbox + * dumbbell + * earphone + * edit + * eject + * electricity + * embed + * envelope + * euro + * evernote + * exit + * expand + * eyedropper + * fabric + * facebook + * factory + * fax + * female + * file + * film + * filter + * fins + * fire + * fishes + * flag + * flash + * flickr + * flower + * font + * forrst + * forward + * foursquare + * fullscreen + * gamepad + * gbp + * gift + * girl + * github + * glass + * global + * globe + * golf + * goodreads + * google_plus + * grater + * group + * hdd + * header + * headphones + * headset + * heart + * heat + * history + * hockey + * home + * hospital + * imac + * inbox + * instagram + * instapaper + * ios + * ipad + * iphone + * ipod + * italic + * jolicloud + * justify + * kettle + * keynote + * keys + * kiosk + * last_fm + * leaf + * leather + * lightbulb + * link + * linked_in + * list + * lock + * luggage + * macbook + * magic + * magnet + * male + * microphone + * minus + * money + * moon + * more + * move + * music + * mute + * myspace + * nails + * nameplate + * note + * notes + * ok + * package + * pants + * paperclip + * parents + * pause + * pen + * pencil + * piano + * picasa + * picture + * pin + * pinboard + * pinterest + * pipe + * pizza + * play + * playlist + * playstation + * plus + * podium + * pool + * posterous_spaces + * pot + * power + * print + * projector + * pushpin + * qrcode + * quora + * rabbit + * radar + * random + * read_it_later + * readability + * record + * redo + * refresh + * remove + * repeat + * restart + * retweet + * rewind + * riflescope + * ring + * road + * roundabout + * router + * rss + * rugby + * ruller + * sampler + * scissors + * screenshot + * search + * send + * server + * settings + * share + * shield + * shirt + * shop + * signal + * skateboard + * skitch + * skull + * skype + * smoking + * snowflake + * sort + * sorting + * spade + * spotify + * spray + * star + * stats + * stop + * stopwatch + * stroller + * stumbleupon + * subtitles + * suitcase + * sun + * sweater + * table + * tablet + * tag + * tags + * tie + * tint + * tower + * train + * transfer + * translate + * truck + * tumblr + * turtle + * twitter + * umbrella + * unchecked + * underwear + * undo + * unlock + * unshare + * upload + * usd + * user + * vases + * vcard + * vimeo + * vine + * wallet + * webcam + * wifi + * windows + * woman + * wordpress + * wrench + * xbox + * xing + * yahoo + * yelp + * youtube + * zootool + */ + +@charset "utf-8"; +@font-face { + font-family: Glyphicons Regular; + src: local(Glyphicons), + url(/font/Glyphicons.woff) format("woff"), + url(//webtoplex.github.io/font/Glyphicons.woff) format("woff"); + font-weight: 400; + font-style: normal +} + +@font-face { + font-family: Glyphicons Social Regular; + src: local(Glyphicons Social), + url(/font/Glyphicons Social.woff) format("woff"), + url(//webtoplex.github.io/font/Glyphicons Social.woff) format("woff"); + font-weight: 400; + font-style: normal +} + +[class*="glyphicon"i], [glyph] { + position: relative; + top: 1px; + display: inline-block; + font-family: Glyphicons Regular !important; + font-style: normal; + font-weight: 400; + line-height: 1; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale + width: 1em; +} + +.glyphicon-social, [glyph~="social"] { + font-family: Glyphicons Social Regular +} + +.glyphicon-lg, [glyph~="lg"] { + font-size: 1.33333333em; + line-height: .75em; + vertical-align: -15% +} + +.glyphicon-2x, [glyph~="2x"] { + font-size: 2em +} + +.glyphicon-3x, [glyph~="3x"] { + font-size: 3em +} + +.glyphicon-4x, [glyph~="4x"] { + font-size: 4em +} + +.glyphicon-5x, [glyph~="5x"] { + font-size: 5em +} + +[class*="glyphicon"].glass:before, [glyph~="glass"]:before { + content: "\e001" +} + +[class*="glyphicon"].leaf:before, [glyph~="leaf"]:before { + content: "\e002" +} + +[class*="glyphicon"].dog:before, [glyph~="dog"]:before { + content: "\1F415" +} + +[class*="glyphicon"].user:before, [glyph~="user"]:before { + content: "\e004" +} + +[class*="glyphicon"].girl:before, [glyph~="girl"]:before { + content: "\1F467" +} + +[class*="glyphicon"].car:before, [glyph~="car"]:before { + content: "\e006" +} + +[class*="glyphicon"].user-add:before { + content: "\e007" +} + +[class*="glyphicon"].user-remove:before { + content: "\e008" +} + +[class*="glyphicon"].film:before, [glyph~="film"]:before { + content: "\e009" +} + +[class*="glyphicon"].magic:before, [glyph~="magic"]:before { + content: "\e010" +} + +[class*="glyphicon"].envelope:before, [glyph~="envelope"]:before { + content: "\2709" +} + +[class*="glyphicon"].camera:before, [glyph~="camera"]:before { + content: "\1F4F7" +} + +[class*="glyphicon"].heart:before, [glyph~="heart"]:before { + content: "\e013" +} + +[class*="glyphicon"].beach-umbrella:before { + content: "\e014" +} + +[class*="glyphicon"].train:before, [glyph~="train"]:before { + content: "\1F686" +} + +[class*="glyphicon"].print:before, [glyph~="print"]:before { + content: "\e016" +} + +[class*="glyphicon"].bin:before, [glyph~="bin"]:before { + content: "\e017" +} + +[class*="glyphicon"].music:before, [glyph~="music"]:before { + content: "\e018" +} + +[class*="glyphicon"].note:before, [glyph~="note"]:before { + content: "\e019" +} + +[class*="glyphicon"].heart-empty:before { + content: "\e020" +} + +[class*="glyphicon"].home:before, [glyph~="home"]:before { + content: "\e021" +} + +[class*="glyphicon"].snowflake:before, [glyph~="snowflake"]:before { + content: "\2744" +} + +[class*="glyphicon"].fire:before, [glyph~="fire"]:before { + content: "\1F525" +} + +[class*="glyphicon"].magnet:before, [glyph~="magnet"]:before { + content: "\e024" +} + +[class*="glyphicon"].parents:before, [glyph~="parents"]:before { + content: "\e025" +} + +[class*="glyphicon"].binoculars:before, [glyph~="binoculars"]:before { + content: "\e026" +} + +[class*="glyphicon"].road:before, [glyph~="road"]:before { + content: "\e027" +} + +[class*="glyphicon"].search:before, [glyph~="search"]:before { + content: "\e028" +} + +[class*="glyphicon"].cars:before, [glyph~="cars"]:before { + content: "\e029" +} + +[class*="glyphicon"].notes-2:before { + content: "\e030" +} + +[class*="glyphicon"].pencil:before, [glyph~="pencil"]:before { + content: "\270F" +} + +[class*="glyphicon"].bus:before, [glyph~="bus"]:before { + content: "\1F68C" +} + +[class*="glyphicon"].wifi-alt:before { + content: "\e033" +} + +[class*="glyphicon"].luggage:before, [glyph~="luggage"]:before { + content: "\e034" +} + +[class*="glyphicon"].old-man:before { + content: "\e035" +} + +[class*="glyphicon"].woman:before, [glyph~="woman"]:before { + content: "\1F469" +} + +[class*="glyphicon"].file:before, [glyph~="file"]:before { + content: "\e037" +} + +[class*="glyphicon"].coins:before, [glyph~="coins"]:before { + content: "\e038" +} + +[class*="glyphicon"].airplane:before, [glyph~="airplane"]:before { + content: "\2708" +} + +[class*="glyphicon"].notes:before, [glyph~="notes"]:before { + content: "\e040" +} + +[class*="glyphicon"].stats:before, [glyph~="stats"]:before { + content: "\e041" +} + +[class*="glyphicon"].charts:before, [glyph~="charts"]:before { + content: "\e042" +} + +[class*="glyphicon"].pie-chart:before { + content: "\e043" +} + +[class*="glyphicon"].group:before, [glyph~="group"]:before { + content: "\e044" +} + +[class*="glyphicon"].keys:before, [glyph~="keys"]:before { + content: "\e045" +} + +[class*="glyphicon"].calendar:before, [glyph~="calendar"]:before { + content: "\1F4C5" +} + +[class*="glyphicon"].router:before, [glyph~="router"]:before { + content: "\e047" +} + +[class*="glyphicon"].camera-small:before { + content: "\e048" +} + +[class*="glyphicon"].dislikes:before, [glyph~="dislikes"]:before { + content: "\e049" +} + +[class*="glyphicon"].star:before, [glyph~="star"]:before { + content: "\e050" +} + +[class*="glyphicon"].link:before, [glyph~="link"]:before { + content: "\e051" +} + +[class*="glyphicon"].eye-open:before { + content: "\e052" +} + +[class*="glyphicon"].eye-close:before { + content: "\e053" +} + +[class*="glyphicon"].alarm:before, [glyph~="alarm"]:before { + content: "\e054" +} + +[class*="glyphicon"].clock:before, [glyph~="clock"]:before { + content: "\e055" +} + +[class*="glyphicon"].stopwatch:before, [glyph~="stopwatch"]:before { + content: "\e056" +} + +[class*="glyphicon"].projector:before, [glyph~="projector"]:before { + content: "\e057" +} + +[class*="glyphicon"].history:before, [glyph~="history"]:before { + content: "\e058" +} + +[class*="glyphicon"].truck:before, [glyph~="truck"]:before { + content: "\e059" +} + +[class*="glyphicon"].cargo:before, [glyph~="cargo"]:before { + content: "\e060" +} + +[class*="glyphicon"].compass:before, [glyph~="compass"]:before { + content: "\e061" +} + +[class*="glyphicon"].keynote:before, [glyph~="keynote"]:before { + content: "\e062" +} + +[class*="glyphicon"].paperclip:before, [glyph~="paperclip"]:before { + content: "\1F4CE" +} + +[class*="glyphicon"].power:before, [glyph~="power"]:before { + content: "\e064" +} + +[class*="glyphicon"].lightbulb:before, [glyph~="lightbulb"]:before { + content: "\e065" +} + +[class*="glyphicon"].tag:before, [glyph~="tag"]:before { + content: "\e066" +} + +[class*="glyphicon"].tags:before, [glyph~="tags"]:before { + content: "\e067" +} + +[class*="glyphicon"].cleaning:before, [glyph~="cleaning"]:before { + content: "\e068" +} + +[class*="glyphicon"].ruller:before, [glyph~="ruller"]:before { + content: "\e069" +} + +[class*="glyphicon"].gift:before, [glyph~="gift"]:before { + content: "\e070" +} + +[class*="glyphicon"].umbrella:before, [glyph~="umbrella"]:before { + content: "\2602" +} + +[class*="glyphicon"].book:before, [glyph~="book"]:before { + content: "\e072" +} + +[class*="glyphicon"].bookmark:before, [glyph~="bookmark"]:before { + content: "\1F516" +} + +[class*="glyphicon"].wifi:before, [glyph~="wifi"]:before { + content: "\e074" +} + +[class*="glyphicon"].cup:before, [glyph~="cup"]:before { + content: "\e075" +} + +[class*="glyphicon"].stroller:before, [glyph~="stroller"]:before { + content: "\e076" +} + +[class*="glyphicon"].headphones:before, [glyph~="headphones"]:before { + content: "\e077" +} + +[class*="glyphicon"].headset:before, [glyph~="headset"]:before { + content: "\e078" +} + +[class*="glyphicon"].warning-sign:before { + content: "\e079" +} + +[class*="glyphicon"].signal:before, [glyph~="signal"]:before { + content: "\e080" +} + +[class*="glyphicon"].retweet:before, [glyph~="retweet"]:before { + content: "\e081" +} + +[class*="glyphicon"].refresh:before, [glyph~="refresh"]:before { + content: "\e082" +} + +[class*="glyphicon"].roundabout:before, [glyph~="roundabout"]:before { + content: "\e083" +} + +[class*="glyphicon"].random:before, [glyph~="random"]:before { + content: "\e084" +} + +[class*="glyphicon"].heat:before, [glyph~="heat"]:before { + content: "\e085" +} + +[class*="glyphicon"].repeat:before, [glyph~="repeat"]:before { + content: "\e086" +} + +[class*="glyphicon"].display:before, [glyph~="display"]:before { + content: "\e087" +} + +[class*="glyphicon"].log-book:before { + content: "\e088" +} + +[class*="glyphicon"].address-book:before { + content: "\e089" +} + +[class*="glyphicon"].building:before, [glyph~="building"]:before { + content: "\e090" +} + +[class*="glyphicon"].eyedropper:before, [glyph~="eyedropper"]:before { + content: "\e091" +} + +[class*="glyphicon"].adjust:before, [glyph~="adjust"]:before { + content: "\e092" +} + +[class*="glyphicon"].tint:before, [glyph~="tint"]:before { + content: "\e093" +} + +[class*="glyphicon"].crop:before, [glyph~="crop"]:before { + content: "\e094" +} + +[class*="glyphicon"].vector-path-square:before { + content: "\e095" +} + +[class*="glyphicon"].vector-path-circle:before { + content: "\e096" +} + +[class*="glyphicon"].vector-path-polygon:before { + content: "\e097" +} + +[class*="glyphicon"].vector-path-line:before { + content: "\e098" +} + +[class*="glyphicon"].vector-path-curve:before { + content: "\e099" +} + +[class*="glyphicon"].vector-path-all:before { + content: "\e100" +} + +[class*="glyphicon"].font:before, [glyph~="font"]:before { + content: "\e101" +} + +[class*="glyphicon"].italic:before, [glyph~="italic"]:before { + content: "\e102" +} + +[class*="glyphicon"].bold:before, [glyph~="bold"]:before { + content: "\e103" +} + +[class*="glyphicon"].text-underline:before { + content: "\e104" +} + +[class*="glyphicon"].text-strike:before { + content: "\e105" +} + +[class*="glyphicon"].text-height:before { + content: "\e106" +} + +[class*="glyphicon"].text-width:before { + content: "\e107" +} + +[class*="glyphicon"].text-resize:before { + content: "\e108" +} + +[class*="glyphicon"].left-indent:before { + content: "\e109" +} + +[class*="glyphicon"].right-indent:before { + content: "\e110" +} + +[class*="glyphicon"].align-left:before { + content: "\e111" +} + +[class*="glyphicon"].align-center:before { + content: "\e112" +} + +[class*="glyphicon"].align-right:before { + content: "\e113" +} + +[class*="glyphicon"].justify:before, [glyph~="justify"]:before { + content: "\e114" +} + +[class*="glyphicon"].list:before, [glyph~="list"]:before { + content: "\e115" +} + +[class*="glyphicon"].text-smaller:before { + content: "\e116" +} + +[class*="glyphicon"].text-bigger:before { + content: "\e117" +} + +[class*="glyphicon"].embed:before, [glyph~="embed"]:before { + content: "\e118" +} + +[class*="glyphicon"].embed-close:before { + content: "\e119" +} + +[class*="glyphicon"].table:before, [glyph~="table"]:before { + content: "\e120" +} + +[class*="glyphicon"].message-full:before { + content: "\e121" +} + +[class*="glyphicon"].message-empty:before { + content: "\e122" +} + +[class*="glyphicon"].message-in:before { + content: "\e123" +} + +[class*="glyphicon"].message-out:before { + content: "\e124" +} + +[class*="glyphicon"].message-plus:before { + content: "\e125" +} + +[class*="glyphicon"].message-minus:before { + content: "\e126" +} + +[class*="glyphicon"].message-ban:before { + content: "\e127" +} + +[class*="glyphicon"].message-flag:before { + content: "\e128" +} + +[class*="glyphicon"].message-lock:before { + content: "\e129" +} + +[class*="glyphicon"].message-new:before { + content: "\e130" +} + +[class*="glyphicon"].inbox:before, [glyph~="inbox"]:before { + content: "\e131" +} + +[class*="glyphicon"].inbox-plus:before { + content: "\e132" +} + +[class*="glyphicon"].inbox-minus:before { + content: "\e133" +} + +[class*="glyphicon"].inbox-lock:before { + content: "\e134" +} + +[class*="glyphicon"].inbox-in:before { + content: "\e135" +} + +[class*="glyphicon"].inbox-out:before { + content: "\e136" +} + +[class*="glyphicon"].cogwheel:before, [glyph~="cogwheel"]:before { + content: "\e137" +} + +[class*="glyphicon"].cogwheels:before, [glyph~="cogwheels"]:before { + content: "\e138" +} + +[class*="glyphicon"].picture:before, [glyph~="picture"]:before { + content: "\e139" +} + +[class*="glyphicon"].adjust-alt:before { + content: "\e140" +} + +[class*="glyphicon"].database-lock:before { + content: "\e141" +} + +[class*="glyphicon"].database-plus:before { + content: "\e142" +} + +[class*="glyphicon"].database-minus:before { + content: "\e143" +} + +[class*="glyphicon"].database-ban:before { + content: "\e144" +} + +[class*="glyphicon"].folder-open:before { + content: "\e145" +} + +[class*="glyphicon"].folder-plus:before { + content: "\e146" +} + +[class*="glyphicon"].folder-minus:before { + content: "\e147" +} + +[class*="glyphicon"].folder-lock:before { + content: "\e148" +} + +[class*="glyphicon"].folder-flag:before { + content: "\e149" +} + +[class*="glyphicon"].folder-new:before { + content: "\e150" +} + +[class*="glyphicon"].edit:before, [glyph~="edit"]:before { + content: "\e151" +} + +[class*="glyphicon"].new-window:before { + content: "\e152" +} + +[class*="glyphicon"].check:before, [glyph~="check"]:before { + content: "\e153" +} + +[class*="glyphicon"].unchecked:before, [glyph~="unchecked"]:before { + content: "\e154" +} + +[class*="glyphicon"].more-windows:before { + content: "\e155" +} + +[class*="glyphicon"].show-big-thumbnails:before { + content: "\e156" +} + +[class*="glyphicon"].show-thumbnails:before { + content: "\e157" +} + +[class*="glyphicon"].show-thumbnails-with-lines:before { + content: "\e158" +} + +[class*="glyphicon"].show-lines:before { + content: "\e159" +} + +[class*="glyphicon"].playlist:before, [glyph~="playlist"]:before { + content: "\e160" +} + +[class*="glyphicon"].imac:before, [glyph~="imac"]:before { + content: "\e161" +} + +[class*="glyphicon"].macbook:before, [glyph~="macbook"]:before { + content: "\e162" +} + +[class*="glyphicon"].ipad:before, [glyph~="ipad"]:before { + content: "\e163" +} + +[class*="glyphicon"].iphone:before, [glyph~="iphone"]:before { + content: "\e164" +} + +[class*="glyphicon"].iphone-transfer:before { + content: "\e165" +} + +[class*="glyphicon"].iphone-exchange:before { + content: "\e166" +} + +[class*="glyphicon"].ipod:before, [glyph~="ipod"]:before { + content: "\e167" +} + +[class*="glyphicon"].ipod-shuffle:before { + content: "\e168" +} + +[class*="glyphicon"].ear-plugs:before { + content: "\e169" +} + +[class*="glyphicon"].record:before, [glyph~="record"]:before { + content: "\e170" +} + +[class*="glyphicon"].step-backward:before { + content: "\e171" +} + +[class*="glyphicon"].fast-backward:before { + content: "\e172" +} + +[class*="glyphicon"].rewind:before, [glyph~="rewind"]:before { + content: "\e173" +} + +[class*="glyphicon"].play:before, [glyph~="play"]:before { + content: "\e174" +} + +[class*="glyphicon"].pause:before, [glyph~="pause"]:before { + content: "\e175" +} + +[class*="glyphicon"].stop:before, [glyph~="stop"]:before { + content: "\e176" +} + +[class*="glyphicon"].forward:before, [glyph~="forward"]:before { + content: "\e177" +} + +[class*="glyphicon"].fast-forward:before { + content: "\e178" +} + +[class*="glyphicon"].step-forward:before { + content: "\e179" +} + +[class*="glyphicon"].eject:before, [glyph~="eject"]:before { + content: "\e180" +} + +[class*="glyphicon"].facetime-video:before { + content: "\e181" +} + +[class*="glyphicon"].download-alt:before { + content: "\e182" +} + +[class*="glyphicon"].mute:before, [glyph~="mute"]:before { + content: "\e183" +} + +[class*="glyphicon"].volume-down:before { + content: "\e184" +} + +[class*="glyphicon"].volume-up:before { + content: "\e185" +} + +[class*="glyphicon"].screenshot:before, [glyph~="screenshot"]:before { + content: "\e186" +} + +[class*="glyphicon"].move:before, [glyph~="move"]:before { + content: "\e187" +} + +[class*="glyphicon"].more:before, [glyph~="more"]:before { + content: "\e188" +} + +[class*="glyphicon"].brightness-reduce:before { + content: "\e189" +} + +[class*="glyphicon"].brightness-increase:before { + content: "\e190" +} + +[class*="glyphicon"].circle-plus:before { + content: "\e191" +} + +[class*="glyphicon"].circle-minus:before { + content: "\e192" +} + +[class*="glyphicon"].circle-remove:before { + content: "\e193" +} + +[class*="glyphicon"].circle-ok:before { + content: "\e194" +} + +[class*="glyphicon"].circle-question-mark:before { + content: "\e195" +} + +[class*="glyphicon"].circle-info:before { + content: "\e196" +} + +[class*="glyphicon"].circle-exclamation-mark:before { + content: "\e197" +} + +[class*="glyphicon"].remove:before, [glyph~="remove"]:before { + content: "\e198" +} + +[class*="glyphicon"].ok:before, [glyph~="ok"]:before { + content: "\e199" +} + +[class*="glyphicon"].ban:before, [glyph~="ban"]:before { + content: "\e200" +} + +[class*="glyphicon"].download:before, [glyph~="download"]:before { + content: "\e201" +} + +[class*="glyphicon"].upload:before, [glyph~="upload"]:before { + content: "\e202" +} + +[class*="glyphicon"].shopping-cart:before { + content: "\e203" +} + +[class*="glyphicon"].lock:before, [glyph~="lock"]:before { + content: "\1F512" +} + +[class*="glyphicon"].unlock:before, [glyph~="unlock"]:before { + content: "\e205" +} + +[class*="glyphicon"].electricity:before, [glyph~="electricity"]:before { + content: "\e206" +} + +[class*="glyphicon"].ok-2:before { + content: "\e207" +} + +[class*="glyphicon"].remove-2:before { + content: "\e208" +} + +[class*="glyphicon"].cart-out:before { + content: "\e209" +} + +[class*="glyphicon"].cart-in:before { + content: "\e210" +} + +[class*="glyphicon"].left-arrow:before { + content: "\e211" +} + +[class*="glyphicon"].right-arrow:before { + content: "\e212" +} + +[class*="glyphicon"].down-arrow:before { + content: "\e213" +} + +[class*="glyphicon"].up-arrow:before { + content: "\e214" +} + +[class*="glyphicon"].resize-small:before { + content: "\e215" +} + +[class*="glyphicon"].resize-full:before { + content: "\e216" +} + +[class*="glyphicon"].circle-arrow-left:before { + content: "\e217" +} + +[class*="glyphicon"].circle-arrow-right:before { + content: "\e218" +} + +[class*="glyphicon"].circle-arrow-top:before { + content: "\e219" +} + +[class*="glyphicon"].circle-arrow-down:before { + content: "\e220" +} + +[class*="glyphicon"].play-button:before { + content: "\e221" +} + +[class*="glyphicon"].unshare:before, [glyph~="unshare"]:before { + content: "\e222" +} + +[class*="glyphicon"].share:before, [glyph~="share"]:before { + content: "\e223" +} + +[class*="glyphicon"].chevron-right:before { + content: "\e224" +} + +[class*="glyphicon"].chevron-left:before { + content: "\e225" +} + +[class*="glyphicon"].chevron-up:before { + content: "\e224" +} + +[class*="glyphicon"].chevron-up { + -webkit-transform: rotate(-90deg); + -ms-transform: rotate(-90deg); + transform: rotate(-90deg) +} + +[class*="glyphicon"].chevron-down:before { + content: "\e224" +} + +[class*="glyphicon"].chevron-down { + -webkit-transform: rotate(90deg); + -ms-transform: rotate(90deg); + transform: rotate(90deg) +} + +[class*="glyphicon"].bluetooth:before, [glyph~="bluetooth"]:before { + content: "\e226" +} + +[class*="glyphicon"].euro:before, [glyph~="euro"]:before { + content: "\20AC" +} + +[class*="glyphicon"].usd:before, [glyph~="usd"]:before { + content: "\e228" +} + +[class*="glyphicon"].gbp:before, [glyph~="gbp"]:before { + content: "\e229" +} + +[class*="glyphicon"].retweet-2:before { + content: "\e230" +} + +[class*="glyphicon"].moon:before, [glyph~="moon"]:before { + content: "\e231" +} + +[class*="glyphicon"].sun:before, [glyph~="sun"]:before { + content: "\2609" +} + +[class*="glyphicon"].cloud:before, [glyph~="cloud"]:before { + content: "\2601" +} + +[class*="glyphicon"].direction:before, [glyph~="direction"]:before { + content: "\e234" +} + +[class*="glyphicon"].brush:before, [glyph~="brush"]:before { + content: "\e235" +} + +[class*="glyphicon"].pen:before, [glyph~="pen"]:before { + content: "\e236" +} + +[class*="glyphicon"].zoom-in:before { + content: "\e237" +} + +[class*="glyphicon"].zoom-out:before { + content: "\e238" +} + +[class*="glyphicon"].pin:before, [glyph~="pin"]:before { + content: "\e239" +} + +[class*="glyphicon"].albums:before, [glyph~="albums"]:before { + content: "\e240" +} + +[class*="glyphicon"].rotation-lock:before { + content: "\e241" +} + +[class*="glyphicon"].flash:before, [glyph~="flash"]:before { + content: "\e242" +} + +[class*="glyphicon"].google-maps:before { + content: "\e243" +} + +[class*="glyphicon"].anchor:before, [glyph~="anchor"]:before { + content: "\2693" +} + +[class*="glyphicon"].conversation:before, [glyph~="conversation"]:before { + content: "\e245" +} + +[class*="glyphicon"].chat:before, [glyph~="chat"]:before { + content: "\e246" +} + +[class*="glyphicon"].male:before, [glyph~="male"]:before { + content: "\e247" +} + +[class*="glyphicon"].female:before, [glyph~="female"]:before { + content: "\e248" +} + +[class*="glyphicon"].asterisk:before, [glyph~="asterisk"]:before { + content: "\002A" +} + +[class*="glyphicon"].divide:before, [glyph~="divide"]:before { + content: "\00F7" +} + +[class*="glyphicon"].snorkel-diving:before { + content: "\e251" +} + +[class*="glyphicon"].scuba-diving:before { + content: "\e252" +} + +[class*="glyphicon"].oxygen-bottle:before { + content: "\e253" +} + +[class*="glyphicon"].fins:before, [glyph~="fins"]:before { + content: "\e254" +} + +[class*="glyphicon"].fishes:before, [glyph~="fishes"]:before { + content: "\e255" +} + +[class*="glyphicon"].boat:before, [glyph~="boat"]:before { + content: "\e256" +} + +[class*="glyphicon"].delete:before, [glyph~="delete"]:before { + content: "\e257" +} + +[class*="glyphicon"].sheriffs-star:before { + content: "\e258" +} + +[class*="glyphicon"].qrcode:before, [glyph~="qrcode"]:before { + content: "\e259" +} + +[class*="glyphicon"].barcode:before, [glyph~="barcode"]:before { + content: "\e260" +} + +[class*="glyphicon"].pool:before, [glyph~="pool"]:before { + content: "\e261" +} + +[class*="glyphicon"].buoy:before, [glyph~="buoy"]:before { + content: "\e262" +} + +[class*="glyphicon"].spade:before, [glyph~="spade"]:before { + content: "\e263" +} + +[class*="glyphicon"].bank:before, [glyph~="bank"]:before { + content: "\1F3E6" +} + +[class*="glyphicon"].vcard:before, [glyph~="vcard"]:before { + content: "\e265" +} + +[class*="glyphicon"].electrical-plug:before { + content: "\e266" +} + +[class*="glyphicon"].flag:before, [glyph~="flag"]:before { + content: "\e267" +} + +[class*="glyphicon"].credit-card:before { + content: "\e268" +} + +[class*="glyphicon"].keyboard-wireless:before { + content: "\e269" +} + +[class*="glyphicon"].keyboard-wired:before { + content: "\e270" +} + +[class*="glyphicon"].shield:before, [glyph~="shield"]:before { + content: "\e271" +} + +[class*="glyphicon"].ring:before, [glyph~="ring"]:before { + content: "\02DA" +} + +[class*="glyphicon"].cake:before, [glyph~="cake"]:before { + content: "\e273" +} + +[class*="glyphicon"].drink:before, [glyph~="drink"]:before { + content: "\e274" +} + +[class*="glyphicon"].beer:before, [glyph~="beer"]:before { + content: "\e275" +} + +[class*="glyphicon"].fast-food:before { + content: "\e276" +} + +[class*="glyphicon"].cutlery:before, [glyph~="cutlery"]:before { + content: "\e277" +} + +[class*="glyphicon"].pizza:before, [glyph~="pizza"]:before { + content: "\e278" +} + +[class*="glyphicon"].birthday-cake:before { + content: "\e279" +} + +[class*="glyphicon"].tablet:before, [glyph~="tablet"]:before { + content: "\e280" +} + +[class*="glyphicon"].settings:before, [glyph~="settings"]:before { + content: "\e281" +} + +[class*="glyphicon"].bullets:before, [glyph~="bullets"]:before { + content: "\e282" +} + +[class*="glyphicon"].cardio:before, [glyph~="cardio"]:before { + content: "\e283" +} + +[class*="glyphicon"].t-shirt:before { + content: "\e284" +} + +[class*="glyphicon"].pants:before, [glyph~="pants"]:before { + content: "\e285" +} + +[class*="glyphicon"].sweater:before, [glyph~="sweater"]:before { + content: "\e286" +} + +[class*="glyphicon"].fabric:before, [glyph~="fabric"]:before { + content: "\e287" +} + +[class*="glyphicon"].leather:before, [glyph~="leather"]:before { + content: "\e288" +} + +[class*="glyphicon"].scissors:before, [glyph~="scissors"]:before { + content: "\e289" +} + +[class*="glyphicon"].bomb:before, [glyph~="bomb"]:before { + content: "\1F4A3" +} + +[class*="glyphicon"].skull:before, [glyph~="skull"]:before { + content: "\1F480" +} + +[class*="glyphicon"].celebration:before, [glyph~="celebration"]:before { + content: "\e292" +} + +[class*="glyphicon"].tea-kettle:before { + content: "\e293" +} + +[class*="glyphicon"].french-press:before { + content: "\e294" +} + +[class*="glyphicon"].coffe-cup:before { + content: "\e295" +} + +[class*="glyphicon"].pot:before, [glyph~="pot"]:before { + content: "\e296" +} + +[class*="glyphicon"].grater:before, [glyph~="grater"]:before { + content: "\e297" +} + +[class*="glyphicon"].kettle:before, [glyph~="kettle"]:before { + content: "\e298" +} + +[class*="glyphicon"].hospital:before, [glyph~="hospital"]:before { + content: "\1F3E5" +} + +[class*="glyphicon"].hospital-h:before { + content: "\e300" +} + +[class*="glyphicon"].microphone:before, [glyph~="microphone"]:before { + content: "\1F3A4" +} + +[class*="glyphicon"].webcam:before, [glyph~="webcam"]:before { + content: "\e302" +} + +[class*="glyphicon"].temple-christianity-church:before { + content: "\e303" +} + +[class*="glyphicon"].temple-islam:before { + content: "\e304" +} + +[class*="glyphicon"].temple-hindu:before { + content: "\e305" +} + +[class*="glyphicon"].temple-buddhist:before { + content: "\e306" +} + +[class*="glyphicon"].bicycle:before, [glyph~="bicycle"]:before { + content: "\1F6B2" +} + +[class*="glyphicon"].life-preserver:before { + content: "\e308" +} + +[class*="glyphicon"].share-alt:before { + content: "\e309" +} + +[class*="glyphicon"].comments:before, [glyph~="comments"]:before { + content: "\e310" +} + +[class*="glyphicon"].flower:before, [glyph~="flower"]:before { + content: "\2698" +} + +[class*="glyphicon"].baseball:before, [glyph~="baseball"]:before { + content: "\26BE" +} + +[class*="glyphicon"].rugby:before, [glyph~="rugby"]:before { + content: "\e313" +} + +[class*="glyphicon"].ax:before, [glyph~="ax"]:before { + content: "\e314" +} + +[class*="glyphicon"].table-tennis:before { + content: "\e315" +} + +[class*="glyphicon"].bowling:before, [glyph~="bowling"]:before { + content: "\1F3B3" +} + +[class*="glyphicon"].tree-conifer:before { + content: "\e317" +} + +[class*="glyphicon"].tree-deciduous:before { + content: "\e318" +} + +[class*="glyphicon"].more-items:before { + content: "\e319" +} + +[class*="glyphicon"].sort:before, [glyph~="sort"]:before { + content: "\e320" +} + +[class*="glyphicon"].filter:before, [glyph~="filter"]:before { + content: "\e321" +} + +[class*="glyphicon"].gamepad:before, [glyph~="gamepad"]:before { + content: "\e322" +} + +[class*="glyphicon"].playing-dices:before { + content: "\e323" +} + +[class*="glyphicon"].calculator:before, [glyph~="calculator"]:before { + content: "\e324" +} + +[class*="glyphicon"].tie:before, [glyph~="tie"]:before { + content: "\e325" +} + +[class*="glyphicon"].wallet:before, [glyph~="wallet"]:before { + content: "\e326" +} + +[class*="glyphicon"].piano:before, [glyph~="piano"]:before { + content: "\e327" +} + +[class*="glyphicon"].sampler:before, [glyph~="sampler"]:before { + content: "\e328" +} + +[class*="glyphicon"].podium:before, [glyph~="podium"]:before { + content: "\e329" +} + +[class*="glyphicon"].soccer-ball:before { + content: "\e330" +} + +[class*="glyphicon"].blog:before, [glyph~="blog"]:before { + content: "\e331" +} + +[class*="glyphicon"].dashboard:before, [glyph~="dashboard"]:before { + content: "\e332" +} + +[class*="glyphicon"].certificate:before, [glyph~="certificate"]:before { + content: "\e333" +} + +[class*="glyphicon"].bell:before, [glyph~="bell"]:before { + content: "\1F514" +} + +[class*="glyphicon"].candle:before, [glyph~="candle"]:before { + content: "\e335" +} + +[class*="glyphicon"].pushpin:before, [glyph~="pushpin"]:before { + content: "\1F4CC" +} + +[class*="glyphicon"].iphone-shake:before { + content: "\e337" +} + +[class*="glyphicon"].pin-flag:before { + content: "\e338" +} + +[class*="glyphicon"].turtle:before, [glyph~="turtle"]:before { + content: "\1F422" +} + +[class*="glyphicon"].rabbit:before, [glyph~="rabbit"]:before { + content: "\1F407" +} + +[class*="glyphicon"].globe:before, [glyph~="globe"]:before { + content: "\e341" +} + +[class*="glyphicon"].briefcase:before, [glyph~="briefcase"]:before { + content: "\1F4BC" +} + +[class*="glyphicon"].hdd:before, [glyph~="hdd"]:before { + content: "\e343" +} + +[class*="glyphicon"].thumbs-up:before { + content: "\e344" +} + +[class*="glyphicon"].thumbs-down:before { + content: "\e345" +} + +[class*="glyphicon"].hand-right:before { + content: "\e346" +} + +[class*="glyphicon"].hand-left:before { + content: "\e347" +} + +[class*="glyphicon"].hand-up:before { + content: "\e348" +} + +[class*="glyphicon"].hand-down:before { + content: "\e349" +} + +[class*="glyphicon"].fullscreen:before, [glyph~="fullscreen"]:before { + content: "\e350" +} + +[class*="glyphicon"].shopping-bag:before { + content: "\e351" +} + +[class*="glyphicon"].book-open:before { + content: "\e352" +} + +[class*="glyphicon"].nameplate:before, [glyph~="nameplate"]:before { + content: "\e353" +} + +[class*="glyphicon"].nameplate-alt:before { + content: "\e354" +} + +[class*="glyphicon"].vases:before, [glyph~="vases"]:before { + content: "\e355" +} + +[class*="glyphicon"].bullhorn:before, [glyph~="bullhorn"]:before { + content: "\e356" +} + +[class*="glyphicon"].dumbbell:before, [glyph~="dumbbell"]:before { + content: "\e357" +} + +[class*="glyphicon"].suitcase:before, [glyph~="suitcase"]:before { + content: "\e358" +} + +[class*="glyphicon"].file-import:before { + content: "\e359" +} + +[class*="glyphicon"].file-export:before { + content: "\e360" +} + +[class*="glyphicon"].bug:before, [glyph~="bug"]:before { + content: "\1F41B" +} + +[class*="glyphicon"].crown:before, [glyph~="crown"]:before { + content: "\1F451" +} + +[class*="glyphicon"].smoking:before, [glyph~="smoking"]:before { + content: "\e363" +} + +[class*="glyphicon"].cloud-upload:before { + content: "\e364" +} + +[class*="glyphicon"].cloud-download:before { + content: "\e365" +} + +[class*="glyphicon"].restart:before, [glyph~="restart"]:before { + content: "\e366" +} + +[class*="glyphicon"].security-camera:before { + content: "\e367" +} + +[class*="glyphicon"].expand:before, [glyph~="expand"]:before { + content: "\e368" +} + +[class*="glyphicon"].collapse:before, [glyph~="collapse"]:before { + content: "\e369" +} + +[class*="glyphicon"].collapse-top:before { + content: "\e370" +} + +[class*="glyphicon"].globe-af:before { + content: "\e371" +} + +[class*="glyphicon"].global:before, [glyph~="global"]:before { + content: "\e372" +} + +[class*="glyphicon"].spray:before, [glyph~="spray"]:before { + content: "\e373" +} + +[class*="glyphicon"].nails:before, [glyph~="nails"]:before { + content: "\e374" +} + +[class*="glyphicon"].claw-hammer:before { + content: "\e375" +} + +[class*="glyphicon"].classic-hammer:before { + content: "\e376" +} + +[class*="glyphicon"].hand-saw:before { + content: "\e377" +} + +[class*="glyphicon"].riflescope:before, [glyph~="riflescope"]:before { + content: "\e378" +} + +[class*="glyphicon"].electrical-socket-eu:before { + content: "\e379" +} + +[class*="glyphicon"].electrical-socket-us:before { + content: "\e380" +} + +[class*="glyphicon"].message-forward:before { + content: "\e381" +} + +[class*="glyphicon"].coat-hanger:before { + content: "\e382" +} + +[class*="glyphicon"].dress:before, [glyph~="dress"]:before { + content: "\1F457" +} + +[class*="glyphicon"].bathrobe:before, [glyph~="bathrobe"]:before { + content: "\e384" +} + +[class*="glyphicon"].shirt:before, [glyph~="shirt"]:before { + content: "\e385" +} + +[class*="glyphicon"].underwear:before, [glyph~="underwear"]:before { + content: "\e386" +} + +[class*="glyphicon"].log-in:before { + content: "\e387" +} + +[class*="glyphicon"].log-out:before { + content: "\e388" +} + +[class*="glyphicon"].exit:before, [glyph~="exit"]:before { + content: "\e389" +} + +[class*="glyphicon"].new-window-alt:before { + content: "\e390" +} + +[class*="glyphicon"].video-sd:before { + content: "\e391" +} + +[class*="glyphicon"].video-hd:before { + content: "\e392" +} + +[class*="glyphicon"].subtitles:before, [glyph~="subtitles"]:before { + content: "\e393" +} + +[class*="glyphicon"].sound-stereo:before { + content: "\e394" +} + +[class*="glyphicon"].sound-dolby:before { + content: "\e395" +} + +[class*="glyphicon"].sound-5-1:before { + content: "\e396" +} + +[class*="glyphicon"].sound-6-1:before { + content: "\e397" +} + +[class*="glyphicon"].sound-7-1:before { + content: "\e398" +} + +[class*="glyphicon"].copyright-mark:before { + content: "\e399" +} + +[class*="glyphicon"].registration-mark:before { + content: "\e400" +} + +[class*="glyphicon"].radar:before, [glyph~="radar"]:before { + content: "\e401" +} + +[class*="glyphicon"].skateboard:before, [glyph~="skateboard"]:before { + content: "\e402" +} + +[class*="glyphicon"].golf-course:before { + content: "\e403" +} + +[class*="glyphicon"].sorting:before, [glyph~="sorting"]:before { + content: "\e404" +} + +[class*="glyphicon"].sort-by-alphabet:before { + content: "\e405" +} + +[class*="glyphicon"].sort-by-alphabet-alt:before { + content: "\e406" +} + +[class*="glyphicon"].sort-by-order:before { + content: "\e407" +} + +[class*="glyphicon"].sort-by-order-alt:before { + content: "\e408" +} + +[class*="glyphicon"].sort-by-attributes:before { + content: "\e409" +} + +[class*="glyphicon"].sort-by-attributes-alt:before { + content: "\e410" +} + +[class*="glyphicon"].compressed:before, [glyph~="compressed"]:before { + content: "\e411" +} + +[class*="glyphicon"].package:before, [glyph~="package"]:before { + content: "\1F4E6" +} + +[class*="glyphicon"].cloud-plus:before { + content: "\e413" +} + +[class*="glyphicon"].cloud-minus:before { + content: "\e414" +} + +[class*="glyphicon"].disk-save:before { + content: "\e415" +} + +[class*="glyphicon"].disk-open:before { + content: "\e416" +} + +[class*="glyphicon"].disk-saved:before { + content: "\e417" +} + +[class*="glyphicon"].disk-remove:before { + content: "\e418" +} + +[class*="glyphicon"].disk-import:before { + content: "\e419" +} + +[class*="glyphicon"].disk-export:before { + content: "\e420" +} + +[class*="glyphicon"].tower:before, [glyph~="tower"]:before { + content: "\e421" +} + +[class*="glyphicon"].send:before, [glyph~="send"]:before { + content: "\e422" +} + +[class*="glyphicon"].git-branch:before { + content: "\e423" +} + +[class*="glyphicon"].git-create:before { + content: "\e424" +} + +[class*="glyphicon"].git-private:before { + content: "\e425" +} + +[class*="glyphicon"].git-delete:before { + content: "\e426" +} + +[class*="glyphicon"].git-merge:before { + content: "\e427" +} + +[class*="glyphicon"].git-pull-request:before { + content: "\e428" +} + +[class*="glyphicon"].git-compare:before { + content: "\e429" +} + +[class*="glyphicon"].git-commit:before { + content: "\e430" +} + +[class*="glyphicon"].construction-cone:before { + content: "\e431" +} + +[class*="glyphicon"].shoe-steps:before { + content: "\e432" +} + +[class*="glyphicon"].plus:before, [glyph~="plus"]:before { + content: "\002B" +} + +[class*="glyphicon"].minus:before, [glyph~="minus"]:before { + content: "\2212" +} + +[class*="glyphicon"].redo:before, [glyph~="redo"]:before { + content: "\e435" +} + +[class*="glyphicon"].undo:before, [glyph~="undo"]:before { + content: "\e436" +} + +[class*="glyphicon"].golf:before, [glyph~="golf"]:before { + content: "\e437" +} + +[class*="glyphicon"].hockey:before, [glyph~="hockey"]:before { + content: "\e438" +} + +[class*="glyphicon"].pipe:before, [glyph~="pipe"]:before { + content: "\e439" +} + +[class*="glyphicon"].wrench:before, [glyph~="wrench"]:before { + content: "\1F527" +} + +[class*="glyphicon"].folder-closed:before { + content: "\e441" +} + +[class*="glyphicon"].phone-alt:before { + content: "\e442" +} + +[class*="glyphicon"].earphone:before, [glyph~="earphone"]:before { + content: "\e443" +} + +[class*="glyphicon"].floppy-disk:before { + content: "\e444" +} + +[class*="glyphicon"].floppy-saved:before { + content: "\e445" +} + +[class*="glyphicon"].floppy-remove:before { + content: "\e446" +} + +[class*="glyphicon"].floppy-save:before { + content: "\e447" +} + +[class*="glyphicon"].floppy-open:before { + content: "\e448" +} + +[class*="glyphicon"].translate:before, [glyph~="translate"]:before { + content: "\e449" +} + +[class*="glyphicon"].fax:before, [glyph~="fax"]:before { + content: "\e450" +} + +[class*="glyphicon"].factory:before, [glyph~="factory"]:before { + content: "\1F3ED" +} + +[class*="glyphicon"].shop-window:before { + content: "\e452" +} + +[class*="glyphicon"].shop:before, [glyph~="shop"]:before { + content: "\e453" +} + +[class*="glyphicon"].kiosk:before, [glyph~="kiosk"]:before { + content: "\e454" +} + +[class*="glyphicon"].kiosk-wheels:before { + content: "\e455" +} + +[class*="glyphicon"].kiosk-light:before { + content: "\e456" +} + +[class*="glyphicon"].kiosk-food:before { + content: "\e457" +} + +[class*="glyphicon"].transfer:before, [glyph~="transfer"]:before { + content: "\e458" +} + +[class*="glyphicon"].money:before, [glyph~="money"]:before { + content: "\e459" +} + +[class*="glyphicon"].header:before, [glyph~="header"]:before { + content: "\e460" +} + +[class*="glyphicon"].blacksmith:before, [glyph~="blacksmith"]:before { + content: "\e461" +} + +[class*="glyphicon"].saw-blade:before { + content: "\e462" +} + +[class*="glyphicon"].basketball:before, [glyph~="basketball"]:before { + content: "\e463" +} + +[class*="glyphicon"].server:before, [glyph~="server"]:before { + content: "\e464" +} + +[class*="glyphicon"].server-plus:before { + content: "\e465" +} + +[class*="glyphicon"].server-minus:before { + content: "\e466" +} + +[class*="glyphicon"].server-ban:before { + content: "\e467" +} + +[class*="glyphicon"].server-flag:before { + content: "\e468" +} + +[class*="glyphicon"].server-lock:before { + content: "\e469" +} + +[class*="glyphicon"].server-new:before { + content: "\e470" +} + +.glyphicon-social.pinterest:before, [glyph~="social pinterest"]:before { + content: "\e001" +} + +.glyphicon-social.dropbox:before, [glyph~="social dropbox"]:before { + content: "\e002" +} + +.glyphicon-social.google_plus:before, [glyph~="social google_plus"]:before { + content: "\e003" +} + +.glyphicon-social.jolicloud:before, [glyph~="social jolicloud"]:before { + content: "\e004" +} + +.glyphicon-social.yahoo:before, [glyph~="social yahoo"]:before { + content: "\e005" +} + +.glyphicon-social.blogger:before, [glyph~="social blogger"]:before { + content: "\e006" +} + +.glyphicon-social.picasa:before, [glyph~="social picasa"]:before { + content: "\e007" +} + +.glyphicon-social.amazon:before, [glyph~="social amazon"]:before { + content: "\e008" +} + +.glyphicon-social.tumblr:before, [glyph~="social tumblr"]:before { + content: "\e009" +} + +.glyphicon-social.wordpress:before, [glyph~="social wordpress"]:before { + content: "\e010" +} + +.glyphicon-social.instapaper:before, [glyph~="social instapaper"]:before { + content: "\e011" +} + +.glyphicon-social.evernote:before, [glyph~="social evernote"]:before { + content: "\e012" +} + +.glyphicon-social.xing:before, [glyph~="social xing"]:before { + content: "\e013" +} + +.glyphicon-social.zootool:before, [glyph~="social zootool"]:before { + content: "\e014" +} + +.glyphicon-social.dribbble:before, [glyph~="social dribbble"]:before { + content: "\e015" +} + +.glyphicon-social.deviantart:before, [glyph~="social deviantart"]:before { + content: "\e016" +} + +.glyphicon-social.read_it_later:before, [glyph~="social read_it_later"]:before { + content: "\e017" +} + +.glyphicon-social.linked_in:before, [glyph~="social linked_in"]:before { + content: "\e018" +} + +.glyphicon-social.forrst:before, [glyph~="social forrst"]:before { + content: "\e019" +} + +.glyphicon-social.pinboard:before, [glyph~="social pinboard"]:before { + content: "\e020" +} + +.glyphicon-social.behance:before, [glyph~="social behance"]:before { + content: "\e021" +} + +.glyphicon-social.github:before, [glyph~="social github"]:before { + content: "\e022" +} + +.glyphicon-social.youtube:before, [glyph~="social youtube"]:before { + content: "\e023" +} + +.glyphicon-social.skitch:before, [glyph~="social skitch"]:before { + content: "\e024" +} + +.glyphicon-social.foursquare:before, [glyph~="social foursquare"]:before { + content: "\e025" +} + +.glyphicon-social.quora:before, [glyph~="social quora"]:before { + content: "\e026" +} + +.glyphicon-social.badoo:before, [glyph~="social badoo"]:before { + content: "\e027" +} + +.glyphicon-social.spotify:before, [glyph~="social spotify"]:before { + content: "\e028" +} + +.glyphicon-social.stumbleupon:before, [glyph~="social stumbleupon"]:before { + content: "\e029" +} + +.glyphicon-social.readability:before, [glyph~="social readability"]:before { + content: "\e030" +} + +.glyphicon-social.facebook:before, [glyph~="social facebook"]:before { + content: "\e031" +} + +.glyphicon-social.twitter:before, [glyph~="social twitter"]:before { + content: "\e032" +} + +.glyphicon-social.instagram:before, [glyph~="social instagram"]:before { + content: "\e033" +} + +.glyphicon-social.posterous_spaces:before, [glyph~="social posterous_spaces"]:before { + content: "\e034" +} + +.glyphicon-social.vimeo:before, [glyph~="social vimeo"]:before { + content: "\e035" +} + +.glyphicon-social.flickr:before, [glyph~="social flickr"]:before { + content: "\e036" +} + +.glyphicon-social.last_fm:before, [glyph~="social last_fm"]:before { + content: "\e037" +} + +.glyphicon-social.rss:before, [glyph~="social rss"]:before { + content: "\e038" +} + +.glyphicon-social.skype:before, [glyph~="social skype"]:before { + content: "\e039" +} + +.glyphicon-social.e-mail:before { + content: "\e040" +} + +.glyphicon-social.vine:before, [glyph~="social vine"]:before { + content: "\e041" +} + +.glyphicon-social.myspace:before, [glyph~="social myspace"]:before { + content: "\e042" +} + +.glyphicon-social.goodreads:before, [glyph~="social goodreads"]:before { + content: "\e043" +} + +.glyphicon-social.apple:before, [glyph~="social apple"]:before { + content: "\F8FF" +} + +.glyphicon-social.windows:before, [glyph~="social windows"]:before { + content: "\e045" +} + +.glyphicon-social.yelp:before, [glyph~="social yelp"]:before { + content: "\e046" +} + +.glyphicon-social.playstation:before, [glyph~="social playstation"]:before { + content: "\e047" +} + +.glyphicon-social.xbox:before, [glyph~="social xbox"]:before { + content: "\e048" +} + +.glyphicon-social.android:before, [glyph~="social android"]:before { + content: "\e049" +} + +.glyphicon-social.ios:before, [glyph~="social ios"]:before { + content: "\e050" +} diff --git a/src/sites/google/index.css b/src/sites/google/index.css index e69de29..b800ac7 100644 --- a/src/sites/google/index.css +++ b/src/sites/google/index.css @@ -0,0 +1,68 @@ +.wtp-w { + display: inline-block; + margin: 8px 0 0 4px; + transition: all 0.2s!important; +} + +.wtp-w, .web-to-plex-minion { + color: #ffffff!important; +} + +.web-to-plex-minion { + background: rgba(0,0,0,0)!important; +} + +.wtp-b { + background-color: #e5a00d!important; + line-height: 36px; + border-radius: 4px; + border: 1px; + font-size: 14px; + height: 36px; + padding: 0 20px; + box-shadow: 0 1px 0 rgba(0,0,0,0.05); + box-sizing: border-box; + cursor: pointer; + display: inline-block; + font-style: normal; + font-weight: 500; + min-width: 40px; + position: relative; + text-align: center; + text-decoration: none; + white-space: no-wrap; + vertical-align: middle; +} + +.wtp-b:hover { + background-color: #fbc022!important; + box-shadow: inset 0 -2px 0 rgba(0,0,0,0.27); +} + +.wtp--download .wtp-b { + background-color: var(--blue)!important; +} + +.wtp--download .wtp-b:hover { + background-color: var(--light-blue)!important; +} + +.wtp--found .wtp-b { + background-color: var(--orange)!important; +} + +.wtp--found .wtp-b:hover { + background-color: var(--light-orange)!important; +} + +.wtp-b:empty { + display: none!important; +} + +#tt--0-0 { + color: #666!important; +} + +#tt--0-0:hover { + color: #888!important; +} diff --git a/src/sites/hulu/index.css b/src/sites/hulu/index.css index e69de29..f75c93d 100644 --- a/src/sites/hulu/index.css +++ b/src/sites/hulu/index.css @@ -0,0 +1,37 @@ +.web-to-plex-minion { + background: 0 !important; + border: 0 !important; + display: inline-block !important; + color: #e6e6e6 !important; + font-size: 12px !important; + height: 20px !important; + text-decoration: none !important; + user-select: none !important; + vertical-align: top !important; + margin-top: 35%!important; + outline: #0000!important; +} + +.web-to-plex-minion.wtp--download { + color: #f45a26 !important; +} + +.web-to-plex-minion.wtp--download:hover { + color: #f67e56 !important; +} + +.web-to-plex-minion.wtp--found { + color: #e5a00d !important; +} + +.web-to-plex-minion.wtp--found:hover { + color: #f9be03 !important; +} + +#tt--0-0 { + color: #666 !important; +} + +#tt--0-0:hover { + color: #888 !important; +} diff --git a/src/sites/imdb/index.css b/src/sites/imdb/index.css index e69de29..48c3858 100644 --- a/src/sites/imdb/index.css +++ b/src/sites/imdb/index.css @@ -0,0 +1,78 @@ +.web-to-plex-minion { + background: #727272; + border-radius: 1000px; + color: #fff!important; + display: inline-block; + text-decoration: none!important; + text-transform: uppercase; + + margin-top: 10px; + padding: 5px 10px; +} + +.web-to-plex-minion.wtp--queued { + background: var(--light-blue)!important; +} + +.web-to-plex-minion.wtp--queued:hover { + background: var(--blue)!important; +} + +.web-to-plex-minion.wtp--download { + background: var(--dark-blue)!important; +} + +.web-to-plex-minion.wtp--download:hover { + background: var(--blue)!important; +} + +.web-to-plex-minion.wtp--found { + background: var(--orange)!important; +} + +.web-to-plex-minion.wtp--found:hover { + background: var(--light-orange)!important; +} + +.lister-list .web-to-plex-minion { + background: 0!important; + border: 0!important; + border-bottom: 5px solid #727272!important; + border-radius: 0!important; + font-size: 10px; + + margin-top: 0; + padding: 4px 6px; +} + +.lister-list .web-to-plex-minion.wtp--queued { + border-color: var(--light-blue)!important; +} + +.lister-list .web-to-plex-minion.wtp--queued:hover { + border-color: var(--blue)!important; +} + +.lister-list .web-to-plex-minion.wtp--download { + border-color: var(--dark-blue)!important; +} + +.lister-list .web-to-plex-minion.wtp--download:hover { + border-color: var(--blue)!important; +} + +.lister-list .web-to-plex-minion.wtp--found { + border-color: var(--orange)!important; +} + +.lister-list .web-to-plex-minion.wtp--found:hover { + border-color: var(--light-orange)!important; +} + +#tt--0-0 { + background: var(--grey)!important; +} + +#tt--0-0:hover { + background: var(--light-grey)!important; +} diff --git a/src/sites/itunes/index.css b/src/sites/itunes/index.css index e69de29..1e33870 100644 --- a/src/sites/itunes/index.css +++ b/src/sites/itunes/index.css @@ -0,0 +1,9 @@ +.web-to-plex-minion { + border-color: var(--orange) !important; + color: var(--orange) !important; +} + +.web-to-plex-minion:hover { + border-color: var(--light-orange) !important; + color: var(--light-orange) !important; +} diff --git a/src/sites/letterboxd/index.css b/src/sites/letterboxd/index.css index e69de29..bfe692d 100644 --- a/src/sites/letterboxd/index.css +++ b/src/sites/letterboxd/index.css @@ -0,0 +1,10 @@ +.web-to-plex-minion { + display: inline-block; + vertical-align: middle; + font-size: 12px; + line-height: 1.66666667; +} + +.web-to-plex-minion:hover { + color: #f67e56!important; +} diff --git a/src/sites/movieo/index.css b/src/sites/movieo/index.css index e69de29..7638814 100644 --- a/src/sites/movieo/index.css +++ b/src/sites/movieo/index.css @@ -0,0 +1,35 @@ +.mid-top-actions .share-box { + padding-left: 20px!important; +} + +.web-to-plex-minion { + margin-right: 8px; + color: #566273!important; +} + +.web-to-plex-minion.wtp--download { + border-color: #f45a26!important; + color: #acb4bf!important; +} + +.web-to-plex-minion.wtp--download:hover { + border-color: #f67e56!important; +} + +.web-to-plex-minion.wtp--found { + border-color: #ca7c1f!important; + color: #acb4bf!important; +} + +.web-to-plex-minion.wtp--found:hover { + border-color: #f8c022!important; +} + +#tt--0-0 { + background: #666!important; +} + +#tt--0-0:hover { + background: #888!important; + border-color: #888!important; +} diff --git a/src/sites/rottentomatoes/index.css b/src/sites/rottentomatoes/index.css index e69de29..a757498 100644 --- a/src/sites/rottentomatoes/index.css +++ b/src/sites/rottentomatoes/index.css @@ -0,0 +1,43 @@ +.mid-top-actions .share-box { + padding-left: 20px!important; +} + +.web-to-plex-minion { + border: 1px solid #f3f3f3; + border-radius: 22px; + color: #fff!important; + font: inherit; + font-family: 'Franklin Gothic FS Med', Arila, Helvetica, Tahoma, Century, Verdana, sans-serif; + font-size: 16px; + font-weight: 400; + visibility: visible; + + margin-right: 8px; + + height: 44px; +} + +.web-to-plex-minion.wtp--download { + background-color: #f45a26!important; +} + +.web-to-plex-minion.wtp--download:hover { + background-color: #f67e56!important; +} + +.web-to-plex-minion.wtp--found { + background-color: #ca7c1f!important; +} + +.web-to-plex-minion.wtp--found:hover { + background-color: #f8c022!important; +} + +#tt--0-0 { + background: #666!important; +} + +#tt--0-0:hover { + background: #888!important; + border-color: #888!important; +} diff --git a/src/sites/theme.css b/src/sites/theme.css index 75130c8..9aeffa3 100644 --- a/src/sites/theme.css +++ b/src/sites/theme.css @@ -49,8 +49,8 @@ BUTTON [style].web-to-plex-button... ::after */ -/* Button location */ -.web-to-plex-button.button-location-right { +/* Button location (Circular) */ +.web-to-plex-button.button-location-right:not(.button-shape-box) { left: unset !important; right: 5px !important; } @@ -68,7 +68,72 @@ BUTTON [style].web-to-plex-button... opacity: 0.10 !important; } -/* Button shape */ -.web-to-plex-button.button-shape-box { - /* ... */ +/* Button shape (Box) */ +.web-to-plex-button.button-shape-box.animate::before { + border-radius: 75px !important; +} + +.web-to-plex-button.button-shape-box, #plexit-bookmarklet-frame ~ .web-to-plex-button.button-shape-box { + border-radius: 0 !important; + border-bottom-left-radius: 2px !important; + border-bottom-right-radius: 2px !important; + + bottom: 0 !important; + margin-left: calc(50% - 34.5px) !important; + margin-top: -3px !important; + padding: 0 !important; + top: 0 !important; + + height: 20px !important; + width: 75px !important; +} + +.web-to-plex-button.button-shape-box .list-action img { + height: 16px !important; + width: 16px !important; +} + +.web-to-plex-button.button-shape-box:not(:hover) { + margin-top: -20px !important; +} + +.web-to-plex-button.button-shape-box::after { + border: 0 !important; + border-radius: 0 !important; + border-bottom: inherit !important; + + top: 100% !important; + + height: 2px !important; + width: 100% !important; +} + +.web-to-plex-button.button-shape-box:not(:hover)::after { + height: 6px !important; +} + +.web-to-plex-button.button-shape-box #wtp-list-name { + padding: 0 !important; + margin-top: 0 !important; + + width: 75px !important; +} + +.web-to-plex-button.button-shape-box #wtp-list-name img[src$="16.png"] { + height: 12px !important; +} + +.web-to-plex-button.button-shape-box:not(:hover) #wtp-list-name ~ * { + display: none !important; +} + +.web-to-plex-button.button-shape-box.open li:hover [tooltip]::before, .web-to-plex-button.button-shape-box.open [tooltip]::before { + background: #000c !important; + + left: -115px !important; + top: 30px !important; + z-index: 999999999 !important; + + height: fit-content !important; + width: 300px !important; } diff --git a/src/sites/tmdb/index.css b/src/sites/tmdb/index.css index e69de29..f6e5cef 100644 --- a/src/sites/tmdb/index.css +++ b/src/sites/tmdb/index.css @@ -0,0 +1,34 @@ +.web-to-plex-minion:hover, .web-to-plex-minion.wtp--downloader:hover, .web-to-plex-minion.wtp--found:hover { + cursor: pointer!important; + transition: background 0.2s, border 0.2s; +} + +.web-to-plex-minion, .web-to-plex-minion.wtp--downloader { + border-color: #f45a26!important; + background: #f45a26!important; + color: #fff!important; +} + +.web-to-plex-minion.wtp--downloader:hover { + border-color: #f67e56!important; + background: #f67e56!important; +} + +.web-to-plex-minion.wtp--found { + background: #e5a00d!important; + border-color: #e5a00d!important; + color: #fff!important; +} + +.web-to-plex-minion.wtp--found:hover { + background: #f9be03!important; +} + +#tt--0-0 { + background: #666!important; +} + +#tt--0-0:hover { + background: #888!important; + border-color: #888!important; +} diff --git a/src/sites/trakt/index.css b/src/sites/trakt/index.css index e69de29..5c11d0f 100644 --- a/src/sites/trakt/index.css +++ b/src/sites/trakt/index.css @@ -0,0 +1,22 @@ +.web-to-plex-minion { + background-color: #f45a26!important; + border-color: #f45a26!important; + color: #fff!important; +} + +.web-to-plex-minion:hover { + background-color: #d43a06!important; +} + +.wtp-min.under-info[title]::after { + content: attr(title); +} + +#tt--0-0 { + color: #666!important; + text-decoration: line-through!important; +} + +#tt--0-0:hover { + color: #888!important; +} diff --git a/src/sites/tvmaze/index.css b/src/sites/tvmaze/index.css index e69de29..d503832 100644 --- a/src/sites/tvmaze/index.css +++ b/src/sites/tvmaze/index.css @@ -0,0 +1,27 @@ +.web-to-plex-minion { + background: #727272; + color: #fff!important; + text-decoration: none!important; + text-transform: none!important; +} + +.web-to-plex-minion.wtp--download { + background: #f45a26!important; +} + +.web-to-plex-wrapper:hover > .web-to-plex-minion.wtp--download, .web-to-plex-minion.wtp--download:hover { + background-color: #f67e56!important; + color: #ffffff!important; +} + +.web-to-plex-wrapper:hover > .web-to-plex-minion.wtp--found, .web-to-plex-minion.wtp--found:hover { + background-color: #f8c022!important; +} + +#tt--0-0 { + background: #666!important; +} + +#tt--0-0:hover { + background: #888!important; +} diff --git a/src/sites/verizon/index.css b/src/sites/verizon/index.css index e69de29..a789420 100644 --- a/src/sites/verizon/index.css +++ b/src/sites/verizon/index.css @@ -0,0 +1,34 @@ +.web-to-plex-minion { + border-color: #f45a26!important; + background: #f45a26!important; + color: #fff; +} + +.web-to-plex-minion.wtp--download { + border-color: #f45a26!important; + background: #f45a26!important; + color: #fff!important; +} + +.web-to-plex-minion.wtp--download:hover { + background: #f67e56!important; +} + +.web-to-plex-minion.wtp--found { + background: #e5a00d!important; + border-color: #e5a00d!important; + color: #fff!important; +} + +.web-to-plex-minion.wtp--found:hover { + background: #f9be03!important; +} + +#tt--0-0 { + background: #666!important; +} + +#tt--0-0:hover { + background: #888!important; + border-color: #888!important; +} diff --git a/src/sites/vrv/index.css b/src/sites/vrv/index.css index e69de29..95d2961 100644 --- a/src/sites/vrv/index.css +++ b/src/sites/vrv/index.css @@ -0,0 +1,57 @@ +.web-to-plex-minion:nth-child(2) { + margin-left: 0.625rem; +} + +.web-to-plex-minion { + box-shadow: inset 0 0 0 0.125rem var(--orange)!important; + color: var(--orange)!important; +} + +.web-to-plex-minion:hover { + background-color: var(--orange)!important; + color: var(--light-white)!important; +} + +.web-to-plex-minion.wtp--found { + box-shadow: inset 0 0 0 0.125rem var(--orange)!important; + color: var(--orange)!important; +} + +.web-to-plex-minion.wtp--found:hover { + background-color: var(--orange)!important; + color: var(--light-white)!important; +} + +.web-to-plex-minion.wtp--download { + box-shadow: inset 0 0 0 0.125rem var(--blue)!important; + color: var(--blue)!important; +} + +.web-to-plex-minion.wtp--download:hover { + background-color: var(--blue)!important; + color: var(--light-white)!important; +} + +.web-to-plex-minion.wtp--error { + box-shadow: inset 0 0 0 0.125rem var(--red)!important; + color: var(--red)!important; +} + +.web-to-plex-minion.wtp--error:hover { + background-color: var(--red)!important; + color: var(--light-white)!important; +} + +.watchlist-card .web-to-plex-minion { + background: 0!important; + box-shadow: none!important; +} + +#tt--0-0 { + box-shadow: inset 0 0 0 0.125rem var(--grey)!important; + color: var(--grey)!important; +} + +#tt--0-0:hover { + background-color: var(--grey)!important; +} diff --git a/src/sites/vudu/index.css b/src/sites/vudu/index.css index e69de29..8af438d 100644 --- a/src/sites/vudu/index.css +++ b/src/sites/vudu/index.css @@ -0,0 +1,59 @@ +.web-to-plex-minion { + color: #566273!important; + background: transparent; + border: 2px solid #566273; + height: 30px; + font-size: 15px; + line-height: 25px; + border-radius: 6px; + font-weight: 700; + text-align: center; + display: block; + outline: none; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + cursor: pointer; + text-decoration: none!important; + margin-top: 12px; +} + +.web-to-plex-minion:hover { + opacity: 0.7; +} + +[class*="web-to-plex-wrapper"] { + width: 33.333333%!important; +} + +*:not(:first-child) ~ [class*="web-to-plex-wrapper"] { + margin-top: 35px!important; +} + +.web-to-plex-minion.wtp--download { + border-color: #f45a26!important; + color: #f45a26!important; +} + +[class*="web-to-plex-wrapper"]:hover > .web-to-plex-minion.wtp--download { + border-color: #f67e56!important; + color: #f67e56!important; +} + +.web-to-plex-minion.wtp--found { + border-color: #ca7c1f!important; + color: #ca7c1f!important; +} + +[class*="web-to-plex-wrapper"]:hover > .web-to-plex-minion.wtp--found { + border-color: #f8c022!important; + color: #f8c022!important; +} + +#tt--0-0 { + border-color: #666!important;; +} + +#tt--0-0:hover { + border-color: #888!important; +} diff --git a/src/utils.js b/src/utils.js index 26697fa..631642c 100644 --- a/src/utils.js +++ b/src/utils.js @@ -1,9 +1,105 @@ /* eslint-disable no-unused-vars */ /* global configuration, init, Update, "Helpers" */ -let configuration, init, Update; +let configuration, init, Update, IMAGES, Glyphs = {}, + HELPERS_STORAGE = { + get(keys, callback = () => {}) { + let results; + + if(keys === null) { + return callback(configuration); + } else if(keys instanceof String) { + return callback(configuration[keys]); + } else if(keys instanceof Array) { + results = [...keys]; + + for(let key of keys) + result.push(configuration[key]); + return callback(results); + } else if(keys instanceof Object) { + results = { ...keys }; + + for(let key in keys) + results[key] = configuration[key]; + return callback(results); + } + }, + + set(keys, callback = () => {}) { + let results = {}; + + for(let key in keys) + configuration[key] = results[key] = keys[key]; + return callback(results); + }, + + remove(keys, callback = () => {}) { + if(keys === null) + for(let key in configuration) + delete configuration[key]; + else if(keys instanceof String) + delete configuration[key]; + else if(keys instanceof Array) + for(let key of keys) + delete configuration[key]; + else if(keys instanceof Object) + for(let key in keys) + delete configuration[key]; + + callback(); + } + }, + MINIONS = [], + addMinions = (...minions) => { + MINIONS = [...minions, ...MINIONS]; + + return { + stayUnique: status => { + MINIONS = MINIONS.map(minion => ((!!~minions.indexOf(minion)? minion.setAttribute('ignore-web-to-plex-updates', status): null), minion)) + } + } + }; + +class UUID { + constructor(length = 16, symbol = '-') { + let values = []; + + window.crypto.getRandomValues(new Uint32Array(length)).forEach(value => values.push(value.toString(36))); + + return values.join(symbol).replace(/^[^a-z]+/i, ''); + } + + static from(object, seed = 64, symbol = '-') { + let id = []; + + for(let key in object) { + let o = object[key]; + + if(o instanceof Array) + o = o.join(symbol); + else if(o instanceof Object) + o = Object.values(o).join(symbol); + else + o = o + ''; + + if(typeof(o) == 'string') + o = o + .toLowerCase() + .split('') + .reduce((a, b) => ((typeof(a) == 'number'? a: a.charCodeAt(0)) + b.charCodeAt(0)), seed) + .toString(36); + else + return + /* Error occurred */; + + id.push(o); + } -(async date => { + return id.join('').replace(/(\w{1,8})(\w{4})?(\w{4})?(\w{4})?(\w{12})?(\w+)?/, '$1-$2-$3-$4-$5:$6').replace(/\-+\:/g, ''); + } +} + +let INITIALIZE = (async date => { // default date items let YEAR = date.getFullYear(), @@ -14,7 +110,38 @@ let configuration, init, Update; RUNNING = false, // Other items /* Items that the user has already asked for */ - CAUGHT, COMPRESS; + CAUGHT, COMPRESS, + REFINED = {}; + + // update ALL minions + let updateMinions = (attributes) => { + let { title, href, text, hover, classes, event } = attributes; + + for(let minion of MINIONS.filter(minion => minion.getAttribute('ignore-web-to-plex-updates') != 'true')) { + classes.forEach(c => minion.classList.add(c)); + minion.setAttribute('title', hover); + minion.addEventListener('click', event? event: href? (() => top.open(href, '_top')): null); + } + }, + // update a single (ID) minion + updateMinion = (properties, status, options) => { + let minions; + + for(let property in properties) + if(!(minions = $(`.web-to-plex-minion[${ property }="${properties[property]}"], .web-to-plex-minion[${ property }id="${properties[property]}"], .web-to-plex-minion[${ property }-id="${properties[property]}"]`)).empty) + break; + + if(!minions || minions.empty) + return; + + minions.forEach(minion => status? minion.classList.add(`wtp--${status}`): ''); + + if(status == 'found' && options.key) + minions.forEach(minion => { + minion.setAttribute('href', Request_PlexURL(__CONFIG__.server.id, options.key)); + minion.setAttribute('title', `Watch "${options.title} (${options.year})" on Plex`); + }); + }; // simple helpers let extURL = url => chrome.extension.getURL(url), @@ -22,9 +149,10 @@ let configuration, init, Update; // DO NOT EXPOSE __CONFIG__, ALLOWED, PERMISS; - let IMG_URL = { + let IMG_URL = IMAGES = { 'nil': extURL('img/null.png'), 'icon_16': extURL('img/16.png'), + 'icon_32': extURL('img/32.png'), 'icon_48': extURL('img/48.png'), 'background': extURL('img/background.png'), 'hide_icon_16': extURL('img/hide.16.png'), @@ -34,6 +162,7 @@ let configuration, init, Update; 'close_icon_16': extURL('img/close.16.png'), 'close_icon_48': extURL('img/close.48.png'), 'icon_white_16': extURL('img/_16.png'), + 'icon_white_32': extURL('img/_32.png'), 'icon_white_48': extURL('img/_48.png'), 'plexit_icon_16': extURL('img/plexit.16.png'), 'plexit_icon_48': extURL('img/plexit.48.png'), @@ -46,6 +175,15 @@ let configuration, init, Update; 'settings_icon_48': extURL('img/settings.48.png'), }; + for(let glyph of "adjust,airplane,alarm,albums,amazon,anchor,android,apple,asterisk,ax,badoo,ban,bank,barcode,baseball,basketball,bathrobe,beer,behance,bell,bicycle,bin,binoculars,blacksmith,blog,blogger,bluetooth,boat,bold,bomb,book,bookmark,bowling,briefcase,brush,bug,building,bullets,bullhorn,buoy,bus,cake,calculator,calendar,camera,candle,car,cardio,cargo,cars,celebration,certificate,charts,chat,check,cleaning,clock,cloud,cogwheel,cogwheels,coins,collapse,comments,compass,compressed,conversation,crop,crown,cup,cutlery,dashboard,delete,deviantart,direction,dislikes,display,divide,dog,download,dress,dribbble,drink,dropbox,dumbbell,earphone,edit,eject,electricity,embed,envelope,euro,evernote,exit,expand,eyedropper,fabric,facebook,factory,fax,female,file,film,filter,fins,fire,fishes,flag,flash,flickr,flower,font,forrst,forward,foursquare,fullscreen,gamepad,gbp,gift,girl,github,glass,global,globe,golf,goodreads,google_plus,grater,group,hdd,header,headphones,headset,heart,heat,history,hockey,home,hospital,imac,inbox,instagram,instapaper,ios,ipad,iphone,ipod,italic,jolicloud,justify,kettle,keynote,keys,kiosk,last_fm,leaf,leather,lightbulb,link,linked_in,list,lock,luggage,macbook,magic,magnet,male,microphone,minus,money,moon,more,move,music,mute,myspace,nails,nameplate,note,notes,ok,package,pants,paperclip,parents,pause,pen,pencil,piano,picasa,picture,pin,pinboard,pinterest,pipe,pizza,play,playlist,playstation,plus,podium,pool,posterous_spaces,pot,power,print,projector,pushpin,qrcode,quora,rabbit,radar,random,read_it_later,readability,record,redo,refresh,remove,repeat,restart,retweet,rewind,riflescope,ring,road,roundabout,router,rss,rugby,ruller,sampler,scissors,screenshot,search,send,server,settings,share,shield,shirt,shop,signal,skateboard,skitch,skull,skype,smoking,snowflake,sort,sorting,spade,spotify,spray,star,stats,stop,stopwatch,stroller,stumbleupon,subtitles,suitcase,sun,sweater,table,tablet,tag,tags,tie,tint,tower,train,transfer,translate,truck,tumblr,turtle,twitter,umbrella,unchecked,underwear,undo,unlock,unshare,upload,usd,user,vases,vcard,vimeo,vine,wallet,webcam,wifi,windows,woman,wordpress,wrench,xbox,xing,yahoo,yelp,youtube,zootool".split(',')) + Object.defineProperty(Glyphs, glyph, { + get() { return document.furnish('i', { glyph }) }, + set(value) { return document.furnish('i', { glyph: value }) }, + + configurable: true, + enumerable: true, + }); + // the storage - priority to sync const UTILS_STORAGE = chrome.storage.sync || chrome.storage.local; @@ -64,7 +202,7 @@ let configuration, init, Update; UTILS_STORAGE.get(null, DISK => { if(chrome.runtime.lastError) - chrome.storage.local.get(null, LOAD); + UTILS_STORAGE.get(null, LOAD); else LOAD(DISK); }); @@ -302,7 +440,7 @@ let configuration, init, Update; ); let preX = document.queryBy('.web-to-plex-prompt').first, - movie = /^(m(?:ovies?)?|f(?:ilms?)?|c(?:inemas?)?)?$/i; + movie = /^(m(?:ovies?)?|f(?:ilms?)?|c(?:inemas?)?|theat[re]{2})?$/i; if(preX) return /* Ignore while another prompt is open, prevents double prompts */; @@ -329,7 +467,7 @@ let configuration, init, Update; header.innerText = 'Approve ' + counter.children.length + (counter.children.length == 1?' item': ' items'); }; - prompt = furnish('div.web-to-plex-prompt', {}, + prompt = furnish('div.web-to-plex-prompt', { type: prompt_type }, furnish('div.web-to-plex-prompt-body', { style: `background-image: url(${ IMG_URL.noise_background }), url(${ IMG_URL.background }); background-size: auto, cover;` }, // The prompt's title furnish('h1.web-to-plex-prompt-header', {}, 'Approve ' + array.length + (array.length == 1? ' item': ' items')), @@ -346,14 +484,14 @@ let configuration, init, Update; elements.push( furnish('li.web-to-plex-prompt-option.mutable', { value: index, innerHTML: `

${ index + 1 }. ${ title }${ year? ` (${ year })`: '' } \u2014 ${ type }

` }, - furnish('button.remove', { title: `Remove "${ title }"`, onmouseup: event => { remove(event.target.parentElement); event.target.remove() } }), + furnish('i[glyph=remove]', { title: `Remove "${ title }"`, onmouseup: event => { remove(event.target.parentElement); event.target.remove() } }), ( __CONFIG__.PromptQuality? - P_QUA = furnish('select.quality', { index, onchange: event => data[event.target.getAttribute('index')].quality = event.target.value }, ...profiles[/(movie|film|cinema)/i.test(type)?'movie':'show'].map(Q => furnish('option', { value: Q.id }, Q.name))): + P_QUA = furnish('select.quality', { index, onchange: event => data[event.target.getAttribute('index')].quality = event.target.value }, ...profiles[movie.test(type)?'movie':'show'].map(Q => furnish('option', { value: Q.id }, Q.name))): '' ),( __CONFIG__.PromptLocation? - P_LOC = furnish('select.location', { index, onchange: event => data[event.target.getAttribute('index')].location = event.target.value }, ...locations[/(movie|film|cinema)/i.test(type)?'movie':'show'].map(Q => furnish('option', { value: Q.id }, Q.path))): + P_LOC = furnish('select.location', { index, onchange: event => data[event.target.getAttribute('index')].location = event.target.value }, ...locations[movie.test(type)?'movie':'show'].map(Q => furnish('option', { value: Q.id }, Q.path))): '' ) ) @@ -374,7 +512,7 @@ let configuration, init, Update; furnish('input.web-to-plex-prompt-input[type=text]', { placeholder: 'Add an item (enter to add): Title (Year) Type / ID Type', title: 'Solo: A Star Wars Story (2018) movie / tt3778644 m', onkeydown: async event => { if(event.keyCode == 13) { let title, year, type, self = event.target, R = RegExp, - movie = /^(m(?:ovies?)?|f(?:ilms?)?|c(?:inemas?)?)/i, + movie = /^(m(?:ovies?)?|f(?:ilms?)?|c(?:inemas?)?|theat[re]{2})/i, Db, IMDbID, TMDbID, TVDbID, value = self.value; self.setAttribute('disabled', self.disabled = true); @@ -420,9 +558,9 @@ let configuration, init, Update; } } } }), - furnish('button.web-to-plex-prompt-decline', { onmouseup: event => { remove(true); callback([]) }, title: 'Close' }, '\u2718'), - furnish('button.web-to-plex-prompt-accept', { onmouseup: event => { remove(true); new Prompt(prompt_type, options, callback, container) }, title: 'Reset' }, '\u21BA'), - furnish('button.web-to-plex-prompt-accept', { onmouseup: event => { remove(true); callback(data.filter(value => value !== null && value !== undefined)) }, title: 'Continue' }, '\u2714') + furnish('button.web-to-plex-prompt-decline', { onmouseup: event => { remove(true); callback([]) }, title: 'Close' }, Glyphs.exit), + furnish('button.web-to-plex-prompt-accept', { onmouseup: event => { remove(true); new Prompt(prompt_type, options, callback, container) }, title: 'Reset' }, Glyphs.restart), + furnish('button.web-to-plex-prompt-accept', { onmouseup: event => { remove(true); callback(data.filter(value => value !== null && value !== undefined)) }, title: 'Continue' }, Glyphs.ok) ) ) ); @@ -448,7 +586,7 @@ let configuration, init, Update; header.innerText = `Correction ready...`; }; - prompt = furnish('div.web-to-plex-prompt', {}, + prompt = furnish('div.web-to-plex-prompt', { type: prompt_type }, furnish('div.web-to-plex-prompt-body', { style: `background-image: url(${ IMG_URL.noise_background }), url(${ IMG_URL.background }); background-size: auto, cover;` }, // The prompt's title furnish('h1.web-to-plex-prompt-header', {}, (array.length == 1? 'Correction ready...': `Choose a correction from ${array.length} items`)), @@ -477,16 +615,21 @@ let configuration, init, Update; captured.tvdb.push(v); elements.push( - furnish('li.web-to-plex-prompt-option.mutable', { value: index, innerHTML: `

${ index + 1 }. ${ title }${ year? ` (${ year })`: '' } \u2014 ${ type }

${ i? `${i}`: '/' } \u2014 ${ t? `${t}`: '/' } \u2014 ${ v? `${v}`: '/' }` }, - furnish('button.choose', { title: `Use "${ title } (${ year })"`, onmouseup: event => { - let element = event.target.parentElement, - children = [...element.parentElement.children].filter(e => e != element); + furnish('li.web-to-plex-prompt-option.mutable.choose', { + value: index, + innerHTML: `

${ index + 1 }. ${ title }${ year? ` (${ year })`: '' } \u2014 ${ type }

${ i? `${i}`: '/' } \u2014 ${ t? `${t}`: '/' } \u2014 ${ v? `${v}`: '/' }`, + onmouseup: event => { + let self = traverse(event.target, element => element.classList.contains('mutable')), + children = [...self.parentElement.children].filter(e => e != self); children.forEach(child => { + child.classList.remove('chosen'); remove(child); - element.parentElement.appendChild(child); + self.parentElement.appendChild(child); }); - element.classList.add('chosen'); - } }) + self.classList.add('chosen'); + } + }, + furnish('i[glyph=ok]', { title: `Use "${ title } (${ year })"` }) ) ); } @@ -545,9 +688,9 @@ let configuration, init, Update; } } } }), - furnish('button.web-to-plex-prompt-decline', { onmouseup: event => { remove(true); prompt.done = true; callback([]) }, title: 'Close' }, '\u2718'), - furnish('button.web-to-plex-prompt-accept', { onmouseup: event => { remove(true); prompt.done = false; new Prompt(prompt_type, options, callback, container) }, title: 'Reset' }, '\u21BA'), - furnish('button.web-to-plex-prompt-accept', { onmouseup: event => { remove(true); prompt.done = true; callback(data.filter(value => value !== null && value !== undefined)) }, title: 'Continue' }, '\u2714') + furnish('button.web-to-plex-prompt-decline', { onmouseup: event => { remove(true); prompt.done = true; callback([]) }, title: 'Close' }, Glyphs.exit), + furnish('button.web-to-plex-prompt-accept', { onmouseup: event => { remove(true); prompt.done = false; new Prompt(prompt_type, options, callback, container) }, title: 'Reset' }, Glyphs.restart), + furnish('button.web-to-plex-prompt-accept', { onmouseup: event => { remove(true); prompt.done = true; callback(data.filter(value => value !== null && value !== undefined)) }, title: 'Continue' }, Glyphs.ok) ) ) ); @@ -569,7 +712,7 @@ let configuration, init, Update; header.innerText = 'Approve ' + counter.children.length + (counter.children.length == 1?' item': ' items'); }; - prompt = furnish('div.web-to-plex-prompt', {}, + prompt = furnish('div.web-to-plex-prompt', { type: prompt_type }, furnish('div.web-to-plex-prompt-body', { style: `background-image: url(${ IMG_URL.noise_background }), url(${ IMG_URL.background }); background-size: auto, cover;` }, // The prompt's title furnish('h1.web-to-plex-prompt-header', {}, 'Approve ' + array.length + (array.length == 1? ' item': ' items')), @@ -586,21 +729,21 @@ let configuration, init, Update; elements.push( furnish('li.web-to-plex-prompt-option.mutable', { value: index, innerHTML: `

${ index + 1 } \u00b7 ${ title }${ year? ` (${ year })`: '' } \u2014 ${ type }

` }, - furnish('button.remove', { title: `Remove "${ title }"`, onmouseup: event => { remove(event.target.parentElement); event.target.remove() } }), + furnish('i[glyph=remove]', { title: `Remove "${ title }"`, onmouseup: event => { remove(event.target.parentElement); event.target.remove() } }), ( __CONFIG__.PromptQuality? - P_QUA = furnish('select.quality', { index, onchange: event => data[event.target.getAttribute('index')].quality = event.target.value }, ...profiles[/(movie|film|cinema)/i.test(type)?'movie':'show'].map(Q => furnish('option', { value: Q.id }, Q.name))): + P_QUA = furnish('select.quality', { index, onchange: event => data[+event.target.getAttribute('index')].quality = event.target.value }, ...profiles[movie.test(type)?'movie':'show'].map(Q => furnish('option', { value: Q.id }, Q.name))): '' ),( __CONFIG__.PromptLocation? - P_LOC = furnish('select.location', { index, onchange: event => data[event.target.getAttribute('index')].location = event.target.value }, ...locations[/(movie|film|cinema)/i.test(type)?'movie':'show'].map(Q => furnish('option', { value: Q.id }, Q.path))): + P_LOC = furnish('select.location', { index, onchange: event => data[+event.target.getAttribute('index')].location = event.target.value }, ...locations[movie.test(type)?'movie':'show'].map(Q => furnish('option', { value: Q.id }, Q.path))): '' ) ) ); - if(P_QUA) P_QUA.value = defaults[type].quality; - if(P_LOC) P_LOC.value = defaults[type].location; + if(P_QUA) P_QUA.value = data[index].quality = defaults[type].quality; + if(P_LOC) P_LOC.value = data[index].location = defaults[type].location; P_QUA = P_LOC = null; } @@ -611,9 +754,9 @@ let configuration, init, Update; // The engagers furnish('div.web-to-plex-prompt-footer', {}, - furnish('button.web-to-plex-prompt-decline', { onmouseup: event => { remove(true); callback([]) }, title: 'Close' }, '\u2718'), - furnish('button.web-to-plex-prompt-accept', { onmouseup: event => { remove(true); new Prompt(prompt_type, options, callback, container) }, title: 'Reset' }, '\u21BA'), - furnish('button.web-to-plex-prompt-accept', { onmouseup: event => { remove(true); callback(data.filter(value => value !== null && value !== undefined)) }, title: 'Continue' }, '\u2714') + furnish('button.web-to-plex-prompt-decline', { onmouseup: event => { remove(true); callback([]) }, title: 'Close' }, Glyphs.exit), + furnish('button.web-to-plex-prompt-accept', { onmouseup: event => { remove(true); new Prompt(prompt_type, options, callback, container) }, title: 'Reset' }, Glyphs.restart), + furnish('button.web-to-plex-prompt-accept', { onmouseup: event => { remove(true); callback(data.filter(value => value)) }, title: 'Continue' }, Glyphs.ok) ) ) ); @@ -622,8 +765,15 @@ let configuration, init, Update; /* Allows the user to modify a single item (before being pushed) */ case 'modify': let { title, year, type, IMDbID, TMDbID, TVDbID } = options, + refined = { ...defaults[type], ...options }, + uuid = UUID.from({ type, title, IMDbID, TMDbID, TVDbID }), P_QUA, P_LOC; + if(REFINED[uuid]) + refined = REFINED[uuid]; + else + REFINED[uuid] = refined; + let i = IMDbID, t = TMDbID, v = TVDbID, @@ -644,34 +794,34 @@ let configuration, init, Update; element.remove(); }; - type = /(movie|film|cinema)/i.test(type)?'movie':'show'; + type = /(movie|film|cinema|theat[re]{2})s?/i.test(type)?'movie':'show'; - prompt = furnish('div.web-to-plex-prompt', {}, + prompt = furnish('div.web-to-plex-prompt', { type: prompt_type }, furnish('div.web-to-plex-prompt-body', { style: `background-image: url(${ IMG_URL.noise_background }), url(${ IMG_URL.background }); background-size: auto, cover;` }, // The prompt's title - furnish('h1.web-to-plex-prompt-header', { innerHTML: `${ title }${ year? ` (${ year })`: '' }` }), + furnish('h1.web-to-plex-prompt-header', { innerHTML: `${ title.length < 40? title: title.slice(0, 37) + '...' }${ parseInt(year)? ` (${ year })`: '' } \u2014 ${ movie.test(type)? 'Movie': 'TV Show' }` }), // The prompt's items furnish('div.web-to-plex-prompt-options', {}, - furnish('div.web-to-plex-prompt-option', { innerHTML: `${ type } \u2014 ${ i? `${i}`: '/' } \u2014 ${ t? `${t}`: '/' } \u2014 ${ v? `${v}`: '/' }` }), + furnish('div.web-to-plex-prompt-option', { innerHTML: `${ i? `${i}`: '?' } \u2014 ${ t? `${t}`: '?' } \u2014 ${ v? `${v}`: '?' }` }), ( __CONFIG__.PromptQuality? - P_QUA = furnish('select.quality', { onchange: event => options.quality = event.target.value }, ...profiles[type].map(Q => furnish('option', { value: Q.id }, Q.name))): + P_QUA = furnish('select.quality', { onchange: event => REFINED[uuid].quality = event.target.value }, ...profiles[type].map(Q => furnish('option', { value: Q.id }, Q.name))): '' ), furnish('br'), ( __CONFIG__.PromptLocation? - P_LOC = furnish('select.location', { onchange: event => options.location = event.target.value }, ...locations[type].map(Q => furnish('option', { value: Q.id }, Q.path))): + P_LOC = furnish('select.location', { onchange: event => REFINED[uuid].location = event.target.value }, ...locations[type].map(Q => furnish('option', { value: Q.id }, Q.path))): '' ) ), // The engagers furnish('div.web-to-plex-prompt-footer', {}, - furnish('button.web-to-plex-prompt-decline', { onmouseup: event => { remove(true); callback([]) }, title: 'Close' }, '\u2718'), - furnish('button.web-to-plex-prompt-accept', { onmouseup: event => { remove(true); new Prompt(prompt_type, options, callback, container) }, title: 'Reset' }, '\u21BA'), - furnish('button.web-to-plex-prompt-accept', { onmouseup: event => { remove(true); callback(options) }, title: 'Continue' }, '\u2714'), + furnish('button.web-to-plex-prompt-decline', { onmouseup: event => { remove(true); callback([]) }, title: 'Close' }, Glyphs.exit), + furnish('button.web-to-plex-prompt-accept', { onmouseup: event => { remove(true); new Prompt(prompt_type, REFINED[uuid], callback, container) }, title: 'Reset' }, Glyphs.restart), + furnish('button.web-to-plex-prompt-accept', { onmouseup: event => { remove(true); callback(REFINED[uuid]) }, title: 'Continue' }, Glyphs.ok), ( (!__CONFIG__.UseLowCache || (__CONFIG__.UseLowCache && CAUGHT.has({ imdb: i, tmdb: t, tvdb: v })))? furnish('button.web-to-plex-prompt-accept', { onmouseup: event => { let self = event.target; open(self.getAttribute('href'), '_blank') }, href: slugify(type), title: `Open on ${ manager[type] }` }, manager[type]): @@ -722,14 +872,14 @@ let configuration, init, Update; (init && !RUNNING? (init(), RUNNING = true): RUNNING = false); }; - prompt = furnish('div.web-to-plex-prompt', {}, + prompt = furnish('div.web-to-plex-prompt', { type: prompt_type }, furnish('div.web-to-plex-prompt-body', { style: `background-image: url(${ IMG_URL.noise_background }), url(${ IMG_URL.background }); background-size: auto, cover;` }, // The prompt's title furnish('h1.web-to-plex-prompt-header', { innerHTML: `"${ alias || name }" would like:` }), // The prompt's items furnish('div.web-to-plex-prompt-options', {}, - ...((permissions = permission.split(/\s*,\s*/).filter(v=>v&&v.length)).map( + ...((permissions = permission.split(/\s*,\s*/).filter((v,i,a)=>v&&v.length&&a.indexOf(v)==i)).map( __permission => furnish('div.web-to-plex-prompt-option.web-to-plex-permission', { innerHTML: `Access to your ${ __permission.replace(/(y)?s?$/, ($0, $1, $$, $_) => ($1? 'ies': 's')) } — ` + (p => { let R = RegExp, @@ -773,8 +923,8 @@ let configuration, init, Update; // The engagers furnish('div.web-to-plex-prompt-footer', {}, - furnish('button.web-to-plex-prompt-decline', { onmouseup: event => { if(!event.isTrusted) throw alert('The script for this site is trying to decline its own permissions!'), 'Malicious script. Decline permissions'; remove(true); callback(false, {}) }, title: 'Deny all permissions' }, '\u2718'), - furnish('button.web-to-plex-prompt-accept', { onmouseup: async event => { if(!event.isTrusted) throw alert('The script for this site is trying to grant its own permissions!'), 'Malicious script. Grant permissions'; remove(true); await callback(true, permissions); top.open(top.location.href, '_top'); }, title: 'Allow all permissions' }, '\u2714') + furnish('button.web-to-plex-prompt-decline', { onmouseup: event => { if(!event.isTrusted) throw alert('The script for this site is trying to decline its own permissions!'), 'Malicious script. Decline permissions'; remove(true); callback(false, {}) }, title: 'Deny all permissions' }, Glyphs.ban), + furnish('button.web-to-plex-prompt-accept', { onmouseup: async event => { if(!event.isTrusted) throw alert('The script for this site is trying to grant its own permissions!'), 'Malicious script. Grant permissions'; remove(true); await callback(true, permissions); top.open(top.location.href, '_top'); }, title: 'Allow all permissions' }, Glyphs.ok) ) ) ); @@ -806,7 +956,7 @@ let configuration, init, Update; src: url, style: ` display: none !important; - opacity: 0 !important; + opacity: 0 !important; visibility: hidden !important; `, @@ -824,6 +974,8 @@ let configuration, init, Update; // Send an update query to background.js Update = (type, options = {}, postToo) => { + Update.running = options.script || options.plugin || null; + if(configuration) console.log(`Requesting update (${ type } [post-to-top=${ !!postToo }])`, options); else if(!Update.retry) @@ -1006,7 +1158,7 @@ let configuration, init, Update; UTILS_STORAGE.get(null, options => { if(chrome.runtime.lastError) - chrome.storage.local.get(null, handleOptions); + UTILS_STORAGE.get(null, handleOptions); else handleOptions(options); }); @@ -1017,13 +1169,17 @@ let configuration, init, Update; async function ParsedOptions() { return await options() .then( - options => { + async options => { configuration = {}; + let { running } = Update, + allowed = await load(`has/${ running }`), + permiss = await load(`get/${ running }`); + /* Don't expose the user's authentication information to sites */ for(let key in options) if(/username|password|token|api|server|url|storage|cache|proxy|client|builtin|plugin|qualit/i.test(key)) - if(ALLOWED && RegExp(PERMISS.join('|'),'i').test(key)) + if(allowed && RegExp(permiss.join('|'),'i').test(key)) configuration[key] = options[key]; else /* Do nothing */; @@ -1439,6 +1595,8 @@ let configuration, init, Update; .replace(/@\{b(ase-?)?64-url\}/gi, btoa(URL)) .replace(/@\{enc(ode)?-url\}/gi, encodeURIComponent(URL)) .replace(/@\{(raw-)?url\}/gi, URL); + + headers[$1] = $2; } }); @@ -1464,7 +1622,7 @@ let configuration, init, Update; rqut = apit, // request type: tmdb, imdb, or tvdb manable = __CONFIG__.ManagerSearch && !(rerun & 0b1000), // is the user's "Manager Searches" option enabled? UTF_16 = /[^0\u0020-\u007e, 1\u00a1\u00bf-\u00ff, 2\u0100-\u017f, 3\u0180-\u024f, 4\u0300-\u036f, 5\u0370-\u03ff, 6\u0400-\u04ff, 7\u0500-\u052f, 8\u20a0-\u20bf]+/g, - MV = /^(movies?|films?|cinemas?)$/i.test(apit), + MV = /^(movies?|films?|cinemas?|theat[re]{2}s?)$/i.test(apit), TV = /^(tv[\s\-]*(?:shows?|series)?)$/i.test(apit); iid = iid == 'tt'? null: iid; @@ -1473,7 +1631,7 @@ let configuration, init, Update; rqut = /(tv|show|series)/i.test(rqut)? 'tvdb': - /(movie|film|cinema)s?/i.test(rqut)? + /(movie|film|cinema|theat[re])s?/i.test(rqut)? 'tmdb': rqut || '*'; manable = manable && (__CONFIG__.usingOmbi || (__CONFIG__.usingRadarr && rqut == 'tmdb') || ((__CONFIG__.usingSonarr || __CONFIG__.usingMedusa /*|| __CONFIG__.usingSickBeard*/) && rqut == 'tvdb')); @@ -1570,7 +1728,7 @@ let configuration, init, Update; cors = proxy.url, // if cors is requried and not uspported, proxy through this URL headers = HandleProxyHeaders(proxy.headers, url); - if(proxy.enabled && /(^http:\/\/)(?!localhost|127\.0\.0\.1(?:\/8)?|::1(?:\/128)?|:\d+)\b/i.test(url)) { + if(proxy.enabled && /(^https?:\/\/)(?!localhost|127\.0\.0\.1(?:\/8)?|::1(?:\/128)?|:\d+)\b/i.test(url)) { url = cors .replace(/\{b(ase-?)?64-url\}/gi, btoa(url)) .replace(/\{enc(ode)?-url\}/gi, encodeURIComponent(url)) @@ -2025,7 +2183,7 @@ let configuration, init, Update; ); } - let contentType = (/movies?|film/i.test(options.type)? 'movie': 'tv'); + let contentType = (/(movie|film|cinema|theat[re]{2})s?/i.test(options.type)? 'movie': 'tv'); chrome.runtime.sendMessage({ type: 'PUSH_OMBI', @@ -2156,10 +2314,12 @@ let configuration, init, Update; if(!prompted && (PromptQuality || PromptLocation)) return new Prompt('modify', options, refined => Request_Radarr(refined, true)); + let parsePath = id => JSON.parse(__CONFIG__.radarrStoragePaths).map(item => item.id == id? item: null).filter(n => n)[0].path.replace(/\\/g, '\\\\'); + if(PromptQuality && +options.quality > 0) PromptValues.QualityID = +options.quality; - if(PromptLocation && options.location) - PromptValues.StoragePath = JSON.parse(__CONFIG__.radarrStoragePaths).map(item => item.id == options.location? item: null).filter(n => n)[0].path.replace(/\\/g, '\\\\'); + if(PromptLocation && +options.location > 0) + PromptValues.StoragePath = parsePath(options.location); new Notification('info', `Sending "${ options.title }" to Radarr`, 3000); @@ -2167,7 +2327,7 @@ let configuration, init, Update; type: 'PUSH_RADARR', url: `${ __CONFIG__.radarrURL }api/movie/`, token: __CONFIG__.radarrToken, - StoragePath: __CONFIG__.radarrStoragePath, + StoragePath: parsePath(__CONFIG__.radarrStoragePath), QualityID: __CONFIG__.radarrQualityProfileId, basicAuth: __CONFIG__.radarrBasicAuth, title: options.title, @@ -2213,10 +2373,12 @@ let configuration, init, Update; if(!prompted && (PromptQuality || PromptLocation)) return new Prompt('modify', options, refined => Request_Sonarr(refined, true)); + let parsePath = id => JSON.parse(__CONFIG__.sonarrStoragePaths).map(item => item.id == id? item: null).filter(n => n)[0].path.replace(/\\/g, '\\\\'); + if(PromptQuality && +options.quality > 0) PromptValues.QualityID = +options.quality; - if(PromptLocation && options.location) - PromptValues.StoragePath = JSON.parse(__CONFIG__.sonarrStoragePaths).map(item => item.id == options.location? item: null).filter(n => n)[0].path.replace(/\\/g, '\\\\'); + if(PromptLocation && +options.location > 0) + PromptValues.StoragePath = parsePath(options.location); new Notification('info', `Sending "${ options.title }" to Sonarr`, 3000); @@ -2224,7 +2386,7 @@ let configuration, init, Update; type: 'PUSH_SONARR', url: `${ __CONFIG__.sonarrURL }api/series/`, token: __CONFIG__.sonarrToken, - StoragePath: __CONFIG__.sonarrStoragePath, + StoragePath: parsePath(__CONFIG__.sonarrStoragePath), QualityID: __CONFIG__.sonarrQualityProfileId, basicAuth: __CONFIG__.sonarrBasicAuth, title: options.title, @@ -2384,28 +2546,24 @@ let configuration, init, Update; let { __theme } = __CONFIG__; - let ThemeClasses = JSON.parse(COMPRESS? iBWT(unzip(decompress(__theme))): __theme), + __theme = JSON.parse(COMPRESS? iBWT(unzip(decompress(__theme))): __theme); + + let ThemeClasses = [], HeaderClasses = [], ParsedAttributes = {}; // Theme(s) + for(let theme in __theme) + if((typeof __theme[theme]) == 'boolean') + ThemeClasses.push(theme); + else + ParsedAttributes[theme] = __theme[theme]; + if(!ThemeClasses.length) ThemeClasses = ''; else ThemeClasses = '.' + ThemeClasses.join('.'); - ThemeClasses = ThemeClasses.split('.').filter(v => { - let R = RegExp; - - if(/([^=]+?)=([^.]+?)/.test(v)) { - ParsedAttributes[R.$1] = R.$2; - - return false; - } - - return true; - }).join('.'); - // Header(s) for(let header in headers) if(headers[header]) @@ -2418,7 +2576,7 @@ let configuration, init, Update; // +
- +
- Advance + Advanced

Plex Server Options

- +
Use this to communicate directly with your Plex server.
Such as http://localhost:32400/ or http://192.168.1.100:32400/ @@ -111,14 +112,14 @@

Plex Server Options

Ombi (Movies/TV Shows) - +

Connection Settings

- +
Such as https://example.com/ombi or http://192.168.1.100:5000
@@ -126,35 +127,38 @@

Connection Settings

- +
- 1. Go to Ombi | Settings | Ombi
- 2. Copy/Paste the "API Key."
- Such as aa756d33242f6g8ffbca2b3963586f21 + 1. Go to OmbiSettingsOmbi
+ 2. Copy/Paste the API Key
+ — Such as aa756d33242f6a8ffbca2b3963586f21
- +


- +
@@ -163,14 +167,14 @@

Login (saved)

Watcher (Movies) - +

Connection Settings

- +
Such as https://example.com/watcher or http://192.168.1.100:9090
@@ -178,63 +182,68 @@

Connection Settings

- +
- 1. Go to Watcher | Settings | Server
- 2. Copy/Paste the "API Key."
- Such as aa756d33242f6g8ffbca2b3963586f21 + 1. Go to WatcherSettingsServer
+ 2. Copy/Paste the API Key
+ — Such as aa756d33242f6a8ffbca2b3963586f21
-

Login (saved)

+

Login (saved)

The default username is watcher. The default password is rehctaw.

- +
Only use this if you setup a Watcher username.
- +
Only use this if you setup a Watcher password.
Your password will be hidden once saved.

-
This information will be used for Basic Access Authentication only. This will allow the extension to ask Watcher for your list of films, or to add to your list of films.
+
+ This information will be used for Basic Access Authentication only.
+ This will allow the extension to ask Watcher for your list of films, or to add to your list of films. +
- +
- +
- +


- +
Radarr (Movies) - +

Connection Settings

- +
Such as https://example.com/radarr or http://192.168.1.100:7878
@@ -242,37 +251,40 @@

Connection Settings

- +
- 1. Go to Radarr | Settings | General
- 2. Click on "Show advance," then copy/paste the "API Key."
- Such as aa756d33242f6g8ffbca2b3963586f21 + 1. Go to RadarrSettingsGeneral
+ 2. Click on Show Advanced then copy/paste the API Key
+ — Such as aa756d33242f6a8ffbca2b3963586f21
-

Login (saved)

+

Login (saved)

- +
Only use this if you setup a Radarr username.
- +
Only use this if you setup a Radarr password.
Your password will be hidden once saved.

-
This information will be used for Basic Access Authentication only. This will allow the extension to ask Radarr for your list of films, or to add to your list of films.
+
+ This information will be used for Basic Access Authentication only.
+ This will allow the extension to ask Radarr for your list of films, or to add to your list of films. +
- +
- +
This should be the same path (verbatim) used in Radarr. @@ -280,27 +292,27 @@

Login (saved)

- +


- +
CouchPotato (Movies) - +

Connection Settings

- +
Such as https://example.com/couchpotato or http://192.168.1.100:5050
@@ -308,38 +320,41 @@

Connection Settings

- +
- 1. Go to CouchPotato | Settings
- 2. Copy/Paste the "API Key."
- Such as aa756d33242f6g8ffbca2b3963586f21 + 1. Go to CouchPotatoSettings
+ 2. Copy/Paste the API Key
+ — Such as aa756d33242f6a8ffbca2b3963586f21
-

Login (saved)

+

Login (saved)

- +
Only use this if you setup a CouchPotato username.
- +
Only use this if you setup a CouchPotato password.
Your password will be hidden once saved.
-
This information will be used for Basic Access Authentication only. This will allow the extension to ask Couchpotato for your list of films, or to add to your list of films.
+
+ This information will be used for Basic Access Authentication only.
+ This will allow the extension to ask Couchpotato for your list of films, or to add to your list of films. +
@@ -348,7 +363,7 @@

Login (saved)



- +
@@ -357,14 +372,14 @@

Login (saved)

Medusa (TV Shows) - +

Connection Settings

- +
Such as https://example.com/medusa or http://192.168.1.100:8081
@@ -372,35 +387,38 @@

Connection Settings

- +
- 1. Go to Medusa | Settings | General | Interface | Web Interface
- 2. Copy/Paste the "API key."
- Such as aa756d33242f6g8ffbca2b3963586f21 + 1. Go to MedusaSettingsGeneralInterfaceWeb Interface
+ 2. Copy/Paste the API key
+ — Such as aa756d33242f6a8ffbca2b3963586f21
-

Login (saved)

+

Login (saved)

- +
- +
Your password will be hidden once saved.

-
This information will be used for Basic Access Authentication only. This will allow the extension to ask Medusa for your list of TV shows, or to add to your list of TV shows.
+
+ This information will be used for Basic Access Authentication only.
+ This will allow the extension to ask Medusa for your list of TV shows, or to add to your list of TV shows. +
- +
- +
This should be the same path (verbatim) used in Medusa. @@ -408,33 +426,33 @@

Login (saved)

- - +


- +
Sonarr (TV Shows) - +

Connection Settings

- +
Such as https://example.com/sonarr or http://192.168.1.100:8989
@@ -442,37 +460,40 @@

Connection Settings

- +
- 1. Go to Sonarr | Settings | General
- 2. Click on "Show advance," then copy/paste the "API Key."
- Such as aa756d33242f6g8ffbca2b3963586f21 + 1. Go to SonarrSettingsGeneral
+ 2. Click on Show Advanced then copy/paste the API Key
+ — Such as aa756d33242f6a8ffbca2b3963586f21
-

Login (saved)

+

Login (saved)

- +
Only use this if you setup a Sonarr username.
- +
Only use this if you setup a Sonarr password.
Your password will be hidden once saved.

-
This information will be used for Basic Access Authentication only. This will allow the extension to ask Sonarr for your list of TV shows, or to add to your list of TV shows.
+
+ This information will be used for Basic Access Authentication only.
+ This will allow the extension to ask Sonarr for your list of TV shows, or to add to your list of TV shows. +
- +
- +
This should be the same path (verbatim) used in Sonarr. @@ -480,27 +501,27 @@

Login (saved)

- +


- +
Sick Beard (TV Shows) - +

Connection Settings

- +
Such as https://example.com/sickBeard or http://192.168.1.100:8081
@@ -508,37 +529,40 @@

Connection Settings

- +
- 1. Go to Sick Beard | Config | General | API
- — a. Ensure the checkbox "Enable API" is enabled
- — b. Press the "Generate" button
- 2. Copy/Paste the "API Key."
- Such as aa756d33242f6g8ffbca2b3963586f21 + 1. Go to Sick BeardConfigGeneralAPI
+ — a. Ensure the checkbox Enable API is checked
+ — b. Press the Generate button
+ 2. Copy/Paste the API Key
+ — Such as aa756d33242f6a8ffbca2b3963586f21
-

Login (saved)

+

Login (saved)

- +
- +
Your password will be hidden once saved.

-
This information will be used for Basic Access Authentication only. This will allow the extension to ask Sick Beard for your list of TV shows, or to add to your list of TV shows.
+
+ This information will be used for Basic Access Authentication only.
+ This will allow the extension to ask Sick Beard for your list of TV shows, or to add to your list of TV shows. +
- +
- +
This should be the same path (verbatim) used in Sick Beard. @@ -546,13 +570,13 @@

Login (saved)

- - + - + +

+
+ — a minimalist, rectangular button (top center of page) +
+
+ — a normal, circular button (bottom left or right of page) +
- Which direction should the button open (◯ only)? + Which direction should the master button open (◯ only)?
- +
- How transparent should the button be when hidden? + How transparent should the master button be when hidden? +
+ + +
+
+

The Minion Button(s)

+
+ Allow the use of custom (minion) buttons?
- - + + + + +
@@ -607,14 +649,14 @@

The Button

Experimental Sites -
+


Default Sites -
+

@@ -633,18 +675,18 @@

Proxy Settings

Force Secure Connections - +

- If enabled, all insecure (HTTP) requests will be through an HTTPS proxy. + If enabled, all requests sent by Web to Plex will be proxied.

Proxy URL & Syntax

- +
Please provide the URL of your proxy.
@@ -660,9 +702,9 @@

Proxy URL & Syntax

Proxy Headers

-
+
- If your proxy requires special headers, enter that information in here. + If your proxy requires special headers, enter that information in here.
  • @{raw-url} OR @{url} — the raw, uneditied URL
  • @@ -673,11 +715,23 @@

    Proxy Headers

+
+ +
    +
  • + IP Address + + + +
  • +
+
+

Native Proxy Settings

@@ -691,20 +745,20 @@

Native Proxy Settings

Auto Grab - +

- When the user presses the Grab button, the extension should: + When you press the Grab button, the extension should:
  • Grab ALL — Find items not on Plex, and grab them
  • -
  • ASK user — Find items not on Plex, and grab what the user approves
  • +
  • ASK you — Find items not on Plex, and grab what you approve

Maximum Auto Grabs

- - + +
How many items can be automatically handled before requiring permission to continue?
@@ -714,12 +768,12 @@

Maximum Auto Grabs

Prompt for Save Location - +

- When the user presses the Grab button should the save location be asked for? + When you press the Grab button, should the save location be asked for?
Only supports Medusa, Radarr, and Sonarr.
@@ -729,12 +783,12 @@

Prompt for Quality - +

- When the user presses the Grab button should the quality be asked for? + When you press the Grab button, should the quality be asked for?
Only supports Medusa, Radarr, and Sonarr.
@@ -748,23 +802,23 @@

Ignore Found Items - +

- When the user presses the Grab button and an item already exists, should the notification be ignored or not? + When you press the Grab button and the item already exists, should the notification be ignored or not?

Ignore Repetitive Notifications - +

- When the user presses the Grab button and there are several incoming notifications of similar information, should the notifications be ignored (after the first one) or not? + When you press the Grab button and there are several incoming notifications of similar information, should the notifications be ignored (after the first one) or not?
@@ -776,14 +830,14 @@

Loose Searching - +


- - + +
Allows the extension to search for non-English titles using pattern matching (as a last resort).
@@ -795,13 +849,13 @@

Manager Searching - +

Allows the extension to use your manager(s) to find media.
- Currently supports: Medusa, Ombi, Radarr, and Sonarr. + Currently supports Medusa, Ombi, Radarr, and Sonarr.

@@ -809,7 +863,7 @@

ID Fetching Mode - +

@@ -823,13 +877,13 @@

- Advance Settings + Advanced Settings BETA

API Keys

- +
You can sign up for an API key, or have OMDb used as a last resort.
@@ -837,17 +891,29 @@

API Keys

- +
You can learn more on how to obtain an API key.

Data Handling

+
+

Settings' Data

+ +
+ +
+ WARNING — This will remove all of your data. +
+ If you don't press Save, this action will be ignored. +
+
+

Data Compression

- +
@@ -886,13 +952,13 @@

Developer Options

Developer Mode - +

WARNING — may cause data loss.
- Enables developer (debugging) mode, showing advance errors and logging to the console when possible. + Enables developer (debugging) mode, showing Advanced errors and logging to the console when possible.
@@ -905,12 +971,17 @@

External Links

- +
Browse the Web to Plex Wiki for help setting up
+
Browse issues if you're having trouble
+
+ +
+ Download Plex Media Server. +
+
+
@@ -994,9 +1065,10 @@

External Links

+
- ... + ... diff --git a/win/options/index.js b/win/options/index.js index bf9aa51..2fa2e81 100644 --- a/win/options/index.js +++ b/win/options/index.js @@ -14,7 +14,7 @@ if(chrome.runtime.lastError) // FireFox doesn't support sync storage. const storage = (chrome.storage.sync || chrome.storage.local), - $ = top.$ = (selector, all) => (all? [...document.querySelectorAll(selector)]: document.querySelector(selector)), + $ = top.$ = (selector, all = false, container = document) => (all? [...container.querySelectorAll(selector)]: container.querySelector(selector)), $$ = top.$$ = (selector, all) => (all? [...$('display').querySelectorAll(selector)]: $('display').querySelector(selector)), __servers__ = $('[data-option="preferredServer"]'), __sickBeard_qualityProfile__ = $(`[data-option="sickBeardQualityProfileId"]`), @@ -117,7 +117,7 @@ const storage = (chrome.storage.sync || chrome.storage.local), 'ManagerSearch', 'UseLowCache', - // Advance Settings + // Advanced Settings 'OMDbAPI', 'TMDbAPI', 'UseLZW', @@ -179,7 +179,6 @@ const storage = (chrome.storage.sync || chrome.storage.local), 'builtin_gostream', 'builtin_tubi', 'builtin_webtoplex', - 'builtin_shanaproject', // Plugins - End of file, "let plugins =" 'plugin_toloka', @@ -196,7 +195,11 @@ const storage = (chrome.storage.sync || chrome.storage.local), 'plugin_metacritic', // Theme Settings - ...(() => [...$('[data-option^="theme:"i]', true)].map(e => e.dataset.option))() + 'UseMinions', + ...(() => [...$('[data-option^="theme:"i]', true)].map(e => e.dataset.option))(), + + // Other Settings + '__defaults', ]; let PlexServers = [], @@ -218,7 +221,23 @@ let __caught = { tvdb: [], }, // The theme classes - __theme = []; + __theme = {}; + +// Icon Markers +let MARKERS = [ + // yes + ``, + // no + ``, + // maybe + `` +]; + +MARKERS.yes = MARKERS[0]; +MARKERS.no = MARKERS[1]; +MARKERS.maybe = MARKERS[2]; + +let RESETTING_SETTINGS = false; // create and/or queue a notification // state = "error" - red @@ -522,6 +541,7 @@ function performPlexLogin({ event }) { s.title = ''; __servers__.innerHTML = ''; __save__.disabled = true; + __save__.innerHTML = 'Save ' + MARKERS.maybe; LoadingAnimation(true); tryPlexLogin(u, p) @@ -538,7 +558,8 @@ function performPlexLogin({ event }) { return performPlexTest({}); } - }); + }) + .catch(error => { LoadingAnimation(); new Notification('error', error); __save__.innerHTML = 'Save ' + MARKERS.no; }); } @@ -548,8 +569,9 @@ function performPlexTest({ ServerID, event }) { inusestatus = [...$('[using="plex"]', true)]; __save__.disabled = true; + __save__.innerHTML = 'Save ' + MARKERS.maybe; __servers__.innerHTML = ''; - teststatus.textContent = '?'; + teststatus.innerHTML = MARKERS.maybe; inusestatus.map(e => e.setAttribute('in-use', false)); LoadingAnimation(true); @@ -557,7 +579,7 @@ function performPlexTest({ ServerID, event }) { LoadingAnimation(); PlexServers = servers; - teststatus.textContent = '!'; + teststatus.innerHTML = MARKERS[+!servers]; inusestatus.map(e => e.setAttribute('in-use', false)); if(!servers) @@ -565,6 +587,7 @@ function performPlexTest({ ServerID, event }) { inusestatus.map(e => e.setAttribute('in-use', true)); __save__.disabled = false; + __save__.innerHTML = 'Save ' + MARKERS.yes; teststatus.classList = true; (servers = [{ sourceTitle: 'GitHub', clientIdentifier: '', name: 'No Server', notice: 'This will not connect to any Plex servers' }, ...servers]).forEach(server => { @@ -580,7 +603,8 @@ function performPlexTest({ ServerID, event }) { if(ServerID) { __servers__.value = ServerID; } - }); + }) + .catch(error => { LoadingAnimation(); new Notification('error', error); teststatus.innerHTML = MARKERS.no; __save__.innerHTML = 'Save ' + MARKERS.no; }); } function getPlexConnections(server) { @@ -612,12 +636,16 @@ function getOptionValues() { } }); - let COM = options.UseLZW; + let COM = options.UseLZW, + DEF = options.__defaults == 'true'; for(let key in __caught) __caught[key] = __caught[key].filter(id => id).slice(0, (COM? 200: 100)).sort(); - __theme = __theme.filter(v => v); + // if(options.__theme) + // __theme = JSON.parse(options.__theme); + // + // __theme = __theme.filter(v => v); let _c = JSON.stringify(__caught), _t = JSON.stringify(__theme); @@ -640,6 +668,7 @@ function performOmbiLogin({ event }) { s.title = ''; __servers__.innerHTML = ''; __save__.disabled = true; + __save__.innerHTML = 'Save ' + MARKERS.maybe; LoadingAnimation(true); let APIURL = `${ l }api/v1/`, @@ -748,12 +777,14 @@ function performOmbiLogin({ event }) { } __save__.disabled = false; + __save__.innerHTML = 'Save ' + MARKERS.yes; } else { /* Plex either doesn't exist, or is disabled */ new Notification('error', 'Error getting Plex details from Ombi'); + __save__.innerHTML = 'Save ' + MARKERS.no; } } ) - .catch( error => { new Notification('error', error); throw error } ); + .catch(error => { LoadingAnimation(); new Notification('error', error); __save__.innerHTML = 'Save ' + MARKERS.no; }); } function performOmbiTest({ refreshing = false, event }) { @@ -765,7 +796,7 @@ function performOmbiTest({ refreshing = false, event }) { enabled = refreshing? $('#using-ombi'): $$('#using-ombi'), inusestatus = [...$('[using="ombi"]', true)]; - teststatus.textContent = '?'; + teststatus.innerHTML = MARKERS.maybe; options.ombiURLRoot = url = path.value = options.ombiURLRoot.replace(/^(\:\d+)/, 'localhost$1').replace(/^(?!^http(s)?:)/, 'http$1://').replace(/\/+$/, ''); inusestatus.map(e => e.setAttribute('in-use', false)); LoadingAnimation(true); @@ -779,38 +810,40 @@ function performOmbiTest({ refreshing = false, event }) { __caught.imdb.push(item.imdbId); __caught.tmdb.push(item.theMovieDbId); }); - }); - - fetch(`${ url }/api/v1/Request/tv`) - .then(r => r.json()) - .then(json => { - json.map(item => { - __caught.imdb.push(item.imdbId); - __caught.tvdb.push(item.tvDbId); - }); - }); - fetch(`${ url }/api/v1/Status`, headers) - .then( response => response.text() ) - .then( status => { - LoadingAnimation(); - if (!status || !status.length) throw new Error('Unable to communicate with Ombi'); - - if ((status = +status) >= 200 && status < 400) { - teststatus.textContent = '!'; - enabled.checked = teststatus.classList = true; - enabled.parentElement.removeAttribute('disabled'); - inusestatus.map(e => e.setAttribute('in-use', true)); - } else { - teststatus.textContent = '!'; - enabled.checked = teststatus.classList = false; - enabled.parentElement.setAttribute('disabled'); - inusestatus.map(e => e.setAttribute('in-use', false)); - - throw new Error(`Ombi error [${ status }]`); - } - } ) - .catch( error => { new Notification('error', error) } ); + fetch(`${ url }/api/v1/Request/tv`) + .then(r => r.json()) + .then(json => { + json.map(item => { + __caught.imdb.push(item.imdbId); + __caught.tvdb.push(item.tvDbId); + }); + }) + .catch(error => { LoadingAnimation(); new Notification('error', error); teststatus.innerHTML = MARKERS.no }); + + fetch(`${ url }/api/v1/Status`, headers) + .then( response => response.text() ) + .then( status => { + LoadingAnimation(); + if (!status || !status.length) throw new Error('Unable to communicate with Ombi'); + + if ((status = +status) >= 200 && status < 400) { + teststatus.innerHTML = MARKERS.yes; + enabled.checked = teststatus.classList = true; + enabled.parentElement.removeAttribute('disabled'); + inusestatus.map(e => e.setAttribute('in-use', true)); + } else { + teststatus.innerHTML = MARKERS.no; + enabled.checked = teststatus.classList = false; + enabled.parentElement.setAttribute('disabled'); + inusestatus.map(e => e.setAttribute('in-use', false)); + + throw new Error(`Ombi error [${ status }]`); + } + }) + .catch(error => { LoadingAnimation(); new Notification('error', error); teststatus.innerHTML = MARKERS.no }); + }) + .catch(error => { LoadingAnimation(); new Notification('error', error); teststatus.innerHTML = MARKERS.no }); } catch(error) { LoadingAnimation(); @@ -860,7 +893,7 @@ function performWatcherTest({ QualityProfileID = 'Default', refreshing = false, inusestatus = [...$('[using="watcher"]', true)]; quality.innerHTML = ''; - teststatus.textContent = '?'; + teststatus.innerHTML = MARKERS.maybe; storagepath.value = '[Empty]'; options.watcherURLRoot = url = path.value = options.watcherURLRoot.replace(/^(\:\d+)/, 'localhost$1').replace(/^(?!^http(s)?:)/, 'http$1://').replace(/\/+$/, ''); inusestatus.map(e => e.setAttribute('in-use', false)); @@ -873,7 +906,8 @@ function performWatcherTest({ QualityProfileID = 'Default', refreshing = false, __caught.imdb.push(item.movies.imdbid); __caught.tmdb.push(item.movies.tmdbid); }); - }); + }) + .catch(error => { LoadingAnimation(); new Notification('error', error); teststatus.innerHTML = MARKERS.no }); getWatcher(options, 'getconfig').then(configuration => { LoadingAnimation(); @@ -892,8 +926,7 @@ function performWatcherTest({ QualityProfileID = 'Default', refreshing = false, name }); - teststatus.textContent = '!'; - teststatus.classList = enabled.checked = !!profiles.length; + teststatus.innerHTML = MARKERS[+!(teststatus.classList = enabled.checked = !!profiles.length)]; inusestatus.map(e => e.setAttribute('in-use', enabled.checked)); if(!profiles.length) @@ -920,11 +953,11 @@ function performWatcherTest({ QualityProfileID = 'Default', refreshing = false, storagepath.value = path || '[Default Location]'; $('[data-option="watcherStoragePaths"i]').value = JSON.stringify(path || { path: '[Default Location]', id: 0 }); - }); + }) + .catch(error => { LoadingAnimation(); new Notification('error', error); teststatus.innerHTML = MARKERS.no }); } catch(error) { LoadingAnimation(); - - throw error; + new Notification('error', error); } } @@ -970,7 +1003,7 @@ function performRadarrTest({ QualityProfileID, StoragePath, refreshing = false, inusestatus = [...$('[using="radarr"]', true)]; quality.innerHTML = ''; - teststatus.textContent = '?'; + teststatus.innerHTML = MARKERS.maybe; storagepath.textContent = ''; options.radarrURLRoot = url = path.value = options.radarrURLRoot.replace(/^(\:\d+)/, 'localhost$1').replace(/^(?!^http(s)?:)/, 'http$1://').replace(/\/+$/, ''); inusestatus.map(e => e.setAttribute('in-use', false)); @@ -983,14 +1016,14 @@ function performRadarrTest({ QualityProfileID, StoragePath, refreshing = false, __caught.imdb.push(movie.imdbId); __caught.tmdb.push(movie.tmdbId); }); - }); + }) + .catch(error => { LoadingAnimation(); new Notification('error', error); teststatus.innerHTML = MARKERS.no }); getRadarr(options, 'profile').then(profiles => { LoadingAnimation(); if(!profiles) return new Notification('error', 'Failed to get Radarr configuration'); - teststatus.textContent = '!'; - teststatus.classList = enabled.checked = !!profiles.length; + teststatus.innerHTML = MARKERS[+!(teststatus.classList = enabled.checked = !!profiles.length)]; inusestatus.map(e => e.setAttribute('in-use', enabled.checked)); if(!profiles.length) @@ -1013,29 +1046,42 @@ function performRadarrTest({ QualityProfileID, StoragePath, refreshing = false, // Because the was reset, the original value is lost. - if(StoragePath) { - storagepath.value = StoragePath; - $('[data-option="__radarrStoragePath"i]').value = StoragePaths.indexOf(StoragePath.replace(/\\/g, '/')) + 1; - } - }); + let paths = []; + storagepaths.forEach(folder => { + let option = document.createElement('option'); + let { id, path } = folder; + + option.value = id; + option.textContent = path; + paths.push({ id, path }); + storagepath.appendChild(option); + }); + + $('[data-option="radarrStoragePaths"i]').value = JSON.stringify(paths); + + // Because the was reset, the original value is lost. if(QualityProfileID) $('[data-option="__sonarrQuality"i]').value = quality.value = QualityProfileID; - }); + }) + .catch(error => { LoadingAnimation(); new Notification('error', error); teststatus.innerHTML = MARKERS.no }); let StoragePaths = []; getSonarr(options, 'rootfolder').then(storagepaths => { - storagepaths.forEach(path => { + LoadingAnimation(); + if(!storagepaths) return new Notification('error', 'Failed to get Sonarr configuration'); + + teststatus.innerHTML = MARKERS[+!(teststatus.classList = enabled.checked = !!storagepaths.length)]; + inusestatus.map(e => e.setAttribute('in-use', enabled.checked)); + + if(!storagepaths.length) + return teststatus.title = 'Failed to communicate with Sonarr'; + enabled.parentElement.removeAttribute('disabled'); + + let paths = []; + storagepaths.forEach(folder => { let option = document.createElement('option'); + let { id, path } = folder; - StoragePaths.push((option.value = option.textContent = path.path).replace(/\\/g, '/')); + option.value = id; + option.textContent = path; + paths.push({ id, path }); storagepath.appendChild(option); }); - $('[data-option="sonarrStoragePaths"i]').value = JSON.stringify(storagepaths); + $('[data-option="sonarrStoragePaths"i]').value = JSON.stringify(paths); // Because the was reset, the original value is lost. if(QualityProfileID) $('[data-option="__medusaQuality"i]').value = quality.value = QualityProfileID; - }); + }) + .catch(error => { LoadingAnimation(); new Notification('error', error); teststatus.innerHTML = MARKERS.no }); let StoragePaths = []; getMedusa(options, 'config').then(configuration => { @@ -1260,11 +1320,11 @@ function performMedusaTest({ QualityProfileID, StoragePath, refreshing = false, $('[data-option="__medusaStoragePath"i]').value = StoragePath; storagepath.selectedIndex = StoragePaths.indexOf(StoragePath.replace(/\\/g, '/').replace(/\/+$/, '')); } - }); + }) + .catch(error => { LoadingAnimation(); new Notification('error', error); teststatus.innerHTML = MARKERS.no }); } catch(error) { LoadingAnimation(); - - throw error; + new Notification('error', error); } }; @@ -1310,7 +1370,7 @@ function performSickBeardTest({ QualityProfileID, StoragePath, refreshing = fals inusestatus = [...$('[using="sickbeard"]', true)]; quality.innerHTML = ''; - teststatus.textContent = '?'; + teststatus.innerHTML = MARKERS.maybe; storagepath.textContent = ''; options.sickBeardURLRoot = url = path.value = options.sickBeardURLRoot.replace(/^(\:\d+)/, 'localhost$1').replace(/^(?!^http(s)?:)/, 'http$1://').replace(/\/+$/, ''); inusestatus.map(e => e.setAttribute('in-use', false)); @@ -1329,7 +1389,8 @@ function performSickBeardTest({ QualityProfileID, StoragePath, refreshing = fals shows.map(show => { __caught.tvdb.push(show.tvdbid); }); - }); + }) + .catch(error => { LoadingAnimation(); new Notification('error', error); teststatus.innerHTML = MARKERS.no }); getSickBeard(options, 'sb.getdefaults').then(configuration => { LoadingAnimation(); @@ -1341,8 +1402,7 @@ function performSickBeardTest({ QualityProfileID, StoragePath, refreshing = fals profiles = qualities[profiles]; - teststatus.textContent = '!'; - teststatus.classList = enabled.checked = !!profiles.length; + teststatus.innerHTML = MARKERS[+!(teststatus.classList = enabled.checked = !!profiles.length)]; inusestatus = [...$('[using="sickbeard"]', true)]; if(!profiles.length) @@ -1364,7 +1424,8 @@ function performSickBeardTest({ QualityProfileID, StoragePath, refreshing = fals // Because the +

@@ -2009,7 +2135,7 @@ for(let index = 0, length = builtin_array.length; builtinElement && index < leng `

${ title }

- +
@@ -2100,7 +2226,7 @@ for(let index = 0, length = plugin_array.length; pluginElement && index < length `

${ title }

- +
@@ -2124,7 +2250,7 @@ for(let index = 0, length = plugin_array.length; pluginElement && index < length `

${ title }

- +
@@ -2238,6 +2364,29 @@ addListener($('#erase_cache'), 'mouseup', event => { saveOptions(); }); +/* Erase ALL Settings */ +addListener($('#reset_settings'), 'mouseup', event => { + let options = JSON.stringify(getOptionValues()); + + $('[default]', true) + .forEach(el => { + let de = el.getAttribute('default'); + + if(el.type == 'checkbox') + el.checked = de === 'true'; + else if('contenteditable' in el.attributes) + el.innerHTML = de || ''; + else + el.value = de || ''; + }); + + Recall.CountEnabledSites(); + + new Notification('update', 'All settings have been reset'); + + RESETTING_SETTINGS = true; +}); + $('[type="range"]', true) .forEach((element, index, array) => { let sibling = element.nextElementSibling, @@ -2262,13 +2411,17 @@ $('.checkbox', true) switch(self.id.toLowerCase()) { /* Update the database when the option is toggled */ case 'use-lzw': - if(!self.checked) + let enabled; + + if(enabled = !self.checked) new Notification('update', 'Compressing data...', 3000, () => new Notification('update', 'Compressed', 3000), false); else new Notification('update', 'Decompressing data...', 3000, () => new Notification('update', 'Decompressed', 3000), false); let options = getOptionValues(); + Recall.ToggleConfigurationAvailability(enabled); + for(let name in options) if(/^__/.test(name)) { if(!self.checked) @@ -2311,26 +2464,27 @@ $('.test', true) $('[data-option^="theme:"i], [data-option^="theme:"i] + label', true) .forEach((element, index, array) => { - addListener(element, 'mouseup', async event => { + let UpdateTheme; + + addListener(element, 'mouseup', UpdateTheme = async event => { let self = traverse(event.target, element => /^theme:/i.test(element.dataset.option), true), R = RegExp; - let [a, b] = self.getAttribute('theme').split(/\s*:\s*/).filter(v => v), + let [a, b] = self.getAttribute('theme').split(/^([^]+):([^]+?)$/).filter(v => v), value = `${self.dataset.option.replace(/^theme:/i, '')}-${b}`; if(/^(get|read|for)$/i.test(a)) - __theme.push(`${ value }=${ self.value }`) + __theme[value] = (self.value == 'true'? true: self.value == 'false'? false: self.value); else if(/^(checkbox)$/i.test(self.type) && (self.checked + '') != a) // backwards; fires late - __theme.push(value); - else if(/^(text|input|button|\B)$/i.test(self.type) && R(self.value + '', 'i').test(a)) - __theme.push(value); + __theme[value] = JSON.parse(a); + else if(/^(text|input|button|\B)$/i.test(self.type) && R(a, 'i').test(self.value)) + __theme[value] = self.value; else - __theme = __theme.filter(v => v != value); - - /* Get rid of repeats */ - // __theme = __theme.join('\u0000').replace(/([\w\-]+\=)([^\u0000]+?)\u0000\1[^\u0000]+?/g, ($0, $1, $2, $$, $_) => $1 + $2); + delete __theme[value]; }); + + setTimeout(() => UpdateTheme({ target: element }), 1000); }); let hold = document.createElement('summary'), @@ -2473,6 +2627,11 @@ $('[href^="#!/"]', true) }; }); +$('[id$="_test_status"]', true) + .forEach(element => { + element.innerHTML = MARKERS.maybe; + }); + // CORS exception: SecurityError // MUST be { window }, never { top } let { hash } = window.location; @@ -2493,7 +2652,7 @@ if(hash.length > 1) /* #!/SETTING[/SUB-SETTING] * #!/radarr - * #!/advance-settings/api-keys + * #!/advanced-settings/api-keys */ case '': break; @@ -2505,7 +2664,7 @@ if(hash.length > 1) /* Functions that require some time */ let Recall = { - '@auto': {}, // run at 100ms, and be recallable + '@auto': {}, // run at 100ms '@0sec': {}, // run at 1ms '@1sec': {}, // run at 1000ms }; @@ -2574,15 +2733,125 @@ Recall['@0sec'].SetVersionInfo = async() => { } if(DM) - await fetch('https://api.github.com/repos/SpaceK33z/web-to-plex/releases') + await fetch('https://api.github.com/repos/webtoplex/browser-extension/releases') .then(response => response.json()) .then(versions => useVer(versions[0])); else - await fetch('https://api.github.com/repos/SpaceK33z/web-to-plex/releases/latest') + await fetch('https://api.github.com/repos/webtoplex/browser-extension/releases/latest') .then(response => response.json()) .then(version => useVer(version)); }; +/* Get the user's IP address */ +Recall['@auto'].GetIPAddress = async() => { + let self = $('#ip-address'), + teststatus = $('#proxy_test_status'), + options = getOptionValues(); + + + self.innerHTML = 'Loading...'; + teststatus.innerHTML = MARKERS.maybe; + + let proxy = HandleProxySettings(options); + + if(proxy.enabled) { + let { url, headers } = proxy, + tor = 'https://check.torproject.org'; + + headers = HandleProxyHeaders(headers, tor); + + if(/(^https?:\/\/)(?!localhost|127\.0\.0\.1(?:\/8)?|::1(?:\/128)?|:\d+)\b/i.test(url)) { + url = url + .replace(/\{b(ase-?)?64-url\}/gi, btoa(tor)) + .replace(/\{enc(ode)?-url\}/gi, encodeURIComponent(tor)) + .replace(/\{(raw-)?url\}/gi, tor); + } else { + self.innerHTML = ''; + teststatus.innerHTML = MARKERS.no; + + throw new SyntaxError('Unable to proxy: The URL must be a public (HTTPS/HTTP) address'); + } + + await fetch(url, { mode: 'cors', headers }) + .then(results => results.text()) + .then(text => { + let DOM = new DOMParser, + html = DOM.parseFromString(text, 'text/html'), + strong = $('.content strong', false, html), + IPAddress; + + if(strong) + IPAddress = strong.textContent; + else if(/([\d\.]{7,15})/.test(html.body.textContent)) + IPAddress = RegExp.$1; + else + IPAddress = ''; + + self.innerHTML = IPAddress; + teststatus.innerHTML = MARKERS[+!IPAddress]; + }) + .catch(error => { + self.innerHTML = ''; + teststatus.innerHTML = MARKERS.no; + + new Notification('error', error); + }); + } else { + await fetch('https://check.torproject.org', { mode: 'cors' }) + .then(results => results.text()) + .then(text => { + let DOM = new DOMParser, + html = DOM.parseFromString(text, 'text/html'), + strong = $('.content strong', false, html), + IPAddress; + + if(strong) + IPAddress = strong.textContent; + else if(/([\d\.]{7,15})/.test(html.body.textContent)) + IPAddress = RegExp.$1; + else + IPAddress = ''; + + self.innerHTML = IPAddress; + teststatus.innerHTML = MARKERS[+!IPAddress]; + }) + .catch(error => { + self.innerHTML = ''; + teststatus.innerHTML = MARKERS.no; + + new Notification('error', error); + }); + } + + self.setAttribute('notice', `Public${proxy.enabled?' (Proxy)':''}`); +}; + +let ToggleConfigurationAvailabilityListener = false; +/* Setting the Configuration Data disabled state */ +Recall['@1sec'].ToggleConfigurationAvailability = (enabled = null) => { + if(enabled === null) + enabled = $('#use-lzw').checked; + + if(enabled) { + let parent = $('#json_data').parentElement; + + parent.setAttribute('disabled', ''); + + if(!ToggleConfigurationAvailabilityListener) + $('*:not([data-option])', true, parent).forEach(element => { + addListener(element, 'mousedown', event => { + let disabled = traverse(element, () => 'disabled' in element.attributes, false); + + if(disabled) + return event.preventDefault(); + }); + }); + ToggleConfigurationAvailabilityListener = true; + } else { + $('#json_data').parentElement.removeAttribute('disabled'); + } +}; + for(let func in Recall) { if(/^@/.test(func)) { let f; @@ -2601,15 +2870,21 @@ for(let func in Recall) { for(let fn in Recall[func]) { f = Recall[func][fn]; + Recall[fn] = f; setTimeout(f, 1); } break; - case '@1sec': + default: + /^@(\d+)sec$/i.test(func); + + let time = +RegExp.$1; + for(let fn in Recall[func]) { f = Recall[func][fn]; - setTimeout(f, 1000); + Recall[fn] = f; + setTimeout(f, time * 1000); } break; } @@ -2949,3 +3224,5 @@ function restoreSelection({ anchor, focus }, editor, key) { selection.setBaseAndExtent(nodes.anchor, index.anchor, nodes.focus, index.focus); } + +addListener($('#test-proxy-settings'), 'click', Recall.GetIPAddress); diff --git a/win/plugn.js b/win/plugn.js index d3aea88..70062b9 100644 --- a/win/plugn.js +++ b/win/plugn.js @@ -257,7 +257,15 @@ async function prepare({ code, alias, type, allowed, url }) { Type = type.replace(/^\w/, ($0, $$, $_) => $0.toUpperCase()); let org = url.origin, - ali = TLDHost(url.host); + ali = TLDHost(url.host), + runOnInit = ['(null)']; + + let funcs = { + minions: PLUGN_CONFIGURATION.UseMinions, + }; + for(let func in funcs) + runOnInit.push(`(${ funcs[func] } && ${ type }.${ func } instanceof Function? ${ type }.${ func }(): null)`); + runOnInit = runOnInit.join(','); let { authorized, ...A } = await GetAuthorization(alias); @@ -294,7 +302,7 @@ ${ ${ code .replace(/\/\/+\s*"([^\"\n\f\r\v]+?)"\s*requires?\:?\s*(.+)/i, ($0, $1, $2, $$, $_) => - `;(async() => await Require("${ $2 }", "${ alias }", "${ $1 }", "${ instance }"))();` + `;(async() => await Require("cache,${ $2 }", "${ alias }", "${ $1 }", "${ instance }"))();` ) .replace(/\b(chrome|browser)\.storage\.(sync|local|managed)\.?/g, ($0, $1, $2, $$, $_) => `;console.warn("This ${ type } attempted to access <${ $1 }.storage.${ $2 }>; use , , and instead.");` @@ -334,11 +342,17 @@ return ( /* "ready" is a sync (normal) function */ ${ type }.ready() )? - ${ type }.init( ${ Type }ReadyState ): + ( + ${ runOnInit }, + ${ type }.init( ${ Type }ReadyState ) + ): /* Injected ${ type } isn't ready */ (${ type }.timeout || 1000): /* Injected ${ type } doesn't have the "ready" property */ - ${ type }.init(): + ( + ${ runOnInit }, + ${ type }.init() + ): /* Injected ${ type } isn't properly structured */ (console.warn("The ${ type } (${ alias }) is incorrectly structured. Could not find required function ${ type }.init"), -1): /* URL doesn't match pattern */ @@ -358,8 +372,13 @@ let handle = async(results, tabID, instance, script, type) => { results = await results; + let extURL = url => chrome.extension.getURL(url); + /* Always display a pretty button */ - chrome.tabs.insertCSS(tabID, { file: 'sites/common.css' }); + chrome.tabs.insertCSS(tabID, { cssOrigin: 'user', file: 'sites/common.css' }); + chrome.tabs.insertCSS(tabID, { cssOrigin: 'user', file: 'sites/theme.css' }); + chrome.tabs.insertCSS(tabID, { cssOrigin: 'user', file: 'sites/glyphs.css' }); + chrome.tabs.insertCSS(tabID, { cssOrigin: 'user', file: 'sites/colors.css' }); if((!results || !results[0] || !instance) && !FOUND[instance]) try { @@ -483,6 +502,10 @@ let tabchange = async tabs => { chrome.runtime.getURL(`cloud/plugin/${ js }.js`): `https://webtoplex.github.io/web/${ type }s/${ js }.js`; + let style = (PLUGN_DEVELOPER)? + chrome.runtime.getURL(`sites/${ js }/index.css`): + `https://webtoplex.github.io/web/styles/${ js }.css`; + await fetch(file, { mode: 'cors' }) .then(response => response.text()) .then(async code => { @@ -495,6 +518,10 @@ let tabchange = async tabs => { }) .then(() => running.push(id, instance)) .catch(error => { throw error }); + + await fetch(style, { mode: 'cors' }) + .then(response => response.text()) + .then(async code => chrome.tabs.insertCSS({ code })); }; // listen for message event @@ -540,6 +567,10 @@ chrome.runtime.onMessage.addListener(processMessage = async(request = {}, sender chrome.runtime.getURL(`cloud/plugin/${ plugin }.js`): `https://webtoplex.github.io/web/${ _type }s/${ options[_type] }.js`; + let style = (PLUGN_DEVELOPER)? + chrome.runtime.getURL(`sites/${ options[_type] }/index.css`): + `https://webtoplex.github.io/web/styles/${ options[_type] }.css`; + let { authorized, ...A } = await GetAuthorization(options[_type]); try { @@ -602,6 +633,9 @@ chrome.runtime.onMessage.addListener(processMessage = async(request = {}, sender break; case 'GRANT_PERMISSION': + if(!options[_type]) + return false; + await Save(`has/${ options[_type] }`, options.allowed); await Save(`get/${ options[_type] }`, options.permissions); break; @@ -629,6 +663,10 @@ chrome.runtime.onMessage.addListener(processMessage = async(request = {}, sender return false; } + await fetch(style, { mode: 'cors' }) + .then(response => response.text()) + .then(async code => browser.tabs.insertCSS({ code })); + return true; } catch(error) { PLUGN_TERMINAL.error(error); diff --git a/win/sites/amazon/index.css b/win/sites/amazon/index.css index e69de29..dac83ab 100644 --- a/win/sites/amazon/index.css +++ b/win/sites/amazon/index.css @@ -0,0 +1,32 @@ +.web-to-plex-minion { + margin-right: 8px; + color: #fff!important; + margin-bottom: 8px!important; + line-height: 2!important; + text-decoration: none!important; + width: auto!important; +} + +.web-to-plex-minion.wtp--download { + background: linear-gradient(180deg, #f67e56, #f45a26)!important; +} + +.web-to-plex-minion.wtp--download:hover { + background: linear-gradient(180deg, #ff8960, #fd6430)!important; +} + +.web-to-plex-minion.wtp--found { + background: linear-gradient(180deg, #f7dfa5, #ca7c1f)!important; +} + +.web-to-plex-minion.wtp--found:hover { + background: linear-gradient(180deg, #f7dfa5, #f8c022)!important; +} + +#tt--0-0 { + background: linear-gradient(180deg, #70767d 0, #696f78 0, #3d444e 100%)!important; +} + +#tt--0-0:hover { + background: linear-gradient(180deg, #70767d 0, #696f78 0, #3d444e 100%)!important; +} diff --git a/win/sites/colors.css b/win/sites/colors.css new file mode 100644 index 0000000..5c66061 --- /dev/null +++ b/win/sites/colors.css @@ -0,0 +1,572 @@ +:root { + --light-black: #222; + --black: #282828; + --dark-black: #000; + + --light-blue: #197bcc; + --blue: #265af4; + --dark-blue: #2a2aff; + + --light-grey: #999; + --grey: #666; + --dark-grey: #3f4245; + + --green: #80f080; + + --light-orange: #e59029; + --orange: #cc7b19; + --dark-orange: #f45a26; + + --light-red: #f44; + --red: #ff2a2a; + --dark-red: #d11; + + --light-white: #fff; + --white: #eee; + + --disabled: #909090ee; + --half-black: #0008; + --half-green: #80f08088; + --half-grey: #8888; + --half-white: #fff8; + --quarter-black: #0004; + --quarter-grey: #8884; + --quarter-white: #fff4; + --transparent: #0000; +} + +/* Text Color */ +[light-black]:not([gradient]) { + color: var(--light-black) +} + +[black]:not([gradient]) { + color: var(--black) +} + +[dark-black]:not([gradient]) { + color: var(--dark-black) +} + +[light-blue]:not([gradient]) { + color: var(--light-blue) +} + +[blue]:not([gradient]) { + color: var(--blue) +} + +[dark-blue]:not([gradient]) { + color: var(--dark-blue) +} + +[light-grey]:not([gradient]) { + color: var(--light-grey) +} + +[grey]:not([gradient]) { + color: var(--grey) +} + +[dark-grey]:not([gradient]) { + color: var(--dark-grey) +} + +[green]:not([gradient]) { + color: var(--green) +} + +[light-orange]:not([gradient]) { + color: var(--light-orange) +} + +[orange]:not([gradient]) { + color: var(--orange) +} + +[dark-orange]:not([gradient]) { + color: var(--dark-orange) +} + +[light-red]:not([gradient]) { + color: var(--light-red) +} + +[red]:not([gradient]) { + color: var(--red) +} + +[dark-red]:not([gradient]) { + color: var(--dark-red) +} + +[light-white]:not([gradient]) { + color: var(--light-white) +} + +[white]:not([gradient]) { + color: var(--white) +} + +[disabled]:not([gradient]) { + color: var(--disabled) +} + +[half-black]:not([gradient]) { + color: var(--half-black) +} + +[half-green]:not([gradient]) { + color: var(--half-green) +} + +[half-grey]:not([gradient]) { + color: var(--half-grey) +} + +[half-white]:not([gradient]) { + color: var(--half-white) +} + +[quarter-black]:not([gradient]) { + color: var(--quarter-black) +} + +[quarter-grey]:not([gradient]) { + color: var(--quarter-grey) +} + +[quarter-white]:not([gradient]) { + color: var(--quarter-white) +} + +[transparent]:not([gradient]) { + color: var(--transparent) +} + +/* Text Color (Hover) */ +[light-black\$]:hover { + color: var(--light-black) +} + +[black\$]:hover { + color: var(--black) +} + +[dark-black\$]:hover { + color: var(--dark-black) +} + +[light-blue\$]:hover { + color: var(--light-blue) +} + +[blue\$]:hover { + color: var(--blue) +} + +[dark-blue\$]:hover { + color: var(--dark-blue) +} + +[light-grey\$]:hover { + color: var(--light-grey) +} + +[grey\$]:hover { + color: var(--grey) +} + +[dark-grey\$]:hover { + color: var(--dark-grey) +} + +[green\$]:hover { + color: var(--green) +} + +[light-orange\$]:hover { + color: var(--light-orange) +} + +[orange\$]:hover { + color: var(--orange) +} + +[dark-orange\$]:hover { + color: var(--dark-orange) +} + +[light-red\$]:hover { + color: var(--light-red) +} + +[red\$]:hover { + color: var(--red) +} + +[dark-red\$]:hover { + color: var(--dark-red) +} + +[light-white\$]:hover { + color: var(--light-white) +} + +[white\$]:hover { + color: var(--white) +} + +[disabled\$]:hover { + color: var(--disabled) +} + +[half-black\$]:hover { + color: var(--half-black) +} + +[half-green\$]:hover { + color: var(--half-green) +} + +[half-grey\$]:hover { + color: var(--half-grey) +} + +[half-white\$]:hover { + color: var(--half-white) +} + +[quarter-black\$]:hover { + color: var(--quarter-black) +} + +[quarter-grey\$]:hover { + color: var(--quarter-grey) +} + +[quarter-white\$]:hover { + color: var(--quarter-white) +} + +[transparent\$]:hover { + color: var(--transparent) +} + +/* Background Color */ +[light-black-bg], [light-black][gradient] { + background: var(--light-black) +} + +[black-bg], [black][gradient] { + background: var(--black) +} + +[dark-black-bg], [dark-black][gradient] { + background: var(--dark-black) +} + +[light-blue-bg], [light-blue][gradient] { + background: var(--light-blue) +} + +[blue-bg], [blue][gradient] { + background: var(--blue) +} + +[dark-blue-bg], [dark-blue][gradient] { + background: var(--dark-blue) +} + +[light-grey-bg], [light-grey][gradient] { + background: var(--light-grey) +} + +[grey-bg], [grey][gradient] { + background: var(--grey) +} + +[dark-grey-bg], [dark-grey][gradient] { + background: var(--dark-grey) +} + +[green], [green][gradient] { + background: var(--green) +} + +[light-orange-bg], [light-orange][gradient] { + background: var(--light-orange) +} + +[orange-bg], [orange][gradient] { + background: var(--orange) +} + +[dark-orange-bg], [dark-orange][gradient] { + background: var(--dark-orange) +} + +[light-red-bg], [light-red][gradient] { + background: var(--light-red) +} + +[red-bg], [red][gradient] { + background: var(--red) +} + +[dark-red-bg], [dark-red][gradient] { + background: var(--dark-red) +} + +[light-white-bg], [light-white][gradient] { + background: var(--light-white) +} + +[white-bg], [white][gradient] { + background: var(--white) +} + +[disabled-bg] { + background: var(--disabled) +} + +[half-black-bg] { + background: var(--half-black) +} + +[half-green-bg] { + background: var(--half-green) +} + +[half-grey-bg] { + background: var(--half-grey) +} + +[half-white-bg] { + background: var(--half-white) +} + +[quarter-black-bg] { + background: var(--quarter-black) +} + +[quarter-grey-bg] { + background: var(--quarter-grey) +} + +[quarter-white-bg] { + background: var(--quarter-white) +} + +[transparent-bg] { + background: var(--transparent) +} + +/* Background Color (Hover) */ +[light-black-bg\$]:hover { + background: var(--light-black) +} + +[black-bg\$]:hover { + background: var(--black) +} + +[dark-black-bg\$]:hover { + background: var(--dark-black) +} + +[light-blue-bg\$]:hover { + background: var(--light-blue) +} + +[blue-bg\$]:hover { + background: var(--blue) +} + +[dark-blue-bg\$]:hover { + background: var(--dark-blue) +} + +[light-grey-bg\$]:hover { + background: var(--light-grey) +} + +[grey-bg\$]:hover { + background: var(--grey) +} + +[dark-grey-bg\$]:hover { + background: var(--dark-grey) +} + +[green-bg\$]:hover { + background: var(--green) +} + +[light-orange-bg\$]:hover { + background: var(--light-orange) +} + +[orange-bg\$]:hover { + background: var(--orange) +} + +[dark-orange-bg\$]:hover { + background: var(--dark-orange) +} + +[light-red-bg\$]:hover { + background: var(--light-red) +} + +[red-bg\$]:hover { + background: var(--red) +} + +[dark-red-bg\$]:hover { + background: var(--dark-red) +} + +[light-white-bg\$]:hover { + background: var(--light-white) +} + +[white-bg\$]:hover { + background: var(--white) +} + +[disabled-bg\$]:hover { + background: var(--disabled) +} + +[half-black-bg\$]:hover { + background: var(--half-black) +} + +[half-green-bg\$]:hover { + background: var(--half-green) +} + +[half-grey-bg\$]:hover { + background: var(--half-grey) +} + +[half-white-bg\$]:hover { + background: var(--half-white) +} + +[quarter-black-bg\$]:hover { + background: var(--quarter-black) +} + +[quarter-grey-bg\$]:hover { + background: var(--quarter-grey) +} + +[quarter-white-bg\$]:hover { + background: var(--quarter-white) +} + +[transparent-bg\$]:hover { + background: var(--transparent) +} + +/* Gradient Text (Darken) */ +[light-black][gradient="darken"i] { + background: linear-gradient(var(--light-black), var(--black)) +} + +[black][gradient="darken"i] { + background: linear-gradient(var(--black), var(--dark-black)) +} + +[light-blue][gradient="darken"i] { + background: linear-gradient(var(--light-blue), var(--blue)) +} + +[blue][gradient="darken"i] { + background: linear-gradient(var(--blue), var(--dark-blue)) +} + +[light-grey][gradient="darken"i] { + background: linear-gradient(var(--light-grey), var(--grey)) +} + +[grey][gradient="darken"i] { + background: linear-gradient(var(--grey), var(--dark-grey)) +} + +[light-orange][gradient="darken"i] { + background: linear-gradient(var(--light-orange), var(--orange)) +} + +[orange][gradient="darken"i] { + background: linear-gradient(var(--orange), var(--dark-orange)) +} + +[light-red][gradient="darken"i] { + background: linear-gradient(var(--light-red), var(--red)) +} + +[red][gradient="darken"i] { + background: linear-gradient(var(--red), var(--dark-red)) +} + +[light-white][gradient="darken"i] { + background: linear-gradient(var(--light-white), var(--white)) +} + +/* Gradient Text (Lighten) */ +[black][gradient="lighten"i] { + background: linear-gradient(var(--black), var(--light-black)) +} + +[dark-black][gradient="lighten"i] { + background: linear-gradient(var(--dark-black), var(--black)) +} + +[blue][gradient="lighten"i] { + background: linear-gradient(var(--blue), var(--light-blue)) +} + +[dark-blue][gradient="lighten"i] { + background: linear-gradient(var(--dark-blue), var(--blue)) +} + +[grey][gradient="lighten"i] { + background: linear-gradient(var(--grey), var(--light-grey)) +} + +[dark-grey][gradient="lighten"i] { + background: linear-gradient(var(--dark-grey), var(--grey)) +} + +[orange][gradient="lighten"i] { + background: linear-gradient(var(--orange), var(--light-orange)) +} + +[dark-orange][gradient="lighten"i] { + background: linear-gradient(var(--dark-orange), var(--orange)) +} + +[red][gradient="lighten"i] { + background: linear-gradient(var(--red), var(--light-red)) +} + +[dark-red][gradient="lighten"i] { + background: linear-gradient(var(--dark-red), var(--red)) +} + +[white][gradient="lighten"i] { + background: linear-gradient(var(--white), var(--light-white)) +} + +/* All Gradient Properties */ +[gradient] { + background-clip: text; + -moz-background-clip: text; + -webkit-background-clip: text; + + color: #0000; + -webkit-text-fill-color: #0000; +} diff --git a/win/sites/common.css b/win/sites/common.css index 5657b4f..45e34a4 100644 --- a/win/sites/common.css +++ b/win/sites/common.css @@ -1,98 +1,123 @@ /** Common CSS - * Web to Plex - */ - /* Basic/Global Styling */ - [class*="web-to-plex"]::-webkit-scrollbar, [class*="web-to-plex"]::-moz-scrollbar { - width: 10px !important; - } - - [class*="web-to-plex"]::-webkit-scrollbar-thumb, [class*="web-to-plex"]::-moz-scrollbar-thumb { - min-height: 50px !important; - background: rgba(255, 255, 255, 0.15) !important; - border: 2px solid rgba(0, 0, 0, 0) !important; - border-radius: 8px !important; - background-clip: padding-box !important; - } - - [class*="web-to-plex"]::-webkit-scrollbar-track, [class*="web-to-plex"]::-moz-scrollbar-track { - background: #0000 !important; - } - - [class*="web-to-plex"]::-webkit-input-placeholder, [class*="web-to-plex"]::-moz-placeholder, [class*="web-to-plex"]:-moz-placeholder { - color: #999 !important; - } - - [class*="web-to-plex"] input[type="text"], [class*="web-to-plex"] input[type="password"], [class*="web-to-plex"] select { - width: 30vw !important; - line-height: 1.5em !important; - transition: background 0.2s !important; - display: block !important; - height: 38px !important; - padding: 6px 12px !important; - font-size: 16px !important; - color: #eee !important; - vertical-align: middle; - background: rgba(255, 255, 255, 0.25) !important; - border: 3px solid rgba(0, 0, 0, 0) !important; - border-radius: 3px !important; - font-family: inherit !important; - margin: 0 !important; - } - - [class*="web-to-plex"] select { - margin-left: 10px !important; - font-size: 16px !important; - line-height: inherit !important; - text-transform: none !important; - } - - [class*="web-to-plex"] option { - background: #3f4245 !important; - } - - [class*="web-to-plex"] button { - padding: 10px 18px !important; - font-size: 16px !important; - line-height: 1.33 !important; - border-radius: 3px !important; - font-family: inherit !important; - text-transform: uppercase !important; - border: 0 !important; - box-shadow: none !important; - position: relative !important; - overflow: hidden !important; - color: #fff; - background: #cc7b19; - margin-bottom: 0 !important; - font-weight: 400 !important; - vertical-align: middle; - cursor: pointer !important; - white-space: nowrap; - user-select: none; - transition: all 0.1s !important; - } +* Web to Plex +*/ + +@charset "utf-8"; +@font-face { + font-family: "Plex"; + src: local(Plex), + url(//webtoplex.github.io/font/Plex.woff2) format('woff2'), + url(//webtoplex.github.io/font/Plex.woff) format('woff'); + font-weight: 400; + font-style: normal; +} + +@font-face { + font-family: "Plex-bold"; + src: local(Plex-bold), + url(//webtoplex.github.io/font/Plex.bold.woff2) format('woff2'), + url(//webtoplex.github.io/font/Plex.bold.woff) format('woff'); + font-weight: 400; + font-style: normal; +} + +/* Basic/Global Styling */ +*::-webkit-scrollbar { + width: 10px; +} + +*::-webkit-scrollbar-thumb { + min-height: 50px; + background: rgba(255, 255, 255, 0.15); + border: 2px solid rgba(0, 0, 0, 0); + border-radius: 8px; + background-clip: padding-box; +} - [class*="web-to-plex"] button:hover { - background: #e59029; - } +*::-webkit-scrollbar-track { + /* background: url(../img/noise.png) fixed, #3f4245 !important; */ +} + +*::placeholder { + color: #999 !important; +} - [class*="web-to-plex"] input::placeholder, [class*="web-to-plex"] input:placeholder { +*::-webkit-input-placeholder { color: #999 !important; - } +} + +/* Web to Plex */ +[class*="web-to-plex"] input[type="text"], [class*="web-to-plex"] input[type="password"], [class*="web-to-plex"] select { + width: 30vw !important; + line-height: 1.5em !important; + transition: background 0.2s !important; + display: block !important; + height: 38px !important; + padding: 6px 12px !important; + font-size: 16px !important; + color: var(--white) !important; + vertical-align: middle; + background: rgba(255, 255, 255, 0.25) !important; + border: 3px solid rgba(0, 0, 0, 0) !important; + border-radius: 3px !important; + font-family: inherit !important; + margin: 0 !important; +} + +[class*="web-to-plex"] select { + margin-left: 10px !important; + font-size: 16px !important; + line-height: inherit !important; + text-transform: none !important; +} + +[class*="web-to-plex"] option { + background: var(--light-grey) !important; +} + +[class*="web-to-plex"] button { + padding: 10px 18px !important; + font-size: 16px !important; + line-height: 1.33 !important; + border-radius: 3px !important; + font-family: inherit !important; + text-transform: uppercase !important; + border: 0 !important; + box-shadow: none !important; + position: relative !important; + overflow: hidden !important; + color: var(--light-white); + background: var(--orange); + margin-bottom: 0 !important; + font-weight: 400 !important; + vertical-align: middle; + cursor: pointer !important; + white-space: nowrap; + user-select: none; + transition: all 0.1s !important; +} - [class*="web-to-plex"][disabled], [class*="web-to-plex"] [disabled] { - cursor: not-allowed !important; - color: #909090EE !important; - } +[class*="web-to-plex"] button:hover { + background: var(--light-orange); +} + +[class*="web-to-plex"] input::placeholder, [class*="web-to-plex"] input:placeholder { + color: var(--dark-grey) !important; +} + +[class*="web-to-plex"][disabled], [class*="web-to-plex"] [disabled] { + cursor: not-allowed !important; + color: var(--disabled) !important; +} /* Web to Plex notifications */ .web-to-plex-notification { - background: #F45A26 !important; + background: var(--dark-orange) !important; border-radius: 4px !important; - color: #FFF !important; + color: var(--light-white) !important; cursor: pointer !important; display: block !important; - font-family: arial, verdana, sans-serif !important; + font-family: "Plex", "Open Sans Regular", "Helvetica Neue", Helvetica, Arial, sans-serif, system !important; font-size: 20px !important; text-align: center !important; @@ -108,31 +133,31 @@ /* Web to Plex general information notifications */ .web-to-plex-notification.info { - background: #666 !important; + background: var(--grey) !important; } /* Web to Plex update notifications */ .web-to-plex-notification.update { - background: #2A2AFF !important; + background: var(--dark-blue) !important; } /* Web to Plex success notifications */ .web-to-plex-notification.success { - background: #03BDF9 !important; + background: var(--light-blue) !important; } /* Web to Plex error/warning notifications */ .web-to-plex-notification.warning, .web-to-plex-notification.error { - background: #FF2A2A !important; + background: var(--red) !important; } /* Web to Plex prompts */ .web-to-plex-prompt { - background: #0008 !important; + background: var(--half-black) !important; box-sizing: border-box !important; - color: #eee !important; + color: var(--white) !important; display: block !important; - font-family: Open Sans Regular, Helvetica Neue, Helvetica, Arial, sans-serif !important; + font-family: "Plex", "Open Sans Regular", "Helvetica Neue", Helvetica, Arial, sans-serif, system !important; font-size: 14px !important; line-height: 24px !important; overflow: auto !important; @@ -149,10 +174,11 @@ } .web-to-plex-prompt-body { - background: #282828; + background: var(--black); border: 0 !important; border-radius: 3px !important; box-shadow: 0 5px 15px #0008 !important; + box-sizing: content-box !important; display: block !important; left: 20% !important; @@ -166,10 +192,10 @@ } .web-to-plex-prompt-header, .web-to-plex-prompt-footer { - background: #32323240 !important; + background: var(--quarter-grey) !important; border: 1px solid #0000 !important; box-sizing: border-box !important; - color: #eee !important; + color: var(--white) !important; font: inherit !important; font-size: 2em !important; line-height: initial !important; @@ -182,11 +208,11 @@ height: 65px !important; width: 100% !important; - -webkit-tap-highlight-color: #0000; + -webkit-tap-highlight-color: var(--transparent); } .web-to-plex-prompt-header { - border-bottom-color: #222 !important; + border-bottom-color: var(--light-black) !important; border-bottom-width: 1px !important; border-top-left-radius: 3px !important; border-top-right-radius: 3px !important; @@ -203,7 +229,7 @@ overflow-x: hidden !important; overflow-y: auto !important; - padding: 12px !important; + padding: 0 12px 5px !important; position: relative !important; top: 65px !important; @@ -211,10 +237,10 @@ } .web-to-plex-prompt-option { - background: #32323240 !important; + background: var(--quarter-grey) !important; border: 1px solid #202020 !important; border-radius: 3px !important; - color: #999 !important; + color: var(--light-grey) !important; display: block !important; text-align: left !important; @@ -225,22 +251,21 @@ } .web-to-plex-prompt-option.mutable { + cursor: pointer; max-width: 60% !important; } .web-to-plex-prompt-option.mutable > h2 { - background: #0000 !important; - color: inherit !important; - font-family: inherit !important; - font-size: initial !important; - text-align: inherit !important; + background: var(--transparent) !important; + color: inherit !important; + font-family: inherit !important; + font-size: initial !important; + text-align: inherit !important; - margin: inherit !important; + margin: inherit !important; } -.web-to-plex-prompt-option.mutable > .remove, .web-to-plex-prompt-option.mutable > .choose { - background: #ffffff40 !important; - border-radius: 3px !important; +.web-to-plex-prompt-option.mutable [glyph] { transition: all 0.1s !important; height: 30px !important; @@ -248,67 +273,67 @@ float: right !important; margin-right: -2% !important; - margin-top: -8% !important; + margin-top: -5% !important; padding: 0 !important; } -.web-to-plex-prompt-option.mutable > .remove:hover, .web-to-plex-prompt-option.mutable > .choose:hover { - background: #ffffff4d !important; +.web-to-plex-prompt-option.mutable.choose [glyph] { + color: var(--grey); } -.web-to-plex-prompt-option.mutable > .remove::after { - content: '\00d7' !important; +.web-to-plex-prompt-option.mutable.chosen:first-child { + border-color: var(--green) !important; } -.web-to-plex-prompt-option.mutable > .choose::after { - content: '\2218' !important; -} - -.web-to-plex-prompt-option.mutable:first-child:last-child > .choose, .web-to-plex-prompt-option.mutable.chosen > .choose { - background: #80F08088 !important; +.web-to-plex-prompt-option.mutable.chosen:first-child [glyph] { + color: var(--green); } .web-to-plex-prompt-option.mutable > .quality { - width: 50% !important; + width: 50% !important; } .web-to-plex-prompt-option.mutable > .location { - width: 90% !important; + width: 90% !important; } .web-to-plex-prompt-option.mutable > .location:last-child:not(:first-child) { - margin-top: 5px !important; + margin-top: 5px !important; } .web-to-plex-permission:after { - background: #0000; - border-radius: 3px; - border: 0; - color: #cc7b19; - content: "\29eb"; - display: inline-block; - font-size: 150%; - padding: 0; - text-align: center; + background: var(--transparent); + border-radius: 3px; + border: 0; + color: var(--orange); + content: "\29eb"; + display: inline-block; + font-size: 150%; + padding: 0; + text-align: center; - margin: 0; - position: absolute; - right: 3%; + margin: 0; + position: absolute; + right: 3%; - height: 2em; - width: 2em; + height: 2em; + width: 2em; } .web-to-plex-prompt-footer { text-align: right !important; border-bottom-left-radius: 3px !important; border-bottom-right-radius: 3px !important; - border-top-color: #222 !important; + border-top-color: var(--light-black) !important; border-top-width: 1px !important; bottom: 0 !important; } +.web-to-plex-prompt-footer > * { + vertical-align: text-bottom !important; +} + .web-to-plex-prompt-input { float: left !important; position: relative !important; @@ -321,338 +346,358 @@ } .web-to-plex-prompt-accept { - background: #cc7b19 !important; + background: var(--orange) !important; margin-left: 5px !important; } .web-to-plex-prompt-accept:hover { - background: #e59029 !important; + background: var(--light-orange) !important; } .web-to-plex-prompt-decline { - background: #ffffff40 !important; + background: var(--quarter-white) !important; } .web-to-plex-prompt-decline:hover { - background: #ffffff4d !important; + background: var(--half-white) !important; } /* Web to Plex buttons */ .web-to-plex-button [module] { - position: relative !important; + position: relative !important; } .web-to-plex-button * { - border: none !important; - text-transform: none !important; + border: none !important; + text-transform: none !important; } .web-to-plex-button { - background-color: #3F4245 !important; - border: none !important; - color: #FFF !important; - font-family: Open Sans Semibold, Helvetica Neue, Helvetica, Arial, sans-serif !important; - font-size: 1em !important; - font-weight: 100 !important; - text-align: center !important; - - bottom: 5px !important; - left: 5px !important; - padding: 10px !important; - position: fixed !important; - right: unset !important; - z-index: 999999 !important; - - min-height: 0 !important; - min-width: 0 !important; - height: 72px !important; - width: 180px !important; - - transition: all 0.3s ease !important; + background-color: var(--light-grey) !important; + border: none !important; + color: var(--light-white) !important; + display: block !important; + font-family: "Plex", "Open Sans Regular", "Helvetica Neue", Helvetica, Arial, sans-serif, system !important; + font-size: 1em !important; + font-weight: 100 !important; + text-align: center !important; + + bottom: 5px !important; + left: 5px !important; + padding: 10px !important; + position: fixed !important; + right: unset !important; + z-index: 999999 !important; + + min-height: 0 !important; + min-width: 0 !important; + height: 72px !important; + width: 180px !important; + + transition: all 0.3s ease !important; } .web-to-plex-button.hide { - display: initial !important; + display: initial !important; } .web-to-plex-button.hide:not(:hover), .web-to-plex-button.sleeper { - opacity: 0.1; + opacity: 0.1; } *:not(#plexit-bookmarklet-frame) ~ .web-to-plex-button { - margin-left: 0px !important; + margin-left: 0px; } #plexit-bookmarklet-frame ~ .web-to-plex-button { - margin-left: 280px !important; + margin-left: 280px !important; } *:not(#plexit-bookmarklet-frame) + .web-to-plex-button #plexit, #plexit-bookmarklet-frame + .web-to-plex-button #wtp-plexit { - display: none !important; + display: none !important; } .floating.web-to-plex-button { - border-radius: 50px !important; - box-shadow: 0 4px 10px 0 rgba(0, 0, 0, 0.3) !important; + border-radius: 50px !important; + box-shadow: 0 4px 10px 0 rgba(0, 0, 0, 0.3) !important; - height: 75px !important; - width: 75px !important; + height: 75px !important; + width: 75px !important; } .floating.web-to-plex-button::after { - content: ' ' !important; + content: ' ' !important; - background: #666 !important; - border: 1px solid #888 !important; - border-radius: 16px !important; + background: var(--grey) !important; + border: 1px solid #888 !important; + border-radius: 16px !important; - right: 0 !important; - top: 0 !important; - position: absolute !important; + right: 0 !important; + top: 0 !important; + position: absolute !important; - height: 16px !important; - width: 16px !important; + height: 16px !important; + width: 16px !important; - transition: background 0.4s linear !important; + transition: background 0.4s linear !important; } .floating.web-to-plex-button:not(.restarting):active, .floating.web-to-plex-button:not(.restarting):hover { - box-shadow: 1px 5px 20px 0 rgba(0, 0, 0, 0.6) !important; - cursor: pointer !important; + box-shadow: 1px 5px 20px 0 rgba(0, 0, 0, 0.6) !important; + cursor: pointer !important; } .floating.web-to-plex-button:focus { - outline: #0000 !important; + outline: #0000 !important; } .web-to-plex-button.wtp--download::after, .web-to-plex-button.wtp--download::before { - background: #265AF4 !important; + background: var(--blue) !important; } .web-to-plex-button.wtp--queued::after, .web-to-plex-button.wtp--queued::before { - background: #568AF4 !important; + background: var(--light-blue) !important; } .web-to-plex-button.wtp--found::after, .web-to-plex-button.wtp--found::before { - background: #F9BD03 !important; + background: var(--light-orange) !important; } .web-to-plex-button.wtp--error::after, .web-to-plex-button.wtp--error::before { - background: #FF2A2A !important; + background: var(--red) !important; } .web-to-plex-button::before { - content: ' ' !important; + content: ' ' !important; - background: #FFF6 !important; - border-radius: inherit !important; - display: none !important; + background: var(--half-white) !important; + border-radius: inherit !important; + display: none !important; - margin-top: -10px !important; - margin-left: -10px !important; + margin-top: -10px !important; + margin-left: -10px !important; - height: 75px !important; - width: 75px !important; + height: 75px !important; + width: 75px !important; - position: absolute !important; - z-index: 9999999 !important; + position: absolute !important; + z-index: 9999999 !important; } .web-to-plex-button.animate::before { - display: block !important; + display: block !important; - -webkit-transform: scale(0); - -moz-transform: scale(0); - -o-transform: scale(0); - transform: scale(0); + -webkit-transform: scale(0); + -moz-transform: scale(0); + -o-transform: scale(0); + transform: scale(0); - -webkit-animation: web-to-plex-ripple 0.5s linear; - -moz-animation: web-to-plex-ripple 0.5s linear; - -o-animation: web-to-plex-ripple 0.5s linear; - animation: web-to-plex-ripple 0.5s linear; + -webkit-animation: web-to-plex-ripple 0.5s linear; + -moz-animation: web-to-plex-ripple 0.5s linear; + -o-animation: web-to-plex-ripple 0.5s linear; + animation: web-to-plex-ripple 0.5s linear; } @-webkit-keyframes web-to-plex-ripple { - 100% { - opacity: 0; - -webkit-transform: scale(2.5); - } + 100% { + opacity: 0; + -webkit-transform: scale(2.5); + } } @-moz-keyframes web-to-plex-ripple { - 100% { - opacity: 0; - -moz-transform: scale(2.5); - } + 100% { + opacity: 0; + -moz-transform: scale(2.5); + } } @-o-keyframes web-to-plex-ripple { - 100% { - opacity: 0; - -o-transform: scale(2.5); - } + 100% { + opacity: 0; + -o-transform: scale(2.5); + } } @keyframes web-to-plex-ripple { - 100% { - opacity: 0; - transform: scale(2.5); - } + 100% { + opacity: 0; + transform: scale(2.5); + } } .web-to-plex-button.open, #plexit-bookmarklet-frame + .web-to-plex-button { - opacity: 1; + opacity: 1; - width: 350px !important; + width: 350px !important; } + .web-to-plex-button .list-name { - float: left !important; + float: left !important; } .web-to-plex-button ul { - margin: 0 !important; - padding-left: 0 !important; + margin: 0 !important; + padding-left: 0 !important; } .web-to-plex-button li { - display: inline-block !important; - list-style: none !important; + display: inline-block !important; + list-style: none !important; - margin: 0 !important; - padding: 5px !important; - vertical-align: bottom; + margin: 0 !important; + padding: 5px !important; + vertical-align: bottom; } .web-to-plex-button li > img { - display: inline !important; + display: inline !important; - margin-top: 0 !important; + margin-top: 0 !important; } .web-to-plex-button.hide.closed li:not(:first-child) { - display: none !important; + display: none !important; } *:not(#plexit-bookmarklet-frame) + .web-to-plex-button.closed .list-item { - float: left !important; - opacity: 0; - transition: opacity 0 !important; + float: left !important; + opacity: 0; + transition: opacity 0 !important; } .web-to-plex-button.open .list-item { - opacity: 1; - transition: opacity 2s !important; + opacity: 1; + transition: opacity 2s !important; } .web-to-plex-button.open li:hover [tooltip]::before, .web-to-plex-button.open [tooltip]:hover::before { - content: attr(tooltip) !important; + content: attr(tooltip) !important; - background: #3F424599 !important; - border-radius: 3px !important; - color: #fff !important; - font-family: arial, calibri, sans-serif, sans, monospace !important; - font-size: 15px !important; + background: var(--quarter-grey) !important; + border-radius: 3px !important; + color: var(--light-white) !important; + font-family: "Plex", "Open Sans Regular", "Helvetica Neue", Helvetica, Arial, sans-serif, system !important; + font-size: 15px !important; - bottom: 85px !important; - left: 35px !important; - padding: 3px 6px !important; - position: absolute !important; + bottom: 85px !important; + left: 35px !important; + padding: 3px 6px !important; + position: absolute !important; +} + +.web-to-plex-minion { + cursor: pointer; } /* bbodine @CodePen - https://codepen.io/bbodine1/pen/novBm */ [class*="web-to-plex"] .checkbox { - width: 80px; - height: 26px; - background: #000; - margin: 15px 0; - position: relative; - border-radius: 50px; - box-shadow: inset 0px 1px 1px rgba(0, 0, 0, 0.5), 0px 1px 0px rgba(255, 255, 255, 0.2); + width: 80px; + height: 26px; + background: var(--dark-black); + margin: 15px 0; + position: relative; + border-radius: 50px; + box-shadow: inset 0px 1px 1px rgba(0, 0, 0, 0.5), 0px 1px 0px rgba(255, 255, 255, 0.2); } [class*="web-to-plex"] span.checkbox { - display: inline-block; + display: inline-block; - margin: 0; - vertical-align: text-bottom; + margin: 0; + vertical-align: text-bottom; } [class*="web-to-plex"] .checkbox::after { - content: 'OFF'; - color: #666; - position: absolute; - right: 10px; - z-index: 0; - font: 12px/26px Arial, sans-serif; - font-weight: bold; - text-shadow: 1px 1px 0px rgba(255, 255, 255, 0.15); + content: 'OFF'; + color: var(--grey); + position: absolute; + right: 10px; + z-index: 0; + font: 12px/26px Arial, sans-serif; + font-weight: bold; + text-shadow: 1px 1px 0px rgba(255, 255, 255, 0.15); } [class*="web-to-plex"] .checkbox::before { - content: 'ON'; - color: #cc7b19; - position: absolute; - left: 10px; - z-index: 0; - font: 12px/26px Arial, sans-serif; - font-weight: bold; + content: 'ON'; + color: var(--orange); + position: absolute; + left: 10px; + z-index: 0; + font: 12px/26px Arial, sans-serif; + font-weight: bold; } [class*="web-to-plex"] .checkbox[prompt-yes]::before { - content: attr(prompt-yes); - text-transform: uppercase; + content: attr(prompt-yes); + text-transform: uppercase; } [class*="web-to-plex"] .checkbox[prompt-no]::after { - content: attr(prompt-no); - text-transform: uppercase; + content: attr(prompt-no); + text-transform: uppercase; } [class*="web-to-plex"] .checkbox[prompt-size="large"i]::before, .checkbox[prompt-size="large"i]::after { - font-size: 30px !important; + font-size: 30px !important; } [class*="web-to-plex"] .checkbox[prompt-size="medium"i]::before, .checkbox[prompt-size="medium"i]::after { - font-size: 21px !important; + font-size: 21px !important; } [class*="web-to-plex"] .checkbox[prompt-size="normal"i]::before, .checkbox[prompt-size="normal"i]::after { - font-size: 12px !important; + font-size: 12px !important; } [class*="web-to-plex"] .checkbox[prompt-size="small"i]::before, .checkbox[prompt-size="small"i]::after { - font-size: 6px !important; + font-size: 6px !important; } [class*="web-to-plex"] .checkbox[prompt="y/n"i]::before { - content: 'YES'; + content: 'YES'; } [class*="web-to-plex"] .checkbox[prompt="y/n"i]::after { - content: 'NO'; + content: 'NO'; } [class*="web-to-plex"] .checkbox label { - display: block; - width: 34px; - height: 20px; - cursor: pointer; - position: absolute; - top: 3px; - left: 3px; - z-index: 1; - background: #666; - border-radius: 50px; - transition: all 0.4s ease; - box-shadow: 0px 2px 5px 0px rgba(0, 0, 0, 0.3); + display: block; + width: 34px; + height: 20px; + cursor: pointer; + position: absolute; + top: 3px; + left: 3px; + z-index: 1; + background: var(--grey); + border-radius: 50px; + transition: all 0.4s ease; + box-shadow: 0px 2px 5px 0px rgba(0, 0, 0, 0.3); } [class*="web-to-plex"] .checkbox input[type=checkbox] { - visibility: hidden; + visibility: hidden; } [class*="web-to-plex"] .checkbox input[type=checkbox]:checked + label { - left: 43px; - background: #cc7b19; + left: 43px; + background: var(--orange); } [class*="web-to-plex"] .checkbox[disabled] { - opacity: 0.25 !important; + opacity: 0.25 !important; +} + +/* Minor Bugs */ +[glyph], [class*="glyphicon"] { + width: 1.1em; +} + +[glyph][gradient], [class*="glyphicon"][gradient] { + background-clip: text !important; + -moz-background-clip: text !important; + -webkit-background-clip: text !important; + + color: #0000 !important; + -webkit-text-fill-color: #0000 !important; } diff --git a/win/sites/couchpotato/index.css b/win/sites/couchpotato/index.css index e69de29..6e2a6b1 100644 --- a/win/sites/couchpotato/index.css +++ b/win/sites/couchpotato/index.css @@ -0,0 +1,23 @@ +.web-to-plex-minion.wtp--download { + color: var(--blue)!important; +} + +.web-to-plex-minion.wtp--download:hover { + color: var(--light-blue)!important; +} + +.web-to-plex-minion.wtp--found { + color: var(--orange)!important; +} + +.web-to-plex-minion.wtp--found:hover { + color: var(--light-orange)!important; +} + +#tt--0-0 { + color: var(--grey)!important; +} + +#tt--0-0:hover { + color: var(--light-grey)!important; +} diff --git a/win/sites/fandango/index.css b/win/sites/fandango/index.css index e69de29..5f5284d 100644 --- a/win/sites/fandango/index.css +++ b/win/sites/fandango/index.css @@ -0,0 +1,27 @@ +.web-to-plex-minion { + color: #727272; +} + +.web-to-plex-minion.wtp--download { + color: #f45a26!important; +} + +.web-to-plex-minion.wtp--found { + color: #e5a00d!important; +} + +.web-to-plex-wrapper:hover, .web-to-plex-wrapper:hover > *, .web-to-plex-minion.wtp--download:hover { + color: #f67e56!important; +} + +.web-to-plex-minion.wtp--found:hover { + color: #f8c022!important; +} + +#tt--0-0 { + color: #666!important; +} + +#tt--0-0:hover { + color: #888!important; +} diff --git a/win/sites/glyphs.css b/win/sites/glyphs.css new file mode 100644 index 0000000..4b29026 --- /dev/null +++ b/win/sites/glyphs.css @@ -0,0 +1,2463 @@ +/** Available Icons: + * adjust + * airplane + * alarm + * albums + * amazon + * anchor + * android + * apple + * asterisk + * ax + * badoo + * ban + * bank + * barcode + * baseball + * basketball + * bathrobe + * beer + * behance + * bell + * bicycle + * bin + * binoculars + * blacksmith + * blog + * blogger + * bluetooth + * boat + * bold + * bomb + * book + * bookmark + * bowling + * briefcase + * brush + * bug + * building + * bullets + * bullhorn + * buoy + * bus + * cake + * calculator + * calendar + * camera + * candle + * car + * cardio + * cargo + * cars + * celebration + * certificate + * charts + * chat + * check + * cleaning + * clock + * cloud + * cogwheel + * cogwheels + * coins + * collapse + * comments + * compass + * compressed + * conversation + * crop + * crown + * cup + * cutlery + * dashboard + * delete + * deviantart + * direction + * dislikes + * display + * divide + * dog + * download + * dress + * dribbble + * drink + * dropbox + * dumbbell + * earphone + * edit + * eject + * electricity + * embed + * envelope + * euro + * evernote + * exit + * expand + * eyedropper + * fabric + * facebook + * factory + * fax + * female + * file + * film + * filter + * fins + * fire + * fishes + * flag + * flash + * flickr + * flower + * font + * forrst + * forward + * foursquare + * fullscreen + * gamepad + * gbp + * gift + * girl + * github + * glass + * global + * globe + * golf + * goodreads + * google_plus + * grater + * group + * hdd + * header + * headphones + * headset + * heart + * heat + * history + * hockey + * home + * hospital + * imac + * inbox + * instagram + * instapaper + * ios + * ipad + * iphone + * ipod + * italic + * jolicloud + * justify + * kettle + * keynote + * keys + * kiosk + * last_fm + * leaf + * leather + * lightbulb + * link + * linked_in + * list + * lock + * luggage + * macbook + * magic + * magnet + * male + * microphone + * minus + * money + * moon + * more + * move + * music + * mute + * myspace + * nails + * nameplate + * note + * notes + * ok + * package + * pants + * paperclip + * parents + * pause + * pen + * pencil + * piano + * picasa + * picture + * pin + * pinboard + * pinterest + * pipe + * pizza + * play + * playlist + * playstation + * plus + * podium + * pool + * posterous_spaces + * pot + * power + * print + * projector + * pushpin + * qrcode + * quora + * rabbit + * radar + * random + * read_it_later + * readability + * record + * redo + * refresh + * remove + * repeat + * restart + * retweet + * rewind + * riflescope + * ring + * road + * roundabout + * router + * rss + * rugby + * ruller + * sampler + * scissors + * screenshot + * search + * send + * server + * settings + * share + * shield + * shirt + * shop + * signal + * skateboard + * skitch + * skull + * skype + * smoking + * snowflake + * sort + * sorting + * spade + * spotify + * spray + * star + * stats + * stop + * stopwatch + * stroller + * stumbleupon + * subtitles + * suitcase + * sun + * sweater + * table + * tablet + * tag + * tags + * tie + * tint + * tower + * train + * transfer + * translate + * truck + * tumblr + * turtle + * twitter + * umbrella + * unchecked + * underwear + * undo + * unlock + * unshare + * upload + * usd + * user + * vases + * vcard + * vimeo + * vine + * wallet + * webcam + * wifi + * windows + * woman + * wordpress + * wrench + * xbox + * xing + * yahoo + * yelp + * youtube + * zootool + */ + +@charset "utf-8"; +@font-face { + font-family: Glyphicons Regular; + src: local(Glyphicons), + url(/font/Glyphicons.woff) format("woff"), + url(//webtoplex.github.io/font/Glyphicons.woff) format("woff"); + font-weight: 400; + font-style: normal +} + +@font-face { + font-family: Glyphicons Social Regular; + src: local(Glyphicons Social), + url(/font/Glyphicons Social.woff) format("woff"), + url(//webtoplex.github.io/font/Glyphicons Social.woff) format("woff"); + font-weight: 400; + font-style: normal +} + +[class*="glyphicon"i], [glyph] { + position: relative; + top: 1px; + display: inline-block; + font-family: Glyphicons Regular !important; + font-style: normal; + font-weight: 400; + line-height: 1; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale + width: 1em; +} + +.glyphicon-social, [glyph~="social"] { + font-family: Glyphicons Social Regular +} + +.glyphicon-lg, [glyph~="lg"] { + font-size: 1.33333333em; + line-height: .75em; + vertical-align: -15% +} + +.glyphicon-2x, [glyph~="2x"] { + font-size: 2em +} + +.glyphicon-3x, [glyph~="3x"] { + font-size: 3em +} + +.glyphicon-4x, [glyph~="4x"] { + font-size: 4em +} + +.glyphicon-5x, [glyph~="5x"] { + font-size: 5em +} + +[class*="glyphicon"].glass:before, [glyph~="glass"]:before { + content: "\e001" +} + +[class*="glyphicon"].leaf:before, [glyph~="leaf"]:before { + content: "\e002" +} + +[class*="glyphicon"].dog:before, [glyph~="dog"]:before { + content: "\1F415" +} + +[class*="glyphicon"].user:before, [glyph~="user"]:before { + content: "\e004" +} + +[class*="glyphicon"].girl:before, [glyph~="girl"]:before { + content: "\1F467" +} + +[class*="glyphicon"].car:before, [glyph~="car"]:before { + content: "\e006" +} + +[class*="glyphicon"].user-add:before { + content: "\e007" +} + +[class*="glyphicon"].user-remove:before { + content: "\e008" +} + +[class*="glyphicon"].film:before, [glyph~="film"]:before { + content: "\e009" +} + +[class*="glyphicon"].magic:before, [glyph~="magic"]:before { + content: "\e010" +} + +[class*="glyphicon"].envelope:before, [glyph~="envelope"]:before { + content: "\2709" +} + +[class*="glyphicon"].camera:before, [glyph~="camera"]:before { + content: "\1F4F7" +} + +[class*="glyphicon"].heart:before, [glyph~="heart"]:before { + content: "\e013" +} + +[class*="glyphicon"].beach-umbrella:before { + content: "\e014" +} + +[class*="glyphicon"].train:before, [glyph~="train"]:before { + content: "\1F686" +} + +[class*="glyphicon"].print:before, [glyph~="print"]:before { + content: "\e016" +} + +[class*="glyphicon"].bin:before, [glyph~="bin"]:before { + content: "\e017" +} + +[class*="glyphicon"].music:before, [glyph~="music"]:before { + content: "\e018" +} + +[class*="glyphicon"].note:before, [glyph~="note"]:before { + content: "\e019" +} + +[class*="glyphicon"].heart-empty:before { + content: "\e020" +} + +[class*="glyphicon"].home:before, [glyph~="home"]:before { + content: "\e021" +} + +[class*="glyphicon"].snowflake:before, [glyph~="snowflake"]:before { + content: "\2744" +} + +[class*="glyphicon"].fire:before, [glyph~="fire"]:before { + content: "\1F525" +} + +[class*="glyphicon"].magnet:before, [glyph~="magnet"]:before { + content: "\e024" +} + +[class*="glyphicon"].parents:before, [glyph~="parents"]:before { + content: "\e025" +} + +[class*="glyphicon"].binoculars:before, [glyph~="binoculars"]:before { + content: "\e026" +} + +[class*="glyphicon"].road:before, [glyph~="road"]:before { + content: "\e027" +} + +[class*="glyphicon"].search:before, [glyph~="search"]:before { + content: "\e028" +} + +[class*="glyphicon"].cars:before, [glyph~="cars"]:before { + content: "\e029" +} + +[class*="glyphicon"].notes-2:before { + content: "\e030" +} + +[class*="glyphicon"].pencil:before, [glyph~="pencil"]:before { + content: "\270F" +} + +[class*="glyphicon"].bus:before, [glyph~="bus"]:before { + content: "\1F68C" +} + +[class*="glyphicon"].wifi-alt:before { + content: "\e033" +} + +[class*="glyphicon"].luggage:before, [glyph~="luggage"]:before { + content: "\e034" +} + +[class*="glyphicon"].old-man:before { + content: "\e035" +} + +[class*="glyphicon"].woman:before, [glyph~="woman"]:before { + content: "\1F469" +} + +[class*="glyphicon"].file:before, [glyph~="file"]:before { + content: "\e037" +} + +[class*="glyphicon"].coins:before, [glyph~="coins"]:before { + content: "\e038" +} + +[class*="glyphicon"].airplane:before, [glyph~="airplane"]:before { + content: "\2708" +} + +[class*="glyphicon"].notes:before, [glyph~="notes"]:before { + content: "\e040" +} + +[class*="glyphicon"].stats:before, [glyph~="stats"]:before { + content: "\e041" +} + +[class*="glyphicon"].charts:before, [glyph~="charts"]:before { + content: "\e042" +} + +[class*="glyphicon"].pie-chart:before { + content: "\e043" +} + +[class*="glyphicon"].group:before, [glyph~="group"]:before { + content: "\e044" +} + +[class*="glyphicon"].keys:before, [glyph~="keys"]:before { + content: "\e045" +} + +[class*="glyphicon"].calendar:before, [glyph~="calendar"]:before { + content: "\1F4C5" +} + +[class*="glyphicon"].router:before, [glyph~="router"]:before { + content: "\e047" +} + +[class*="glyphicon"].camera-small:before { + content: "\e048" +} + +[class*="glyphicon"].dislikes:before, [glyph~="dislikes"]:before { + content: "\e049" +} + +[class*="glyphicon"].star:before, [glyph~="star"]:before { + content: "\e050" +} + +[class*="glyphicon"].link:before, [glyph~="link"]:before { + content: "\e051" +} + +[class*="glyphicon"].eye-open:before { + content: "\e052" +} + +[class*="glyphicon"].eye-close:before { + content: "\e053" +} + +[class*="glyphicon"].alarm:before, [glyph~="alarm"]:before { + content: "\e054" +} + +[class*="glyphicon"].clock:before, [glyph~="clock"]:before { + content: "\e055" +} + +[class*="glyphicon"].stopwatch:before, [glyph~="stopwatch"]:before { + content: "\e056" +} + +[class*="glyphicon"].projector:before, [glyph~="projector"]:before { + content: "\e057" +} + +[class*="glyphicon"].history:before, [glyph~="history"]:before { + content: "\e058" +} + +[class*="glyphicon"].truck:before, [glyph~="truck"]:before { + content: "\e059" +} + +[class*="glyphicon"].cargo:before, [glyph~="cargo"]:before { + content: "\e060" +} + +[class*="glyphicon"].compass:before, [glyph~="compass"]:before { + content: "\e061" +} + +[class*="glyphicon"].keynote:before, [glyph~="keynote"]:before { + content: "\e062" +} + +[class*="glyphicon"].paperclip:before, [glyph~="paperclip"]:before { + content: "\1F4CE" +} + +[class*="glyphicon"].power:before, [glyph~="power"]:before { + content: "\e064" +} + +[class*="glyphicon"].lightbulb:before, [glyph~="lightbulb"]:before { + content: "\e065" +} + +[class*="glyphicon"].tag:before, [glyph~="tag"]:before { + content: "\e066" +} + +[class*="glyphicon"].tags:before, [glyph~="tags"]:before { + content: "\e067" +} + +[class*="glyphicon"].cleaning:before, [glyph~="cleaning"]:before { + content: "\e068" +} + +[class*="glyphicon"].ruller:before, [glyph~="ruller"]:before { + content: "\e069" +} + +[class*="glyphicon"].gift:before, [glyph~="gift"]:before { + content: "\e070" +} + +[class*="glyphicon"].umbrella:before, [glyph~="umbrella"]:before { + content: "\2602" +} + +[class*="glyphicon"].book:before, [glyph~="book"]:before { + content: "\e072" +} + +[class*="glyphicon"].bookmark:before, [glyph~="bookmark"]:before { + content: "\1F516" +} + +[class*="glyphicon"].wifi:before, [glyph~="wifi"]:before { + content: "\e074" +} + +[class*="glyphicon"].cup:before, [glyph~="cup"]:before { + content: "\e075" +} + +[class*="glyphicon"].stroller:before, [glyph~="stroller"]:before { + content: "\e076" +} + +[class*="glyphicon"].headphones:before, [glyph~="headphones"]:before { + content: "\e077" +} + +[class*="glyphicon"].headset:before, [glyph~="headset"]:before { + content: "\e078" +} + +[class*="glyphicon"].warning-sign:before { + content: "\e079" +} + +[class*="glyphicon"].signal:before, [glyph~="signal"]:before { + content: "\e080" +} + +[class*="glyphicon"].retweet:before, [glyph~="retweet"]:before { + content: "\e081" +} + +[class*="glyphicon"].refresh:before, [glyph~="refresh"]:before { + content: "\e082" +} + +[class*="glyphicon"].roundabout:before, [glyph~="roundabout"]:before { + content: "\e083" +} + +[class*="glyphicon"].random:before, [glyph~="random"]:before { + content: "\e084" +} + +[class*="glyphicon"].heat:before, [glyph~="heat"]:before { + content: "\e085" +} + +[class*="glyphicon"].repeat:before, [glyph~="repeat"]:before { + content: "\e086" +} + +[class*="glyphicon"].display:before, [glyph~="display"]:before { + content: "\e087" +} + +[class*="glyphicon"].log-book:before { + content: "\e088" +} + +[class*="glyphicon"].address-book:before { + content: "\e089" +} + +[class*="glyphicon"].building:before, [glyph~="building"]:before { + content: "\e090" +} + +[class*="glyphicon"].eyedropper:before, [glyph~="eyedropper"]:before { + content: "\e091" +} + +[class*="glyphicon"].adjust:before, [glyph~="adjust"]:before { + content: "\e092" +} + +[class*="glyphicon"].tint:before, [glyph~="tint"]:before { + content: "\e093" +} + +[class*="glyphicon"].crop:before, [glyph~="crop"]:before { + content: "\e094" +} + +[class*="glyphicon"].vector-path-square:before { + content: "\e095" +} + +[class*="glyphicon"].vector-path-circle:before { + content: "\e096" +} + +[class*="glyphicon"].vector-path-polygon:before { + content: "\e097" +} + +[class*="glyphicon"].vector-path-line:before { + content: "\e098" +} + +[class*="glyphicon"].vector-path-curve:before { + content: "\e099" +} + +[class*="glyphicon"].vector-path-all:before { + content: "\e100" +} + +[class*="glyphicon"].font:before, [glyph~="font"]:before { + content: "\e101" +} + +[class*="glyphicon"].italic:before, [glyph~="italic"]:before { + content: "\e102" +} + +[class*="glyphicon"].bold:before, [glyph~="bold"]:before { + content: "\e103" +} + +[class*="glyphicon"].text-underline:before { + content: "\e104" +} + +[class*="glyphicon"].text-strike:before { + content: "\e105" +} + +[class*="glyphicon"].text-height:before { + content: "\e106" +} + +[class*="glyphicon"].text-width:before { + content: "\e107" +} + +[class*="glyphicon"].text-resize:before { + content: "\e108" +} + +[class*="glyphicon"].left-indent:before { + content: "\e109" +} + +[class*="glyphicon"].right-indent:before { + content: "\e110" +} + +[class*="glyphicon"].align-left:before { + content: "\e111" +} + +[class*="glyphicon"].align-center:before { + content: "\e112" +} + +[class*="glyphicon"].align-right:before { + content: "\e113" +} + +[class*="glyphicon"].justify:before, [glyph~="justify"]:before { + content: "\e114" +} + +[class*="glyphicon"].list:before, [glyph~="list"]:before { + content: "\e115" +} + +[class*="glyphicon"].text-smaller:before { + content: "\e116" +} + +[class*="glyphicon"].text-bigger:before { + content: "\e117" +} + +[class*="glyphicon"].embed:before, [glyph~="embed"]:before { + content: "\e118" +} + +[class*="glyphicon"].embed-close:before { + content: "\e119" +} + +[class*="glyphicon"].table:before, [glyph~="table"]:before { + content: "\e120" +} + +[class*="glyphicon"].message-full:before { + content: "\e121" +} + +[class*="glyphicon"].message-empty:before { + content: "\e122" +} + +[class*="glyphicon"].message-in:before { + content: "\e123" +} + +[class*="glyphicon"].message-out:before { + content: "\e124" +} + +[class*="glyphicon"].message-plus:before { + content: "\e125" +} + +[class*="glyphicon"].message-minus:before { + content: "\e126" +} + +[class*="glyphicon"].message-ban:before { + content: "\e127" +} + +[class*="glyphicon"].message-flag:before { + content: "\e128" +} + +[class*="glyphicon"].message-lock:before { + content: "\e129" +} + +[class*="glyphicon"].message-new:before { + content: "\e130" +} + +[class*="glyphicon"].inbox:before, [glyph~="inbox"]:before { + content: "\e131" +} + +[class*="glyphicon"].inbox-plus:before { + content: "\e132" +} + +[class*="glyphicon"].inbox-minus:before { + content: "\e133" +} + +[class*="glyphicon"].inbox-lock:before { + content: "\e134" +} + +[class*="glyphicon"].inbox-in:before { + content: "\e135" +} + +[class*="glyphicon"].inbox-out:before { + content: "\e136" +} + +[class*="glyphicon"].cogwheel:before, [glyph~="cogwheel"]:before { + content: "\e137" +} + +[class*="glyphicon"].cogwheels:before, [glyph~="cogwheels"]:before { + content: "\e138" +} + +[class*="glyphicon"].picture:before, [glyph~="picture"]:before { + content: "\e139" +} + +[class*="glyphicon"].adjust-alt:before { + content: "\e140" +} + +[class*="glyphicon"].database-lock:before { + content: "\e141" +} + +[class*="glyphicon"].database-plus:before { + content: "\e142" +} + +[class*="glyphicon"].database-minus:before { + content: "\e143" +} + +[class*="glyphicon"].database-ban:before { + content: "\e144" +} + +[class*="glyphicon"].folder-open:before { + content: "\e145" +} + +[class*="glyphicon"].folder-plus:before { + content: "\e146" +} + +[class*="glyphicon"].folder-minus:before { + content: "\e147" +} + +[class*="glyphicon"].folder-lock:before { + content: "\e148" +} + +[class*="glyphicon"].folder-flag:before { + content: "\e149" +} + +[class*="glyphicon"].folder-new:before { + content: "\e150" +} + +[class*="glyphicon"].edit:before, [glyph~="edit"]:before { + content: "\e151" +} + +[class*="glyphicon"].new-window:before { + content: "\e152" +} + +[class*="glyphicon"].check:before, [glyph~="check"]:before { + content: "\e153" +} + +[class*="glyphicon"].unchecked:before, [glyph~="unchecked"]:before { + content: "\e154" +} + +[class*="glyphicon"].more-windows:before { + content: "\e155" +} + +[class*="glyphicon"].show-big-thumbnails:before { + content: "\e156" +} + +[class*="glyphicon"].show-thumbnails:before { + content: "\e157" +} + +[class*="glyphicon"].show-thumbnails-with-lines:before { + content: "\e158" +} + +[class*="glyphicon"].show-lines:before { + content: "\e159" +} + +[class*="glyphicon"].playlist:before, [glyph~="playlist"]:before { + content: "\e160" +} + +[class*="glyphicon"].imac:before, [glyph~="imac"]:before { + content: "\e161" +} + +[class*="glyphicon"].macbook:before, [glyph~="macbook"]:before { + content: "\e162" +} + +[class*="glyphicon"].ipad:before, [glyph~="ipad"]:before { + content: "\e163" +} + +[class*="glyphicon"].iphone:before, [glyph~="iphone"]:before { + content: "\e164" +} + +[class*="glyphicon"].iphone-transfer:before { + content: "\e165" +} + +[class*="glyphicon"].iphone-exchange:before { + content: "\e166" +} + +[class*="glyphicon"].ipod:before, [glyph~="ipod"]:before { + content: "\e167" +} + +[class*="glyphicon"].ipod-shuffle:before { + content: "\e168" +} + +[class*="glyphicon"].ear-plugs:before { + content: "\e169" +} + +[class*="glyphicon"].record:before, [glyph~="record"]:before { + content: "\e170" +} + +[class*="glyphicon"].step-backward:before { + content: "\e171" +} + +[class*="glyphicon"].fast-backward:before { + content: "\e172" +} + +[class*="glyphicon"].rewind:before, [glyph~="rewind"]:before { + content: "\e173" +} + +[class*="glyphicon"].play:before, [glyph~="play"]:before { + content: "\e174" +} + +[class*="glyphicon"].pause:before, [glyph~="pause"]:before { + content: "\e175" +} + +[class*="glyphicon"].stop:before, [glyph~="stop"]:before { + content: "\e176" +} + +[class*="glyphicon"].forward:before, [glyph~="forward"]:before { + content: "\e177" +} + +[class*="glyphicon"].fast-forward:before { + content: "\e178" +} + +[class*="glyphicon"].step-forward:before { + content: "\e179" +} + +[class*="glyphicon"].eject:before, [glyph~="eject"]:before { + content: "\e180" +} + +[class*="glyphicon"].facetime-video:before { + content: "\e181" +} + +[class*="glyphicon"].download-alt:before { + content: "\e182" +} + +[class*="glyphicon"].mute:before, [glyph~="mute"]:before { + content: "\e183" +} + +[class*="glyphicon"].volume-down:before { + content: "\e184" +} + +[class*="glyphicon"].volume-up:before { + content: "\e185" +} + +[class*="glyphicon"].screenshot:before, [glyph~="screenshot"]:before { + content: "\e186" +} + +[class*="glyphicon"].move:before, [glyph~="move"]:before { + content: "\e187" +} + +[class*="glyphicon"].more:before, [glyph~="more"]:before { + content: "\e188" +} + +[class*="glyphicon"].brightness-reduce:before { + content: "\e189" +} + +[class*="glyphicon"].brightness-increase:before { + content: "\e190" +} + +[class*="glyphicon"].circle-plus:before { + content: "\e191" +} + +[class*="glyphicon"].circle-minus:before { + content: "\e192" +} + +[class*="glyphicon"].circle-remove:before { + content: "\e193" +} + +[class*="glyphicon"].circle-ok:before { + content: "\e194" +} + +[class*="glyphicon"].circle-question-mark:before { + content: "\e195" +} + +[class*="glyphicon"].circle-info:before { + content: "\e196" +} + +[class*="glyphicon"].circle-exclamation-mark:before { + content: "\e197" +} + +[class*="glyphicon"].remove:before, [glyph~="remove"]:before { + content: "\e198" +} + +[class*="glyphicon"].ok:before, [glyph~="ok"]:before { + content: "\e199" +} + +[class*="glyphicon"].ban:before, [glyph~="ban"]:before { + content: "\e200" +} + +[class*="glyphicon"].download:before, [glyph~="download"]:before { + content: "\e201" +} + +[class*="glyphicon"].upload:before, [glyph~="upload"]:before { + content: "\e202" +} + +[class*="glyphicon"].shopping-cart:before { + content: "\e203" +} + +[class*="glyphicon"].lock:before, [glyph~="lock"]:before { + content: "\1F512" +} + +[class*="glyphicon"].unlock:before, [glyph~="unlock"]:before { + content: "\e205" +} + +[class*="glyphicon"].electricity:before, [glyph~="electricity"]:before { + content: "\e206" +} + +[class*="glyphicon"].ok-2:before { + content: "\e207" +} + +[class*="glyphicon"].remove-2:before { + content: "\e208" +} + +[class*="glyphicon"].cart-out:before { + content: "\e209" +} + +[class*="glyphicon"].cart-in:before { + content: "\e210" +} + +[class*="glyphicon"].left-arrow:before { + content: "\e211" +} + +[class*="glyphicon"].right-arrow:before { + content: "\e212" +} + +[class*="glyphicon"].down-arrow:before { + content: "\e213" +} + +[class*="glyphicon"].up-arrow:before { + content: "\e214" +} + +[class*="glyphicon"].resize-small:before { + content: "\e215" +} + +[class*="glyphicon"].resize-full:before { + content: "\e216" +} + +[class*="glyphicon"].circle-arrow-left:before { + content: "\e217" +} + +[class*="glyphicon"].circle-arrow-right:before { + content: "\e218" +} + +[class*="glyphicon"].circle-arrow-top:before { + content: "\e219" +} + +[class*="glyphicon"].circle-arrow-down:before { + content: "\e220" +} + +[class*="glyphicon"].play-button:before { + content: "\e221" +} + +[class*="glyphicon"].unshare:before, [glyph~="unshare"]:before { + content: "\e222" +} + +[class*="glyphicon"].share:before, [glyph~="share"]:before { + content: "\e223" +} + +[class*="glyphicon"].chevron-right:before { + content: "\e224" +} + +[class*="glyphicon"].chevron-left:before { + content: "\e225" +} + +[class*="glyphicon"].chevron-up:before { + content: "\e224" +} + +[class*="glyphicon"].chevron-up { + -webkit-transform: rotate(-90deg); + -ms-transform: rotate(-90deg); + transform: rotate(-90deg) +} + +[class*="glyphicon"].chevron-down:before { + content: "\e224" +} + +[class*="glyphicon"].chevron-down { + -webkit-transform: rotate(90deg); + -ms-transform: rotate(90deg); + transform: rotate(90deg) +} + +[class*="glyphicon"].bluetooth:before, [glyph~="bluetooth"]:before { + content: "\e226" +} + +[class*="glyphicon"].euro:before, [glyph~="euro"]:before { + content: "\20AC" +} + +[class*="glyphicon"].usd:before, [glyph~="usd"]:before { + content: "\e228" +} + +[class*="glyphicon"].gbp:before, [glyph~="gbp"]:before { + content: "\e229" +} + +[class*="glyphicon"].retweet-2:before { + content: "\e230" +} + +[class*="glyphicon"].moon:before, [glyph~="moon"]:before { + content: "\e231" +} + +[class*="glyphicon"].sun:before, [glyph~="sun"]:before { + content: "\2609" +} + +[class*="glyphicon"].cloud:before, [glyph~="cloud"]:before { + content: "\2601" +} + +[class*="glyphicon"].direction:before, [glyph~="direction"]:before { + content: "\e234" +} + +[class*="glyphicon"].brush:before, [glyph~="brush"]:before { + content: "\e235" +} + +[class*="glyphicon"].pen:before, [glyph~="pen"]:before { + content: "\e236" +} + +[class*="glyphicon"].zoom-in:before { + content: "\e237" +} + +[class*="glyphicon"].zoom-out:before { + content: "\e238" +} + +[class*="glyphicon"].pin:before, [glyph~="pin"]:before { + content: "\e239" +} + +[class*="glyphicon"].albums:before, [glyph~="albums"]:before { + content: "\e240" +} + +[class*="glyphicon"].rotation-lock:before { + content: "\e241" +} + +[class*="glyphicon"].flash:before, [glyph~="flash"]:before { + content: "\e242" +} + +[class*="glyphicon"].google-maps:before { + content: "\e243" +} + +[class*="glyphicon"].anchor:before, [glyph~="anchor"]:before { + content: "\2693" +} + +[class*="glyphicon"].conversation:before, [glyph~="conversation"]:before { + content: "\e245" +} + +[class*="glyphicon"].chat:before, [glyph~="chat"]:before { + content: "\e246" +} + +[class*="glyphicon"].male:before, [glyph~="male"]:before { + content: "\e247" +} + +[class*="glyphicon"].female:before, [glyph~="female"]:before { + content: "\e248" +} + +[class*="glyphicon"].asterisk:before, [glyph~="asterisk"]:before { + content: "\002A" +} + +[class*="glyphicon"].divide:before, [glyph~="divide"]:before { + content: "\00F7" +} + +[class*="glyphicon"].snorkel-diving:before { + content: "\e251" +} + +[class*="glyphicon"].scuba-diving:before { + content: "\e252" +} + +[class*="glyphicon"].oxygen-bottle:before { + content: "\e253" +} + +[class*="glyphicon"].fins:before, [glyph~="fins"]:before { + content: "\e254" +} + +[class*="glyphicon"].fishes:before, [glyph~="fishes"]:before { + content: "\e255" +} + +[class*="glyphicon"].boat:before, [glyph~="boat"]:before { + content: "\e256" +} + +[class*="glyphicon"].delete:before, [glyph~="delete"]:before { + content: "\e257" +} + +[class*="glyphicon"].sheriffs-star:before { + content: "\e258" +} + +[class*="glyphicon"].qrcode:before, [glyph~="qrcode"]:before { + content: "\e259" +} + +[class*="glyphicon"].barcode:before, [glyph~="barcode"]:before { + content: "\e260" +} + +[class*="glyphicon"].pool:before, [glyph~="pool"]:before { + content: "\e261" +} + +[class*="glyphicon"].buoy:before, [glyph~="buoy"]:before { + content: "\e262" +} + +[class*="glyphicon"].spade:before, [glyph~="spade"]:before { + content: "\e263" +} + +[class*="glyphicon"].bank:before, [glyph~="bank"]:before { + content: "\1F3E6" +} + +[class*="glyphicon"].vcard:before, [glyph~="vcard"]:before { + content: "\e265" +} + +[class*="glyphicon"].electrical-plug:before { + content: "\e266" +} + +[class*="glyphicon"].flag:before, [glyph~="flag"]:before { + content: "\e267" +} + +[class*="glyphicon"].credit-card:before { + content: "\e268" +} + +[class*="glyphicon"].keyboard-wireless:before { + content: "\e269" +} + +[class*="glyphicon"].keyboard-wired:before { + content: "\e270" +} + +[class*="glyphicon"].shield:before, [glyph~="shield"]:before { + content: "\e271" +} + +[class*="glyphicon"].ring:before, [glyph~="ring"]:before { + content: "\02DA" +} + +[class*="glyphicon"].cake:before, [glyph~="cake"]:before { + content: "\e273" +} + +[class*="glyphicon"].drink:before, [glyph~="drink"]:before { + content: "\e274" +} + +[class*="glyphicon"].beer:before, [glyph~="beer"]:before { + content: "\e275" +} + +[class*="glyphicon"].fast-food:before { + content: "\e276" +} + +[class*="glyphicon"].cutlery:before, [glyph~="cutlery"]:before { + content: "\e277" +} + +[class*="glyphicon"].pizza:before, [glyph~="pizza"]:before { + content: "\e278" +} + +[class*="glyphicon"].birthday-cake:before { + content: "\e279" +} + +[class*="glyphicon"].tablet:before, [glyph~="tablet"]:before { + content: "\e280" +} + +[class*="glyphicon"].settings:before, [glyph~="settings"]:before { + content: "\e281" +} + +[class*="glyphicon"].bullets:before, [glyph~="bullets"]:before { + content: "\e282" +} + +[class*="glyphicon"].cardio:before, [glyph~="cardio"]:before { + content: "\e283" +} + +[class*="glyphicon"].t-shirt:before { + content: "\e284" +} + +[class*="glyphicon"].pants:before, [glyph~="pants"]:before { + content: "\e285" +} + +[class*="glyphicon"].sweater:before, [glyph~="sweater"]:before { + content: "\e286" +} + +[class*="glyphicon"].fabric:before, [glyph~="fabric"]:before { + content: "\e287" +} + +[class*="glyphicon"].leather:before, [glyph~="leather"]:before { + content: "\e288" +} + +[class*="glyphicon"].scissors:before, [glyph~="scissors"]:before { + content: "\e289" +} + +[class*="glyphicon"].bomb:before, [glyph~="bomb"]:before { + content: "\1F4A3" +} + +[class*="glyphicon"].skull:before, [glyph~="skull"]:before { + content: "\1F480" +} + +[class*="glyphicon"].celebration:before, [glyph~="celebration"]:before { + content: "\e292" +} + +[class*="glyphicon"].tea-kettle:before { + content: "\e293" +} + +[class*="glyphicon"].french-press:before { + content: "\e294" +} + +[class*="glyphicon"].coffe-cup:before { + content: "\e295" +} + +[class*="glyphicon"].pot:before, [glyph~="pot"]:before { + content: "\e296" +} + +[class*="glyphicon"].grater:before, [glyph~="grater"]:before { + content: "\e297" +} + +[class*="glyphicon"].kettle:before, [glyph~="kettle"]:before { + content: "\e298" +} + +[class*="glyphicon"].hospital:before, [glyph~="hospital"]:before { + content: "\1F3E5" +} + +[class*="glyphicon"].hospital-h:before { + content: "\e300" +} + +[class*="glyphicon"].microphone:before, [glyph~="microphone"]:before { + content: "\1F3A4" +} + +[class*="glyphicon"].webcam:before, [glyph~="webcam"]:before { + content: "\e302" +} + +[class*="glyphicon"].temple-christianity-church:before { + content: "\e303" +} + +[class*="glyphicon"].temple-islam:before { + content: "\e304" +} + +[class*="glyphicon"].temple-hindu:before { + content: "\e305" +} + +[class*="glyphicon"].temple-buddhist:before { + content: "\e306" +} + +[class*="glyphicon"].bicycle:before, [glyph~="bicycle"]:before { + content: "\1F6B2" +} + +[class*="glyphicon"].life-preserver:before { + content: "\e308" +} + +[class*="glyphicon"].share-alt:before { + content: "\e309" +} + +[class*="glyphicon"].comments:before, [glyph~="comments"]:before { + content: "\e310" +} + +[class*="glyphicon"].flower:before, [glyph~="flower"]:before { + content: "\2698" +} + +[class*="glyphicon"].baseball:before, [glyph~="baseball"]:before { + content: "\26BE" +} + +[class*="glyphicon"].rugby:before, [glyph~="rugby"]:before { + content: "\e313" +} + +[class*="glyphicon"].ax:before, [glyph~="ax"]:before { + content: "\e314" +} + +[class*="glyphicon"].table-tennis:before { + content: "\e315" +} + +[class*="glyphicon"].bowling:before, [glyph~="bowling"]:before { + content: "\1F3B3" +} + +[class*="glyphicon"].tree-conifer:before { + content: "\e317" +} + +[class*="glyphicon"].tree-deciduous:before { + content: "\e318" +} + +[class*="glyphicon"].more-items:before { + content: "\e319" +} + +[class*="glyphicon"].sort:before, [glyph~="sort"]:before { + content: "\e320" +} + +[class*="glyphicon"].filter:before, [glyph~="filter"]:before { + content: "\e321" +} + +[class*="glyphicon"].gamepad:before, [glyph~="gamepad"]:before { + content: "\e322" +} + +[class*="glyphicon"].playing-dices:before { + content: "\e323" +} + +[class*="glyphicon"].calculator:before, [glyph~="calculator"]:before { + content: "\e324" +} + +[class*="glyphicon"].tie:before, [glyph~="tie"]:before { + content: "\e325" +} + +[class*="glyphicon"].wallet:before, [glyph~="wallet"]:before { + content: "\e326" +} + +[class*="glyphicon"].piano:before, [glyph~="piano"]:before { + content: "\e327" +} + +[class*="glyphicon"].sampler:before, [glyph~="sampler"]:before { + content: "\e328" +} + +[class*="glyphicon"].podium:before, [glyph~="podium"]:before { + content: "\e329" +} + +[class*="glyphicon"].soccer-ball:before { + content: "\e330" +} + +[class*="glyphicon"].blog:before, [glyph~="blog"]:before { + content: "\e331" +} + +[class*="glyphicon"].dashboard:before, [glyph~="dashboard"]:before { + content: "\e332" +} + +[class*="glyphicon"].certificate:before, [glyph~="certificate"]:before { + content: "\e333" +} + +[class*="glyphicon"].bell:before, [glyph~="bell"]:before { + content: "\1F514" +} + +[class*="glyphicon"].candle:before, [glyph~="candle"]:before { + content: "\e335" +} + +[class*="glyphicon"].pushpin:before, [glyph~="pushpin"]:before { + content: "\1F4CC" +} + +[class*="glyphicon"].iphone-shake:before { + content: "\e337" +} + +[class*="glyphicon"].pin-flag:before { + content: "\e338" +} + +[class*="glyphicon"].turtle:before, [glyph~="turtle"]:before { + content: "\1F422" +} + +[class*="glyphicon"].rabbit:before, [glyph~="rabbit"]:before { + content: "\1F407" +} + +[class*="glyphicon"].globe:before, [glyph~="globe"]:before { + content: "\e341" +} + +[class*="glyphicon"].briefcase:before, [glyph~="briefcase"]:before { + content: "\1F4BC" +} + +[class*="glyphicon"].hdd:before, [glyph~="hdd"]:before { + content: "\e343" +} + +[class*="glyphicon"].thumbs-up:before { + content: "\e344" +} + +[class*="glyphicon"].thumbs-down:before { + content: "\e345" +} + +[class*="glyphicon"].hand-right:before { + content: "\e346" +} + +[class*="glyphicon"].hand-left:before { + content: "\e347" +} + +[class*="glyphicon"].hand-up:before { + content: "\e348" +} + +[class*="glyphicon"].hand-down:before { + content: "\e349" +} + +[class*="glyphicon"].fullscreen:before, [glyph~="fullscreen"]:before { + content: "\e350" +} + +[class*="glyphicon"].shopping-bag:before { + content: "\e351" +} + +[class*="glyphicon"].book-open:before { + content: "\e352" +} + +[class*="glyphicon"].nameplate:before, [glyph~="nameplate"]:before { + content: "\e353" +} + +[class*="glyphicon"].nameplate-alt:before { + content: "\e354" +} + +[class*="glyphicon"].vases:before, [glyph~="vases"]:before { + content: "\e355" +} + +[class*="glyphicon"].bullhorn:before, [glyph~="bullhorn"]:before { + content: "\e356" +} + +[class*="glyphicon"].dumbbell:before, [glyph~="dumbbell"]:before { + content: "\e357" +} + +[class*="glyphicon"].suitcase:before, [glyph~="suitcase"]:before { + content: "\e358" +} + +[class*="glyphicon"].file-import:before { + content: "\e359" +} + +[class*="glyphicon"].file-export:before { + content: "\e360" +} + +[class*="glyphicon"].bug:before, [glyph~="bug"]:before { + content: "\1F41B" +} + +[class*="glyphicon"].crown:before, [glyph~="crown"]:before { + content: "\1F451" +} + +[class*="glyphicon"].smoking:before, [glyph~="smoking"]:before { + content: "\e363" +} + +[class*="glyphicon"].cloud-upload:before { + content: "\e364" +} + +[class*="glyphicon"].cloud-download:before { + content: "\e365" +} + +[class*="glyphicon"].restart:before, [glyph~="restart"]:before { + content: "\e366" +} + +[class*="glyphicon"].security-camera:before { + content: "\e367" +} + +[class*="glyphicon"].expand:before, [glyph~="expand"]:before { + content: "\e368" +} + +[class*="glyphicon"].collapse:before, [glyph~="collapse"]:before { + content: "\e369" +} + +[class*="glyphicon"].collapse-top:before { + content: "\e370" +} + +[class*="glyphicon"].globe-af:before { + content: "\e371" +} + +[class*="glyphicon"].global:before, [glyph~="global"]:before { + content: "\e372" +} + +[class*="glyphicon"].spray:before, [glyph~="spray"]:before { + content: "\e373" +} + +[class*="glyphicon"].nails:before, [glyph~="nails"]:before { + content: "\e374" +} + +[class*="glyphicon"].claw-hammer:before { + content: "\e375" +} + +[class*="glyphicon"].classic-hammer:before { + content: "\e376" +} + +[class*="glyphicon"].hand-saw:before { + content: "\e377" +} + +[class*="glyphicon"].riflescope:before, [glyph~="riflescope"]:before { + content: "\e378" +} + +[class*="glyphicon"].electrical-socket-eu:before { + content: "\e379" +} + +[class*="glyphicon"].electrical-socket-us:before { + content: "\e380" +} + +[class*="glyphicon"].message-forward:before { + content: "\e381" +} + +[class*="glyphicon"].coat-hanger:before { + content: "\e382" +} + +[class*="glyphicon"].dress:before, [glyph~="dress"]:before { + content: "\1F457" +} + +[class*="glyphicon"].bathrobe:before, [glyph~="bathrobe"]:before { + content: "\e384" +} + +[class*="glyphicon"].shirt:before, [glyph~="shirt"]:before { + content: "\e385" +} + +[class*="glyphicon"].underwear:before, [glyph~="underwear"]:before { + content: "\e386" +} + +[class*="glyphicon"].log-in:before { + content: "\e387" +} + +[class*="glyphicon"].log-out:before { + content: "\e388" +} + +[class*="glyphicon"].exit:before, [glyph~="exit"]:before { + content: "\e389" +} + +[class*="glyphicon"].new-window-alt:before { + content: "\e390" +} + +[class*="glyphicon"].video-sd:before { + content: "\e391" +} + +[class*="glyphicon"].video-hd:before { + content: "\e392" +} + +[class*="glyphicon"].subtitles:before, [glyph~="subtitles"]:before { + content: "\e393" +} + +[class*="glyphicon"].sound-stereo:before { + content: "\e394" +} + +[class*="glyphicon"].sound-dolby:before { + content: "\e395" +} + +[class*="glyphicon"].sound-5-1:before { + content: "\e396" +} + +[class*="glyphicon"].sound-6-1:before { + content: "\e397" +} + +[class*="glyphicon"].sound-7-1:before { + content: "\e398" +} + +[class*="glyphicon"].copyright-mark:before { + content: "\e399" +} + +[class*="glyphicon"].registration-mark:before { + content: "\e400" +} + +[class*="glyphicon"].radar:before, [glyph~="radar"]:before { + content: "\e401" +} + +[class*="glyphicon"].skateboard:before, [glyph~="skateboard"]:before { + content: "\e402" +} + +[class*="glyphicon"].golf-course:before { + content: "\e403" +} + +[class*="glyphicon"].sorting:before, [glyph~="sorting"]:before { + content: "\e404" +} + +[class*="glyphicon"].sort-by-alphabet:before { + content: "\e405" +} + +[class*="glyphicon"].sort-by-alphabet-alt:before { + content: "\e406" +} + +[class*="glyphicon"].sort-by-order:before { + content: "\e407" +} + +[class*="glyphicon"].sort-by-order-alt:before { + content: "\e408" +} + +[class*="glyphicon"].sort-by-attributes:before { + content: "\e409" +} + +[class*="glyphicon"].sort-by-attributes-alt:before { + content: "\e410" +} + +[class*="glyphicon"].compressed:before, [glyph~="compressed"]:before { + content: "\e411" +} + +[class*="glyphicon"].package:before, [glyph~="package"]:before { + content: "\1F4E6" +} + +[class*="glyphicon"].cloud-plus:before { + content: "\e413" +} + +[class*="glyphicon"].cloud-minus:before { + content: "\e414" +} + +[class*="glyphicon"].disk-save:before { + content: "\e415" +} + +[class*="glyphicon"].disk-open:before { + content: "\e416" +} + +[class*="glyphicon"].disk-saved:before { + content: "\e417" +} + +[class*="glyphicon"].disk-remove:before { + content: "\e418" +} + +[class*="glyphicon"].disk-import:before { + content: "\e419" +} + +[class*="glyphicon"].disk-export:before { + content: "\e420" +} + +[class*="glyphicon"].tower:before, [glyph~="tower"]:before { + content: "\e421" +} + +[class*="glyphicon"].send:before, [glyph~="send"]:before { + content: "\e422" +} + +[class*="glyphicon"].git-branch:before { + content: "\e423" +} + +[class*="glyphicon"].git-create:before { + content: "\e424" +} + +[class*="glyphicon"].git-private:before { + content: "\e425" +} + +[class*="glyphicon"].git-delete:before { + content: "\e426" +} + +[class*="glyphicon"].git-merge:before { + content: "\e427" +} + +[class*="glyphicon"].git-pull-request:before { + content: "\e428" +} + +[class*="glyphicon"].git-compare:before { + content: "\e429" +} + +[class*="glyphicon"].git-commit:before { + content: "\e430" +} + +[class*="glyphicon"].construction-cone:before { + content: "\e431" +} + +[class*="glyphicon"].shoe-steps:before { + content: "\e432" +} + +[class*="glyphicon"].plus:before, [glyph~="plus"]:before { + content: "\002B" +} + +[class*="glyphicon"].minus:before, [glyph~="minus"]:before { + content: "\2212" +} + +[class*="glyphicon"].redo:before, [glyph~="redo"]:before { + content: "\e435" +} + +[class*="glyphicon"].undo:before, [glyph~="undo"]:before { + content: "\e436" +} + +[class*="glyphicon"].golf:before, [glyph~="golf"]:before { + content: "\e437" +} + +[class*="glyphicon"].hockey:before, [glyph~="hockey"]:before { + content: "\e438" +} + +[class*="glyphicon"].pipe:before, [glyph~="pipe"]:before { + content: "\e439" +} + +[class*="glyphicon"].wrench:before, [glyph~="wrench"]:before { + content: "\1F527" +} + +[class*="glyphicon"].folder-closed:before { + content: "\e441" +} + +[class*="glyphicon"].phone-alt:before { + content: "\e442" +} + +[class*="glyphicon"].earphone:before, [glyph~="earphone"]:before { + content: "\e443" +} + +[class*="glyphicon"].floppy-disk:before { + content: "\e444" +} + +[class*="glyphicon"].floppy-saved:before { + content: "\e445" +} + +[class*="glyphicon"].floppy-remove:before { + content: "\e446" +} + +[class*="glyphicon"].floppy-save:before { + content: "\e447" +} + +[class*="glyphicon"].floppy-open:before { + content: "\e448" +} + +[class*="glyphicon"].translate:before, [glyph~="translate"]:before { + content: "\e449" +} + +[class*="glyphicon"].fax:before, [glyph~="fax"]:before { + content: "\e450" +} + +[class*="glyphicon"].factory:before, [glyph~="factory"]:before { + content: "\1F3ED" +} + +[class*="glyphicon"].shop-window:before { + content: "\e452" +} + +[class*="glyphicon"].shop:before, [glyph~="shop"]:before { + content: "\e453" +} + +[class*="glyphicon"].kiosk:before, [glyph~="kiosk"]:before { + content: "\e454" +} + +[class*="glyphicon"].kiosk-wheels:before { + content: "\e455" +} + +[class*="glyphicon"].kiosk-light:before { + content: "\e456" +} + +[class*="glyphicon"].kiosk-food:before { + content: "\e457" +} + +[class*="glyphicon"].transfer:before, [glyph~="transfer"]:before { + content: "\e458" +} + +[class*="glyphicon"].money:before, [glyph~="money"]:before { + content: "\e459" +} + +[class*="glyphicon"].header:before, [glyph~="header"]:before { + content: "\e460" +} + +[class*="glyphicon"].blacksmith:before, [glyph~="blacksmith"]:before { + content: "\e461" +} + +[class*="glyphicon"].saw-blade:before { + content: "\e462" +} + +[class*="glyphicon"].basketball:before, [glyph~="basketball"]:before { + content: "\e463" +} + +[class*="glyphicon"].server:before, [glyph~="server"]:before { + content: "\e464" +} + +[class*="glyphicon"].server-plus:before { + content: "\e465" +} + +[class*="glyphicon"].server-minus:before { + content: "\e466" +} + +[class*="glyphicon"].server-ban:before { + content: "\e467" +} + +[class*="glyphicon"].server-flag:before { + content: "\e468" +} + +[class*="glyphicon"].server-lock:before { + content: "\e469" +} + +[class*="glyphicon"].server-new:before { + content: "\e470" +} + +.glyphicon-social.pinterest:before, [glyph~="social pinterest"]:before { + content: "\e001" +} + +.glyphicon-social.dropbox:before, [glyph~="social dropbox"]:before { + content: "\e002" +} + +.glyphicon-social.google_plus:before, [glyph~="social google_plus"]:before { + content: "\e003" +} + +.glyphicon-social.jolicloud:before, [glyph~="social jolicloud"]:before { + content: "\e004" +} + +.glyphicon-social.yahoo:before, [glyph~="social yahoo"]:before { + content: "\e005" +} + +.glyphicon-social.blogger:before, [glyph~="social blogger"]:before { + content: "\e006" +} + +.glyphicon-social.picasa:before, [glyph~="social picasa"]:before { + content: "\e007" +} + +.glyphicon-social.amazon:before, [glyph~="social amazon"]:before { + content: "\e008" +} + +.glyphicon-social.tumblr:before, [glyph~="social tumblr"]:before { + content: "\e009" +} + +.glyphicon-social.wordpress:before, [glyph~="social wordpress"]:before { + content: "\e010" +} + +.glyphicon-social.instapaper:before, [glyph~="social instapaper"]:before { + content: "\e011" +} + +.glyphicon-social.evernote:before, [glyph~="social evernote"]:before { + content: "\e012" +} + +.glyphicon-social.xing:before, [glyph~="social xing"]:before { + content: "\e013" +} + +.glyphicon-social.zootool:before, [glyph~="social zootool"]:before { + content: "\e014" +} + +.glyphicon-social.dribbble:before, [glyph~="social dribbble"]:before { + content: "\e015" +} + +.glyphicon-social.deviantart:before, [glyph~="social deviantart"]:before { + content: "\e016" +} + +.glyphicon-social.read_it_later:before, [glyph~="social read_it_later"]:before { + content: "\e017" +} + +.glyphicon-social.linked_in:before, [glyph~="social linked_in"]:before { + content: "\e018" +} + +.glyphicon-social.forrst:before, [glyph~="social forrst"]:before { + content: "\e019" +} + +.glyphicon-social.pinboard:before, [glyph~="social pinboard"]:before { + content: "\e020" +} + +.glyphicon-social.behance:before, [glyph~="social behance"]:before { + content: "\e021" +} + +.glyphicon-social.github:before, [glyph~="social github"]:before { + content: "\e022" +} + +.glyphicon-social.youtube:before, [glyph~="social youtube"]:before { + content: "\e023" +} + +.glyphicon-social.skitch:before, [glyph~="social skitch"]:before { + content: "\e024" +} + +.glyphicon-social.foursquare:before, [glyph~="social foursquare"]:before { + content: "\e025" +} + +.glyphicon-social.quora:before, [glyph~="social quora"]:before { + content: "\e026" +} + +.glyphicon-social.badoo:before, [glyph~="social badoo"]:before { + content: "\e027" +} + +.glyphicon-social.spotify:before, [glyph~="social spotify"]:before { + content: "\e028" +} + +.glyphicon-social.stumbleupon:before, [glyph~="social stumbleupon"]:before { + content: "\e029" +} + +.glyphicon-social.readability:before, [glyph~="social readability"]:before { + content: "\e030" +} + +.glyphicon-social.facebook:before, [glyph~="social facebook"]:before { + content: "\e031" +} + +.glyphicon-social.twitter:before, [glyph~="social twitter"]:before { + content: "\e032" +} + +.glyphicon-social.instagram:before, [glyph~="social instagram"]:before { + content: "\e033" +} + +.glyphicon-social.posterous_spaces:before, [glyph~="social posterous_spaces"]:before { + content: "\e034" +} + +.glyphicon-social.vimeo:before, [glyph~="social vimeo"]:before { + content: "\e035" +} + +.glyphicon-social.flickr:before, [glyph~="social flickr"]:before { + content: "\e036" +} + +.glyphicon-social.last_fm:before, [glyph~="social last_fm"]:before { + content: "\e037" +} + +.glyphicon-social.rss:before, [glyph~="social rss"]:before { + content: "\e038" +} + +.glyphicon-social.skype:before, [glyph~="social skype"]:before { + content: "\e039" +} + +.glyphicon-social.e-mail:before { + content: "\e040" +} + +.glyphicon-social.vine:before, [glyph~="social vine"]:before { + content: "\e041" +} + +.glyphicon-social.myspace:before, [glyph~="social myspace"]:before { + content: "\e042" +} + +.glyphicon-social.goodreads:before, [glyph~="social goodreads"]:before { + content: "\e043" +} + +.glyphicon-social.apple:before, [glyph~="social apple"]:before { + content: "\F8FF" +} + +.glyphicon-social.windows:before, [glyph~="social windows"]:before { + content: "\e045" +} + +.glyphicon-social.yelp:before, [glyph~="social yelp"]:before { + content: "\e046" +} + +.glyphicon-social.playstation:before, [glyph~="social playstation"]:before { + content: "\e047" +} + +.glyphicon-social.xbox:before, [glyph~="social xbox"]:before { + content: "\e048" +} + +.glyphicon-social.android:before, [glyph~="social android"]:before { + content: "\e049" +} + +.glyphicon-social.ios:before, [glyph~="social ios"]:before { + content: "\e050" +} diff --git a/win/sites/google/index.css b/win/sites/google/index.css index e69de29..b800ac7 100644 --- a/win/sites/google/index.css +++ b/win/sites/google/index.css @@ -0,0 +1,68 @@ +.wtp-w { + display: inline-block; + margin: 8px 0 0 4px; + transition: all 0.2s!important; +} + +.wtp-w, .web-to-plex-minion { + color: #ffffff!important; +} + +.web-to-plex-minion { + background: rgba(0,0,0,0)!important; +} + +.wtp-b { + background-color: #e5a00d!important; + line-height: 36px; + border-radius: 4px; + border: 1px; + font-size: 14px; + height: 36px; + padding: 0 20px; + box-shadow: 0 1px 0 rgba(0,0,0,0.05); + box-sizing: border-box; + cursor: pointer; + display: inline-block; + font-style: normal; + font-weight: 500; + min-width: 40px; + position: relative; + text-align: center; + text-decoration: none; + white-space: no-wrap; + vertical-align: middle; +} + +.wtp-b:hover { + background-color: #fbc022!important; + box-shadow: inset 0 -2px 0 rgba(0,0,0,0.27); +} + +.wtp--download .wtp-b { + background-color: var(--blue)!important; +} + +.wtp--download .wtp-b:hover { + background-color: var(--light-blue)!important; +} + +.wtp--found .wtp-b { + background-color: var(--orange)!important; +} + +.wtp--found .wtp-b:hover { + background-color: var(--light-orange)!important; +} + +.wtp-b:empty { + display: none!important; +} + +#tt--0-0 { + color: #666!important; +} + +#tt--0-0:hover { + color: #888!important; +} diff --git a/win/sites/hulu/index.css b/win/sites/hulu/index.css index e69de29..f75c93d 100644 --- a/win/sites/hulu/index.css +++ b/win/sites/hulu/index.css @@ -0,0 +1,37 @@ +.web-to-plex-minion { + background: 0 !important; + border: 0 !important; + display: inline-block !important; + color: #e6e6e6 !important; + font-size: 12px !important; + height: 20px !important; + text-decoration: none !important; + user-select: none !important; + vertical-align: top !important; + margin-top: 35%!important; + outline: #0000!important; +} + +.web-to-plex-minion.wtp--download { + color: #f45a26 !important; +} + +.web-to-plex-minion.wtp--download:hover { + color: #f67e56 !important; +} + +.web-to-plex-minion.wtp--found { + color: #e5a00d !important; +} + +.web-to-plex-minion.wtp--found:hover { + color: #f9be03 !important; +} + +#tt--0-0 { + color: #666 !important; +} + +#tt--0-0:hover { + color: #888 !important; +} diff --git a/win/sites/imdb/index.css b/win/sites/imdb/index.css index e69de29..48c3858 100644 --- a/win/sites/imdb/index.css +++ b/win/sites/imdb/index.css @@ -0,0 +1,78 @@ +.web-to-plex-minion { + background: #727272; + border-radius: 1000px; + color: #fff!important; + display: inline-block; + text-decoration: none!important; + text-transform: uppercase; + + margin-top: 10px; + padding: 5px 10px; +} + +.web-to-plex-minion.wtp--queued { + background: var(--light-blue)!important; +} + +.web-to-plex-minion.wtp--queued:hover { + background: var(--blue)!important; +} + +.web-to-plex-minion.wtp--download { + background: var(--dark-blue)!important; +} + +.web-to-plex-minion.wtp--download:hover { + background: var(--blue)!important; +} + +.web-to-plex-minion.wtp--found { + background: var(--orange)!important; +} + +.web-to-plex-minion.wtp--found:hover { + background: var(--light-orange)!important; +} + +.lister-list .web-to-plex-minion { + background: 0!important; + border: 0!important; + border-bottom: 5px solid #727272!important; + border-radius: 0!important; + font-size: 10px; + + margin-top: 0; + padding: 4px 6px; +} + +.lister-list .web-to-plex-minion.wtp--queued { + border-color: var(--light-blue)!important; +} + +.lister-list .web-to-plex-minion.wtp--queued:hover { + border-color: var(--blue)!important; +} + +.lister-list .web-to-plex-minion.wtp--download { + border-color: var(--dark-blue)!important; +} + +.lister-list .web-to-plex-minion.wtp--download:hover { + border-color: var(--blue)!important; +} + +.lister-list .web-to-plex-minion.wtp--found { + border-color: var(--orange)!important; +} + +.lister-list .web-to-plex-minion.wtp--found:hover { + border-color: var(--light-orange)!important; +} + +#tt--0-0 { + background: var(--grey)!important; +} + +#tt--0-0:hover { + background: var(--light-grey)!important; +} diff --git a/win/sites/itunes/index.css b/win/sites/itunes/index.css index e69de29..1e33870 100644 --- a/win/sites/itunes/index.css +++ b/win/sites/itunes/index.css @@ -0,0 +1,9 @@ +.web-to-plex-minion { + border-color: var(--orange) !important; + color: var(--orange) !important; +} + +.web-to-plex-minion:hover { + border-color: var(--light-orange) !important; + color: var(--light-orange) !important; +} diff --git a/win/sites/letterboxd/index.css b/win/sites/letterboxd/index.css index e69de29..bfe692d 100644 --- a/win/sites/letterboxd/index.css +++ b/win/sites/letterboxd/index.css @@ -0,0 +1,10 @@ +.web-to-plex-minion { + display: inline-block; + vertical-align: middle; + font-size: 12px; + line-height: 1.66666667; +} + +.web-to-plex-minion:hover { + color: #f67e56!important; +} diff --git a/win/sites/movieo/index.css b/win/sites/movieo/index.css index e69de29..7638814 100644 --- a/win/sites/movieo/index.css +++ b/win/sites/movieo/index.css @@ -0,0 +1,35 @@ +.mid-top-actions .share-box { + padding-left: 20px!important; +} + +.web-to-plex-minion { + margin-right: 8px; + color: #566273!important; +} + +.web-to-plex-minion.wtp--download { + border-color: #f45a26!important; + color: #acb4bf!important; +} + +.web-to-plex-minion.wtp--download:hover { + border-color: #f67e56!important; +} + +.web-to-plex-minion.wtp--found { + border-color: #ca7c1f!important; + color: #acb4bf!important; +} + +.web-to-plex-minion.wtp--found:hover { + border-color: #f8c022!important; +} + +#tt--0-0 { + background: #666!important; +} + +#tt--0-0:hover { + background: #888!important; + border-color: #888!important; +} diff --git a/win/sites/rottentomatoes/index.css b/win/sites/rottentomatoes/index.css index e69de29..a757498 100644 --- a/win/sites/rottentomatoes/index.css +++ b/win/sites/rottentomatoes/index.css @@ -0,0 +1,43 @@ +.mid-top-actions .share-box { + padding-left: 20px!important; +} + +.web-to-plex-minion { + border: 1px solid #f3f3f3; + border-radius: 22px; + color: #fff!important; + font: inherit; + font-family: 'Franklin Gothic FS Med', Arila, Helvetica, Tahoma, Century, Verdana, sans-serif; + font-size: 16px; + font-weight: 400; + visibility: visible; + + margin-right: 8px; + + height: 44px; +} + +.web-to-plex-minion.wtp--download { + background-color: #f45a26!important; +} + +.web-to-plex-minion.wtp--download:hover { + background-color: #f67e56!important; +} + +.web-to-plex-minion.wtp--found { + background-color: #ca7c1f!important; +} + +.web-to-plex-minion.wtp--found:hover { + background-color: #f8c022!important; +} + +#tt--0-0 { + background: #666!important; +} + +#tt--0-0:hover { + background: #888!important; + border-color: #888!important; +} diff --git a/win/sites/theme.css b/win/sites/theme.css index 75130c8..9aeffa3 100644 --- a/win/sites/theme.css +++ b/win/sites/theme.css @@ -49,8 +49,8 @@ BUTTON [style].web-to-plex-button... ::after */ -/* Button location */ -.web-to-plex-button.button-location-right { +/* Button location (Circular) */ +.web-to-plex-button.button-location-right:not(.button-shape-box) { left: unset !important; right: 5px !important; } @@ -68,7 +68,72 @@ BUTTON [style].web-to-plex-button... opacity: 0.10 !important; } -/* Button shape */ -.web-to-plex-button.button-shape-box { - /* ... */ +/* Button shape (Box) */ +.web-to-plex-button.button-shape-box.animate::before { + border-radius: 75px !important; +} + +.web-to-plex-button.button-shape-box, #plexit-bookmarklet-frame ~ .web-to-plex-button.button-shape-box { + border-radius: 0 !important; + border-bottom-left-radius: 2px !important; + border-bottom-right-radius: 2px !important; + + bottom: 0 !important; + margin-left: calc(50% - 34.5px) !important; + margin-top: -3px !important; + padding: 0 !important; + top: 0 !important; + + height: 20px !important; + width: 75px !important; +} + +.web-to-plex-button.button-shape-box .list-action img { + height: 16px !important; + width: 16px !important; +} + +.web-to-plex-button.button-shape-box:not(:hover) { + margin-top: -20px !important; +} + +.web-to-plex-button.button-shape-box::after { + border: 0 !important; + border-radius: 0 !important; + border-bottom: inherit !important; + + top: 100% !important; + + height: 2px !important; + width: 100% !important; +} + +.web-to-plex-button.button-shape-box:not(:hover)::after { + height: 6px !important; +} + +.web-to-plex-button.button-shape-box #wtp-list-name { + padding: 0 !important; + margin-top: 0 !important; + + width: 75px !important; +} + +.web-to-plex-button.button-shape-box #wtp-list-name img[src$="16.png"] { + height: 12px !important; +} + +.web-to-plex-button.button-shape-box:not(:hover) #wtp-list-name ~ * { + display: none !important; +} + +.web-to-plex-button.button-shape-box.open li:hover [tooltip]::before, .web-to-plex-button.button-shape-box.open [tooltip]::before { + background: #000c !important; + + left: -115px !important; + top: 30px !important; + z-index: 999999999 !important; + + height: fit-content !important; + width: 300px !important; } diff --git a/win/sites/tmdb/index.css b/win/sites/tmdb/index.css index e69de29..f6e5cef 100644 --- a/win/sites/tmdb/index.css +++ b/win/sites/tmdb/index.css @@ -0,0 +1,34 @@ +.web-to-plex-minion:hover, .web-to-plex-minion.wtp--downloader:hover, .web-to-plex-minion.wtp--found:hover { + cursor: pointer!important; + transition: background 0.2s, border 0.2s; +} + +.web-to-plex-minion, .web-to-plex-minion.wtp--downloader { + border-color: #f45a26!important; + background: #f45a26!important; + color: #fff!important; +} + +.web-to-plex-minion.wtp--downloader:hover { + border-color: #f67e56!important; + background: #f67e56!important; +} + +.web-to-plex-minion.wtp--found { + background: #e5a00d!important; + border-color: #e5a00d!important; + color: #fff!important; +} + +.web-to-plex-minion.wtp--found:hover { + background: #f9be03!important; +} + +#tt--0-0 { + background: #666!important; +} + +#tt--0-0:hover { + background: #888!important; + border-color: #888!important; +} diff --git a/win/sites/trakt/index.css b/win/sites/trakt/index.css index e69de29..5c11d0f 100644 --- a/win/sites/trakt/index.css +++ b/win/sites/trakt/index.css @@ -0,0 +1,22 @@ +.web-to-plex-minion { + background-color: #f45a26!important; + border-color: #f45a26!important; + color: #fff!important; +} + +.web-to-plex-minion:hover { + background-color: #d43a06!important; +} + +.wtp-min.under-info[title]::after { + content: attr(title); +} + +#tt--0-0 { + color: #666!important; + text-decoration: line-through!important; +} + +#tt--0-0:hover { + color: #888!important; +} diff --git a/win/sites/tvmaze/index.css b/win/sites/tvmaze/index.css index e69de29..d503832 100644 --- a/win/sites/tvmaze/index.css +++ b/win/sites/tvmaze/index.css @@ -0,0 +1,27 @@ +.web-to-plex-minion { + background: #727272; + color: #fff!important; + text-decoration: none!important; + text-transform: none!important; +} + +.web-to-plex-minion.wtp--download { + background: #f45a26!important; +} + +.web-to-plex-wrapper:hover > .web-to-plex-minion.wtp--download, .web-to-plex-minion.wtp--download:hover { + background-color: #f67e56!important; + color: #ffffff!important; +} + +.web-to-plex-wrapper:hover > .web-to-plex-minion.wtp--found, .web-to-plex-minion.wtp--found:hover { + background-color: #f8c022!important; +} + +#tt--0-0 { + background: #666!important; +} + +#tt--0-0:hover { + background: #888!important; +} diff --git a/win/sites/verizon/index.css b/win/sites/verizon/index.css index e69de29..a789420 100644 --- a/win/sites/verizon/index.css +++ b/win/sites/verizon/index.css @@ -0,0 +1,34 @@ +.web-to-plex-minion { + border-color: #f45a26!important; + background: #f45a26!important; + color: #fff; +} + +.web-to-plex-minion.wtp--download { + border-color: #f45a26!important; + background: #f45a26!important; + color: #fff!important; +} + +.web-to-plex-minion.wtp--download:hover { + background: #f67e56!important; +} + +.web-to-plex-minion.wtp--found { + background: #e5a00d!important; + border-color: #e5a00d!important; + color: #fff!important; +} + +.web-to-plex-minion.wtp--found:hover { + background: #f9be03!important; +} + +#tt--0-0 { + background: #666!important; +} + +#tt--0-0:hover { + background: #888!important; + border-color: #888!important; +} diff --git a/win/sites/vrv/index.css b/win/sites/vrv/index.css index e69de29..95d2961 100644 --- a/win/sites/vrv/index.css +++ b/win/sites/vrv/index.css @@ -0,0 +1,57 @@ +.web-to-plex-minion:nth-child(2) { + margin-left: 0.625rem; +} + +.web-to-plex-minion { + box-shadow: inset 0 0 0 0.125rem var(--orange)!important; + color: var(--orange)!important; +} + +.web-to-plex-minion:hover { + background-color: var(--orange)!important; + color: var(--light-white)!important; +} + +.web-to-plex-minion.wtp--found { + box-shadow: inset 0 0 0 0.125rem var(--orange)!important; + color: var(--orange)!important; +} + +.web-to-plex-minion.wtp--found:hover { + background-color: var(--orange)!important; + color: var(--light-white)!important; +} + +.web-to-plex-minion.wtp--download { + box-shadow: inset 0 0 0 0.125rem var(--blue)!important; + color: var(--blue)!important; +} + +.web-to-plex-minion.wtp--download:hover { + background-color: var(--blue)!important; + color: var(--light-white)!important; +} + +.web-to-plex-minion.wtp--error { + box-shadow: inset 0 0 0 0.125rem var(--red)!important; + color: var(--red)!important; +} + +.web-to-plex-minion.wtp--error:hover { + background-color: var(--red)!important; + color: var(--light-white)!important; +} + +.watchlist-card .web-to-plex-minion { + background: 0!important; + box-shadow: none!important; +} + +#tt--0-0 { + box-shadow: inset 0 0 0 0.125rem var(--grey)!important; + color: var(--grey)!important; +} + +#tt--0-0:hover { + background-color: var(--grey)!important; +} diff --git a/win/sites/vudu/index.css b/win/sites/vudu/index.css index e69de29..8af438d 100644 --- a/win/sites/vudu/index.css +++ b/win/sites/vudu/index.css @@ -0,0 +1,59 @@ +.web-to-plex-minion { + color: #566273!important; + background: transparent; + border: 2px solid #566273; + height: 30px; + font-size: 15px; + line-height: 25px; + border-radius: 6px; + font-weight: 700; + text-align: center; + display: block; + outline: none; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + cursor: pointer; + text-decoration: none!important; + margin-top: 12px; +} + +.web-to-plex-minion:hover { + opacity: 0.7; +} + +[class*="web-to-plex-wrapper"] { + width: 33.333333%!important; +} + +*:not(:first-child) ~ [class*="web-to-plex-wrapper"] { + margin-top: 35px!important; +} + +.web-to-plex-minion.wtp--download { + border-color: #f45a26!important; + color: #f45a26!important; +} + +[class*="web-to-plex-wrapper"]:hover > .web-to-plex-minion.wtp--download { + border-color: #f67e56!important; + color: #f67e56!important; +} + +.web-to-plex-minion.wtp--found { + border-color: #ca7c1f!important; + color: #ca7c1f!important; +} + +[class*="web-to-plex-wrapper"]:hover > .web-to-plex-minion.wtp--found { + border-color: #f8c022!important; + color: #f8c022!important; +} + +#tt--0-0 { + border-color: #666!important;; +} + +#tt--0-0:hover { + border-color: #888!important; +} diff --git a/win/utils.js b/win/utils.js index 26697fa..4f2f878 100644 --- a/win/utils.js +++ b/win/utils.js @@ -1,9 +1,105 @@ /* eslint-disable no-unused-vars */ /* global configuration, init, Update, "Helpers" */ -let configuration, init, Update; +let configuration, init, Update, IMAGES, Glyphs = {}, + HELPERS_STORAGE = { + get(keys, callback = () => {}) { + let results; + + if(keys === null) { + return callback(configuration); + } else if(keys instanceof String) { + return callback(configuration[keys]); + } else if(keys instanceof Array) { + results = [...keys]; + + for(let key of keys) + result.push(configuration[key]); + return callback(results); + } else if(keys instanceof Object) { + results = { ...keys }; + + for(let key in keys) + results[key] = configuration[key]; + return callback(results); + } + }, + + set(keys, callback = () => {}) { + let results = {}; + + for(let key in keys) + configuration[key] = results[key] = keys[key]; + return callback(results); + }, + + remove(keys, callback = () => {}) { + if(keys === null) + for(let key in configuration) + delete configuration[key]; + else if(keys instanceof String) + delete configuration[key]; + else if(keys instanceof Array) + for(let key of keys) + delete configuration[key]; + else if(keys instanceof Object) + for(let key in keys) + delete configuration[key]; + + callback(); + } + }, + MINIONS = [], + addMinions = (...minions) => { + MINIONS = [...minions, ...MINIONS]; + + return { + stayUnique: status => { + MINIONS = MINIONS.map(minion => ((!!~minions.indexOf(minion)? minion.setAttribute('ignore-web-to-plex-updates', status): null), minion)) + } + } + }; + +class UUID { + constructor(length = 16, symbol = '-') { + let values = []; + + window.crypto.getRandomValues(new Uint32Array(length)).forEach(value => values.push(value.toString(36))); + + return values.join(symbol).replace(/^[^a-z]+/i, ''); + } + + static from(object, seed = 64, symbol = '-') { + let id = []; + + for(let key in object) { + let o = object[key]; + + if(o instanceof Array) + o = o.join(symbol); + else if(o instanceof Object) + o = Object.values(o).join(symbol); + else + o = o + ''; + + if(typeof(o) == 'string') + o = o + .toLowerCase() + .split('') + .reduce((a, b) => ((typeof(a) == 'number'? a: a.charCodeAt(0)) + b.charCodeAt(0)), seed) + .toString(36); + else + return + /* Error occurred */; + + id.push(o); + } -(async date => { + return id.join('').replace(/(\w{1,8})(\w{4})?(\w{4})?(\w{4})?(\w{12})?(\w+)?/, '$1-$2-$3-$4-$5:$6').replace(/\-+\:/g, ''); + } +} + +let INITIALIZE = (async date => { // default date items let YEAR = date.getFullYear(), @@ -14,7 +110,38 @@ let configuration, init, Update; RUNNING = false, // Other items /* Items that the user has already asked for */ - CAUGHT, COMPRESS; + CAUGHT, COMPRESS, + REFINED = {}; + + // update ALL minions + let updateMinions = (attributes) => { + let { title, href, text, hover, classes, event } = attributes; + + for(let minion of MINIONS.filter(minion => minion.getAttribute('ignore-web-to-plex-updates') != 'true')) { + classes.forEach(c => minion.classList.add(c)); + minion.setAttribute('title', hover); + minion.addEventListener('click', event? event: href? (() => top.open(href, '_top')): null); + } + }, + // update a single (ID) minion + updateMinion = (properties, status, options) => { + let minions; + + for(let property in properties) + if(!(minions = $(`.web-to-plex-minion[${ property }="${properties[property]}"], .web-to-plex-minion[${ property }id="${properties[property]}"], .web-to-plex-minion[${ property }-id="${properties[property]}"]`)).empty) + break; + + if(!minions || minions.empty) + return; + + minions.forEach(minion => status? minion.classList.add(`wtp--${status}`): ''); + + if(status == 'found' && options.key) + minions.forEach(minion => { + minion.setAttribute('href', Request_PlexURL(__CONFIG__.server.id, options.key)); + minion.setAttribute('title', `Watch "${options.title} (${options.year})" on Plex`); + }); + }; // simple helpers let extURL = url => chrome.extension.getURL(url), @@ -22,9 +149,10 @@ let configuration, init, Update; // DO NOT EXPOSE __CONFIG__, ALLOWED, PERMISS; - let IMG_URL = { + let IMG_URL = IMAGES = { 'nil': extURL('img/null.png'), 'icon_16': extURL('img/16.png'), + 'icon_32': extURL('img/32.png'), 'icon_48': extURL('img/48.png'), 'background': extURL('img/background.png'), 'hide_icon_16': extURL('img/hide.16.png'), @@ -34,6 +162,7 @@ let configuration, init, Update; 'close_icon_16': extURL('img/close.16.png'), 'close_icon_48': extURL('img/close.48.png'), 'icon_white_16': extURL('img/_16.png'), + 'icon_white_32': extURL('img/_32.png'), 'icon_white_48': extURL('img/_48.png'), 'plexit_icon_16': extURL('img/plexit.16.png'), 'plexit_icon_48': extURL('img/plexit.48.png'), @@ -46,6 +175,15 @@ let configuration, init, Update; 'settings_icon_48': extURL('img/settings.48.png'), }; + for(let glyph of "adjust,airplane,alarm,albums,amazon,anchor,android,apple,asterisk,ax,badoo,ban,bank,barcode,baseball,basketball,bathrobe,beer,behance,bell,bicycle,bin,binoculars,blacksmith,blog,blogger,bluetooth,boat,bold,bomb,book,bookmark,bowling,briefcase,brush,bug,building,bullets,bullhorn,buoy,bus,cake,calculator,calendar,camera,candle,car,cardio,cargo,cars,celebration,certificate,charts,chat,check,cleaning,clock,cloud,cogwheel,cogwheels,coins,collapse,comments,compass,compressed,conversation,crop,crown,cup,cutlery,dashboard,delete,deviantart,direction,dislikes,display,divide,dog,download,dress,dribbble,drink,dropbox,dumbbell,earphone,edit,eject,electricity,embed,envelope,euro,evernote,exit,expand,eyedropper,fabric,facebook,factory,fax,female,file,film,filter,fins,fire,fishes,flag,flash,flickr,flower,font,forrst,forward,foursquare,fullscreen,gamepad,gbp,gift,girl,github,glass,global,globe,golf,goodreads,google_plus,grater,group,hdd,header,headphones,headset,heart,heat,history,hockey,home,hospital,imac,inbox,instagram,instapaper,ios,ipad,iphone,ipod,italic,jolicloud,justify,kettle,keynote,keys,kiosk,last_fm,leaf,leather,lightbulb,link,linked_in,list,lock,luggage,macbook,magic,magnet,male,microphone,minus,money,moon,more,move,music,mute,myspace,nails,nameplate,note,notes,ok,package,pants,paperclip,parents,pause,pen,pencil,piano,picasa,picture,pin,pinboard,pinterest,pipe,pizza,play,playlist,playstation,plus,podium,pool,posterous_spaces,pot,power,print,projector,pushpin,qrcode,quora,rabbit,radar,random,read_it_later,readability,record,redo,refresh,remove,repeat,restart,retweet,rewind,riflescope,ring,road,roundabout,router,rss,rugby,ruller,sampler,scissors,screenshot,search,send,server,settings,share,shield,shirt,shop,signal,skateboard,skitch,skull,skype,smoking,snowflake,sort,sorting,spade,spotify,spray,star,stats,stop,stopwatch,stroller,stumbleupon,subtitles,suitcase,sun,sweater,table,tablet,tag,tags,tie,tint,tower,train,transfer,translate,truck,tumblr,turtle,twitter,umbrella,unchecked,underwear,undo,unlock,unshare,upload,usd,user,vases,vcard,vimeo,vine,wallet,webcam,wifi,windows,woman,wordpress,wrench,xbox,xing,yahoo,yelp,youtube,zootool".split(',')) + Object.defineProperty(Glyphs, glyph, { + get() { return document.furnish('i', { glyph }) }, + set(value) { return document.furnish('i', { glyph: value }) }, + + configurable: true, + enumerable: true, + }); + // the storage - priority to sync const UTILS_STORAGE = chrome.storage.sync || chrome.storage.local; @@ -64,7 +202,7 @@ let configuration, init, Update; UTILS_STORAGE.get(null, DISK => { if(chrome.runtime.lastError) - chrome.storage.local.get(null, LOAD); + UTILS_STORAGE.get(null, LOAD); else LOAD(DISK); }); @@ -302,7 +440,7 @@ let configuration, init, Update; ); let preX = document.queryBy('.web-to-plex-prompt').first, - movie = /^(m(?:ovies?)?|f(?:ilms?)?|c(?:inemas?)?)?$/i; + movie = /^(m(?:ovies?)?|f(?:ilms?)?|c(?:inemas?)?|theat[re]{2})?$/i; if(preX) return /* Ignore while another prompt is open, prevents double prompts */; @@ -329,7 +467,7 @@ let configuration, init, Update; header.innerText = 'Approve ' + counter.children.length + (counter.children.length == 1?' item': ' items'); }; - prompt = furnish('div.web-to-plex-prompt', {}, + prompt = furnish('div.web-to-plex-prompt', { type: prompt_type }, furnish('div.web-to-plex-prompt-body', { style: `background-image: url(${ IMG_URL.noise_background }), url(${ IMG_URL.background }); background-size: auto, cover;` }, // The prompt's title furnish('h1.web-to-plex-prompt-header', {}, 'Approve ' + array.length + (array.length == 1? ' item': ' items')), @@ -346,14 +484,14 @@ let configuration, init, Update; elements.push( furnish('li.web-to-plex-prompt-option.mutable', { value: index, innerHTML: `

${ index + 1 }. ${ title }${ year? ` (${ year })`: '' } \u2014 ${ type }

` }, - furnish('button.remove', { title: `Remove "${ title }"`, onmouseup: event => { remove(event.target.parentElement); event.target.remove() } }), + furnish('i[glyph=remove]', { title: `Remove "${ title }"`, onmouseup: event => { remove(event.target.parentElement); event.target.remove() } }), ( __CONFIG__.PromptQuality? - P_QUA = furnish('select.quality', { index, onchange: event => data[event.target.getAttribute('index')].quality = event.target.value }, ...profiles[/(movie|film|cinema)/i.test(type)?'movie':'show'].map(Q => furnish('option', { value: Q.id }, Q.name))): + P_QUA = furnish('select.quality', { index, onchange: event => data[event.target.getAttribute('index')].quality = event.target.value }, ...profiles[movie.test(type)?'movie':'show'].map(Q => furnish('option', { value: Q.id }, Q.name))): '' ),( __CONFIG__.PromptLocation? - P_LOC = furnish('select.location', { index, onchange: event => data[event.target.getAttribute('index')].location = event.target.value }, ...locations[/(movie|film|cinema)/i.test(type)?'movie':'show'].map(Q => furnish('option', { value: Q.id }, Q.path))): + P_LOC = furnish('select.location', { index, onchange: event => data[event.target.getAttribute('index')].location = event.target.value }, ...locations[movie.test(type)?'movie':'show'].map(Q => furnish('option', { value: Q.id }, Q.path))): '' ) ) @@ -374,7 +512,7 @@ let configuration, init, Update; furnish('input.web-to-plex-prompt-input[type=text]', { placeholder: 'Add an item (enter to add): Title (Year) Type / ID Type', title: 'Solo: A Star Wars Story (2018) movie / tt3778644 m', onkeydown: async event => { if(event.keyCode == 13) { let title, year, type, self = event.target, R = RegExp, - movie = /^(m(?:ovies?)?|f(?:ilms?)?|c(?:inemas?)?)/i, + movie = /^(m(?:ovies?)?|f(?:ilms?)?|c(?:inemas?)?|theat[re]{2})/i, Db, IMDbID, TMDbID, TVDbID, value = self.value; self.setAttribute('disabled', self.disabled = true); @@ -420,9 +558,9 @@ let configuration, init, Update; } } } }), - furnish('button.web-to-plex-prompt-decline', { onmouseup: event => { remove(true); callback([]) }, title: 'Close' }, '\u2718'), - furnish('button.web-to-plex-prompt-accept', { onmouseup: event => { remove(true); new Prompt(prompt_type, options, callback, container) }, title: 'Reset' }, '\u21BA'), - furnish('button.web-to-plex-prompt-accept', { onmouseup: event => { remove(true); callback(data.filter(value => value !== null && value !== undefined)) }, title: 'Continue' }, '\u2714') + furnish('button.web-to-plex-prompt-decline', { onmouseup: event => { remove(true); callback([]) }, title: 'Close' }, Glyphs.exit), + furnish('button.web-to-plex-prompt-accept', { onmouseup: event => { remove(true); new Prompt(prompt_type, options, callback, container) }, title: 'Reset' }, Glyphs.restart), + furnish('button.web-to-plex-prompt-accept', { onmouseup: event => { remove(true); callback(data.filter(value => value !== null && value !== undefined)) }, title: 'Continue' }, Glyphs.ok) ) ) ); @@ -448,7 +586,7 @@ let configuration, init, Update; header.innerText = `Correction ready...`; }; - prompt = furnish('div.web-to-plex-prompt', {}, + prompt = furnish('div.web-to-plex-prompt', { type: prompt_type }, furnish('div.web-to-plex-prompt-body', { style: `background-image: url(${ IMG_URL.noise_background }), url(${ IMG_URL.background }); background-size: auto, cover;` }, // The prompt's title furnish('h1.web-to-plex-prompt-header', {}, (array.length == 1? 'Correction ready...': `Choose a correction from ${array.length} items`)), @@ -477,16 +615,21 @@ let configuration, init, Update; captured.tvdb.push(v); elements.push( - furnish('li.web-to-plex-prompt-option.mutable', { value: index, innerHTML: `

${ index + 1 }. ${ title }${ year? ` (${ year })`: '' } \u2014 ${ type }

${ i? `${i}`: '/' } \u2014 ${ t? `${t}`: '/' } \u2014 ${ v? `${v}`: '/' }` }, - furnish('button.choose', { title: `Use "${ title } (${ year })"`, onmouseup: event => { - let element = event.target.parentElement, - children = [...element.parentElement.children].filter(e => e != element); + furnish('li.web-to-plex-prompt-option.mutable.choose', { + value: index, + innerHTML: `

${ index + 1 }. ${ title }${ year? ` (${ year })`: '' } \u2014 ${ type }

${ i? `${i}`: '/' } \u2014 ${ t? `${t}`: '/' } \u2014 ${ v? `${v}`: '/' }`, + onmouseup: event => { + let self = traverse(event.target, element => element.classList.contains('mutable')), + children = [...self.parentElement.children].filter(e => e != self); children.forEach(child => { + child.classList.remove('chosen'); remove(child); - element.parentElement.appendChild(child); + self.parentElement.appendChild(child); }); - element.classList.add('chosen'); - } }) + self.classList.add('chosen'); + } + }, + furnish('i[glyph=ok]', { title: `Use "${ title } (${ year })"` }) ) ); } @@ -545,9 +688,9 @@ let configuration, init, Update; } } } }), - furnish('button.web-to-plex-prompt-decline', { onmouseup: event => { remove(true); prompt.done = true; callback([]) }, title: 'Close' }, '\u2718'), - furnish('button.web-to-plex-prompt-accept', { onmouseup: event => { remove(true); prompt.done = false; new Prompt(prompt_type, options, callback, container) }, title: 'Reset' }, '\u21BA'), - furnish('button.web-to-plex-prompt-accept', { onmouseup: event => { remove(true); prompt.done = true; callback(data.filter(value => value !== null && value !== undefined)) }, title: 'Continue' }, '\u2714') + furnish('button.web-to-plex-prompt-decline', { onmouseup: event => { remove(true); prompt.done = true; callback([]) }, title: 'Close' }, Glyphs.exit), + furnish('button.web-to-plex-prompt-accept', { onmouseup: event => { remove(true); prompt.done = false; new Prompt(prompt_type, options, callback, container) }, title: 'Reset' }, Glyphs.restart), + furnish('button.web-to-plex-prompt-accept', { onmouseup: event => { remove(true); prompt.done = true; callback(data.filter(value => value !== null && value !== undefined)) }, title: 'Continue' }, Glyphs.ok) ) ) ); @@ -569,7 +712,7 @@ let configuration, init, Update; header.innerText = 'Approve ' + counter.children.length + (counter.children.length == 1?' item': ' items'); }; - prompt = furnish('div.web-to-plex-prompt', {}, + prompt = furnish('div.web-to-plex-prompt', { type: prompt_type }, furnish('div.web-to-plex-prompt-body', { style: `background-image: url(${ IMG_URL.noise_background }), url(${ IMG_URL.background }); background-size: auto, cover;` }, // The prompt's title furnish('h1.web-to-plex-prompt-header', {}, 'Approve ' + array.length + (array.length == 1? ' item': ' items')), @@ -586,21 +729,21 @@ let configuration, init, Update; elements.push( furnish('li.web-to-plex-prompt-option.mutable', { value: index, innerHTML: `

${ index + 1 } \u00b7 ${ title }${ year? ` (${ year })`: '' } \u2014 ${ type }

` }, - furnish('button.remove', { title: `Remove "${ title }"`, onmouseup: event => { remove(event.target.parentElement); event.target.remove() } }), + furnish('i[glyph=remove]', { title: `Remove "${ title }"`, onmouseup: event => { remove(event.target.parentElement); event.target.remove() } }), ( __CONFIG__.PromptQuality? - P_QUA = furnish('select.quality', { index, onchange: event => data[event.target.getAttribute('index')].quality = event.target.value }, ...profiles[/(movie|film|cinema)/i.test(type)?'movie':'show'].map(Q => furnish('option', { value: Q.id }, Q.name))): + P_QUA = furnish('select.quality', { index, onchange: event => data[+event.target.getAttribute('index')].quality = event.target.value }, ...profiles[movie.test(type)?'movie':'show'].map(Q => furnish('option', { value: Q.id }, Q.name))): '' ),( __CONFIG__.PromptLocation? - P_LOC = furnish('select.location', { index, onchange: event => data[event.target.getAttribute('index')].location = event.target.value }, ...locations[/(movie|film|cinema)/i.test(type)?'movie':'show'].map(Q => furnish('option', { value: Q.id }, Q.path))): + P_LOC = furnish('select.location', { index, onchange: event => data[+event.target.getAttribute('index')].location = event.target.value }, ...locations[movie.test(type)?'movie':'show'].map(Q => furnish('option', { value: Q.id }, Q.path))): '' ) ) ); - if(P_QUA) P_QUA.value = defaults[type].quality; - if(P_LOC) P_LOC.value = defaults[type].location; + if(P_QUA) P_QUA.value = data[index].quality = defaults[type].quality; + if(P_LOC) P_LOC.value = data[index].location = defaults[type].location; P_QUA = P_LOC = null; } @@ -611,9 +754,9 @@ let configuration, init, Update; // The engagers furnish('div.web-to-plex-prompt-footer', {}, - furnish('button.web-to-plex-prompt-decline', { onmouseup: event => { remove(true); callback([]) }, title: 'Close' }, '\u2718'), - furnish('button.web-to-plex-prompt-accept', { onmouseup: event => { remove(true); new Prompt(prompt_type, options, callback, container) }, title: 'Reset' }, '\u21BA'), - furnish('button.web-to-plex-prompt-accept', { onmouseup: event => { remove(true); callback(data.filter(value => value !== null && value !== undefined)) }, title: 'Continue' }, '\u2714') + furnish('button.web-to-plex-prompt-decline', { onmouseup: event => { remove(true); callback([]) }, title: 'Close' }, Glyphs.exit), + furnish('button.web-to-plex-prompt-accept', { onmouseup: event => { remove(true); new Prompt(prompt_type, options, callback, container) }, title: 'Reset' }, Glyphs.restart), + furnish('button.web-to-plex-prompt-accept', { onmouseup: event => { remove(true); callback(data.filter(value => value)) }, title: 'Continue' }, Glyphs.ok) ) ) ); @@ -622,8 +765,15 @@ let configuration, init, Update; /* Allows the user to modify a single item (before being pushed) */ case 'modify': let { title, year, type, IMDbID, TMDbID, TVDbID } = options, + refined = { ...defaults[type], ...options }, + uuid = UUID.from({ type, title, IMDbID, TMDbID, TVDbID }), P_QUA, P_LOC; + if(REFINED[uuid]) + refined = REFINED[uuid]; + else + REFINED[uuid] = refined; + let i = IMDbID, t = TMDbID, v = TVDbID, @@ -644,34 +794,34 @@ let configuration, init, Update; element.remove(); }; - type = /(movie|film|cinema)/i.test(type)?'movie':'show'; + type = /(movie|film|cinema|theat[re]{2})s?/i.test(type)?'movie':'show'; - prompt = furnish('div.web-to-plex-prompt', {}, + prompt = furnish('div.web-to-plex-prompt', { type: prompt_type }, furnish('div.web-to-plex-prompt-body', { style: `background-image: url(${ IMG_URL.noise_background }), url(${ IMG_URL.background }); background-size: auto, cover;` }, // The prompt's title - furnish('h1.web-to-plex-prompt-header', { innerHTML: `${ title }${ year? ` (${ year })`: '' }` }), + furnish('h1.web-to-plex-prompt-header', { innerHTML: `${ title.length < 40? title: title.slice(0, 37) + '...' }${ parseInt(year)? ` (${ year })`: '' } \u2014 ${ movie.test(type)? 'Movie': 'TV Show' }` }), // The prompt's items furnish('div.web-to-plex-prompt-options', {}, - furnish('div.web-to-plex-prompt-option', { innerHTML: `${ type } \u2014 ${ i? `${i}`: '/' } \u2014 ${ t? `${t}`: '/' } \u2014 ${ v? `${v}`: '/' }` }), + furnish('div.web-to-plex-prompt-option', { innerHTML: `${ i? `${i}`: '?' } \u2014 ${ t? `${t}`: '?' } \u2014 ${ v? `${v}`: '?' }` }), ( __CONFIG__.PromptQuality? - P_QUA = furnish('select.quality', { onchange: event => options.quality = event.target.value }, ...profiles[type].map(Q => furnish('option', { value: Q.id }, Q.name))): + P_QUA = furnish('select.quality', { onchange: event => REFINED[uuid].quality = event.target.value }, ...profiles[type].map(Q => furnish('option', { value: Q.id }, Q.name))): '' ), furnish('br'), ( __CONFIG__.PromptLocation? - P_LOC = furnish('select.location', { onchange: event => options.location = event.target.value }, ...locations[type].map(Q => furnish('option', { value: Q.id }, Q.path))): + P_LOC = furnish('select.location', { onchange: event => REFINED[uuid].location = event.target.value }, ...locations[type].map(Q => furnish('option', { value: Q.id }, Q.path))): '' ) ), // The engagers furnish('div.web-to-plex-prompt-footer', {}, - furnish('button.web-to-plex-prompt-decline', { onmouseup: event => { remove(true); callback([]) }, title: 'Close' }, '\u2718'), - furnish('button.web-to-plex-prompt-accept', { onmouseup: event => { remove(true); new Prompt(prompt_type, options, callback, container) }, title: 'Reset' }, '\u21BA'), - furnish('button.web-to-plex-prompt-accept', { onmouseup: event => { remove(true); callback(options) }, title: 'Continue' }, '\u2714'), + furnish('button.web-to-plex-prompt-decline', { onmouseup: event => { remove(true); callback([]) }, title: 'Close' }, Glyphs.exit), + furnish('button.web-to-plex-prompt-accept', { onmouseup: event => { remove(true); new Prompt(prompt_type, REFINED[uuid], callback, container) }, title: 'Reset' }, Glyphs.restart), + furnish('button.web-to-plex-prompt-accept', { onmouseup: event => { remove(true); callback(REFINED[uuid]) }, title: 'Continue' }, Glyphs.ok), ( (!__CONFIG__.UseLowCache || (__CONFIG__.UseLowCache && CAUGHT.has({ imdb: i, tmdb: t, tvdb: v })))? furnish('button.web-to-plex-prompt-accept', { onmouseup: event => { let self = event.target; open(self.getAttribute('href'), '_blank') }, href: slugify(type), title: `Open on ${ manager[type] }` }, manager[type]): @@ -722,14 +872,14 @@ let configuration, init, Update; (init && !RUNNING? (init(), RUNNING = true): RUNNING = false); }; - prompt = furnish('div.web-to-plex-prompt', {}, + prompt = furnish('div.web-to-plex-prompt', { type: prompt_type }, furnish('div.web-to-plex-prompt-body', { style: `background-image: url(${ IMG_URL.noise_background }), url(${ IMG_URL.background }); background-size: auto, cover;` }, // The prompt's title furnish('h1.web-to-plex-prompt-header', { innerHTML: `"${ alias || name }" would like:` }), // The prompt's items furnish('div.web-to-plex-prompt-options', {}, - ...((permissions = permission.split(/\s*,\s*/).filter(v=>v&&v.length)).map( + ...((permissions = permission.split(/\s*,\s*/).filter((v,i,a)=>v&&v.length&&a.indexOf(v)==i)).map( __permission => furnish('div.web-to-plex-prompt-option.web-to-plex-permission', { innerHTML: `Access to your ${ __permission.replace(/(y)?s?$/, ($0, $1, $$, $_) => ($1? 'ies': 's')) } — ` + (p => { let R = RegExp, @@ -773,8 +923,8 @@ let configuration, init, Update; // The engagers furnish('div.web-to-plex-prompt-footer', {}, - furnish('button.web-to-plex-prompt-decline', { onmouseup: event => { if(!event.isTrusted) throw alert('The script for this site is trying to decline its own permissions!'), 'Malicious script. Decline permissions'; remove(true); callback(false, {}) }, title: 'Deny all permissions' }, '\u2718'), - furnish('button.web-to-plex-prompt-accept', { onmouseup: async event => { if(!event.isTrusted) throw alert('The script for this site is trying to grant its own permissions!'), 'Malicious script. Grant permissions'; remove(true); await callback(true, permissions); top.open(top.location.href, '_top'); }, title: 'Allow all permissions' }, '\u2714') + furnish('button.web-to-plex-prompt-decline', { onmouseup: event => { if(!event.isTrusted) throw alert('The script for this site is trying to decline its own permissions!'), 'Malicious script. Decline permissions'; remove(true); callback(false, {}) }, title: 'Deny all permissions' }, Glyphs.ban), + furnish('button.web-to-plex-prompt-accept', { onmouseup: async event => { if(!event.isTrusted) throw alert('The script for this site is trying to grant its own permissions!'), 'Malicious script. Grant permissions'; remove(true); await callback(true, permissions); top.open(top.location.href, '_top'); }, title: 'Allow all permissions' }, Glyphs.ok) ) ) ); @@ -806,7 +956,7 @@ let configuration, init, Update; src: url, style: ` display: none !important; - opacity: 0 !important; + opacity: 0 !important; visibility: hidden !important; `, @@ -824,6 +974,8 @@ let configuration, init, Update; // Send an update query to background.js Update = (type, options = {}, postToo) => { + Update.running = options.script || options.plugin || null; + if(configuration) console.log(`Requesting update (${ type } [post-to-top=${ !!postToo }])`, options); else if(!Update.retry) @@ -1006,7 +1158,7 @@ let configuration, init, Update; UTILS_STORAGE.get(null, options => { if(chrome.runtime.lastError) - chrome.storage.local.get(null, handleOptions); + UTILS_STORAGE.get(null, handleOptions); else handleOptions(options); }); @@ -1017,13 +1169,17 @@ let configuration, init, Update; async function ParsedOptions() { return await options() .then( - options => { + async options => { configuration = {}; + let { running } = Update, + allowed = await load(`has/${ running }`), + permiss = await load(`get/${ running }`); + /* Don't expose the user's authentication information to sites */ for(let key in options) if(/username|password|token|api|server|url|storage|cache|proxy|client|builtin|plugin|qualit/i.test(key)) - if(ALLOWED && RegExp(PERMISS.join('|'),'i').test(key)) + if(allowed && RegExp(permiss.join('|'),'i').test(key)) configuration[key] = options[key]; else /* Do nothing */; @@ -1439,6 +1595,8 @@ let configuration, init, Update; .replace(/@\{b(ase-?)?64-url\}/gi, btoa(URL)) .replace(/@\{enc(ode)?-url\}/gi, encodeURIComponent(URL)) .replace(/@\{(raw-)?url\}/gi, URL); + + headers[$1] = $2; } }); @@ -1464,7 +1622,7 @@ let configuration, init, Update; rqut = apit, // request type: tmdb, imdb, or tvdb manable = __CONFIG__.ManagerSearch && !(rerun & 0b1000), // is the user's "Manager Searches" option enabled? UTF_16 = /[^0\u0020-\u007e, 1\u00a1\u00bf-\u00ff, 2\u0100-\u017f, 3\u0180-\u024f, 4\u0300-\u036f, 5\u0370-\u03ff, 6\u0400-\u04ff, 7\u0500-\u052f, 8\u20a0-\u20bf]+/g, - MV = /^(movies?|films?|cinemas?)$/i.test(apit), + MV = /^(movies?|films?|cinemas?|theat[re]{2}s?)$/i.test(apit), TV = /^(tv[\s\-]*(?:shows?|series)?)$/i.test(apit); iid = iid == 'tt'? null: iid; @@ -1473,7 +1631,7 @@ let configuration, init, Update; rqut = /(tv|show|series)/i.test(rqut)? 'tvdb': - /(movie|film|cinema)s?/i.test(rqut)? + /(movie|film|cinema|theat[re])s?/i.test(rqut)? 'tmdb': rqut || '*'; manable = manable && (__CONFIG__.usingOmbi || (__CONFIG__.usingRadarr && rqut == 'tmdb') || ((__CONFIG__.usingSonarr || __CONFIG__.usingMedusa /*|| __CONFIG__.usingSickBeard*/) && rqut == 'tvdb')); @@ -1570,7 +1728,7 @@ let configuration, init, Update; cors = proxy.url, // if cors is requried and not uspported, proxy through this URL headers = HandleProxyHeaders(proxy.headers, url); - if(proxy.enabled && /(^http:\/\/)(?!localhost|127\.0\.0\.1(?:\/8)?|::1(?:\/128)?|:\d+)\b/i.test(url)) { + if(proxy.enabled && /(^https?:\/\/)(?!localhost|127\.0\.0\.1(?:\/8)?|::1(?:\/128)?|:\d+)\b/i.test(url)) { url = cors .replace(/\{b(ase-?)?64-url\}/gi, btoa(url)) .replace(/\{enc(ode)?-url\}/gi, encodeURIComponent(url)) @@ -2025,7 +2183,7 @@ let configuration, init, Update; ); } - let contentType = (/movies?|film/i.test(options.type)? 'movie': 'tv'); + let contentType = (/(movie|film|cinema|theat[re]{2})s?/i.test(options.type)? 'movie': 'tv'); chrome.runtime.sendMessage({ type: 'PUSH_OMBI', @@ -2156,10 +2314,12 @@ let configuration, init, Update; if(!prompted && (PromptQuality || PromptLocation)) return new Prompt('modify', options, refined => Request_Radarr(refined, true)); + let parsePath = id => JSON.parse(__CONFIG__.radarrStoragePaths).map(item => item.id == id? item: null).filter(n => n)[0].path.replace(/\\/g, '\\\\'); + if(PromptQuality && +options.quality > 0) PromptValues.QualityID = +options.quality; - if(PromptLocation && options.location) - PromptValues.StoragePath = JSON.parse(__CONFIG__.radarrStoragePaths).map(item => item.id == options.location? item: null).filter(n => n)[0].path.replace(/\\/g, '\\\\'); + if(PromptLocation && +options.location > 0) + PromptValues.StoragePath = parsePath(options.location); new Notification('info', `Sending "${ options.title }" to Radarr`, 3000); @@ -2167,7 +2327,7 @@ let configuration, init, Update; type: 'PUSH_RADARR', url: `${ __CONFIG__.radarrURL }api/movie/`, token: __CONFIG__.radarrToken, - StoragePath: __CONFIG__.radarrStoragePath, + StoragePath: parsePath(__CONFIG__.radarrStoragePath), QualityID: __CONFIG__.radarrQualityProfileId, basicAuth: __CONFIG__.radarrBasicAuth, title: options.title, @@ -2213,10 +2373,12 @@ let configuration, init, Update; if(!prompted && (PromptQuality || PromptLocation)) return new Prompt('modify', options, refined => Request_Sonarr(refined, true)); + let parsePath = id => JSON.parse(__CONFIG__.sonarrStoragePaths).map(item => item.id == id? item: null).filter(n => n)[0].path.replace(/\\/g, '\\\\'); + if(PromptQuality && +options.quality > 0) PromptValues.QualityID = +options.quality; - if(PromptLocation && options.location) - PromptValues.StoragePath = JSON.parse(__CONFIG__.sonarrStoragePaths).map(item => item.id == options.location? item: null).filter(n => n)[0].path.replace(/\\/g, '\\\\'); + if(PromptLocation && +options.location > 0) + PromptValues.StoragePath = parsePath(options.location); new Notification('info', `Sending "${ options.title }" to Sonarr`, 3000); @@ -2224,7 +2386,7 @@ let configuration, init, Update; type: 'PUSH_SONARR', url: `${ __CONFIG__.sonarrURL }api/series/`, token: __CONFIG__.sonarrToken, - StoragePath: __CONFIG__.sonarrStoragePath, + StoragePath: parsePath(__CONFIG__.sonarrStoragePath), QualityID: __CONFIG__.sonarrQualityProfileId, basicAuth: __CONFIG__.sonarrBasicAuth, title: options.title, @@ -2382,43 +2544,39 @@ let configuration, init, Update; else if(persistent && firstButton !== null && firstButton !== undefined) return firstButton; - let { __theme } = __CONFIG__; + let { __theme } = __CONFIG__; - let ThemeClasses = JSON.parse(COMPRESS? iBWT(unzip(decompress(__theme))): __theme), - HeaderClasses = [], - ParsedAttributes = {}; + __theme = JSON.parse(COMPRESS? iBWT(unzip(decompress(__theme))): __theme); - // Theme(s) - if(!ThemeClasses.length) - ThemeClasses = ''; - else - ThemeClasses = '.' + ThemeClasses.join('.'); - - ThemeClasses = ThemeClasses.split('.').filter(v => { - let R = RegExp; - - if(/([^=]+?)=([^.]+?)/.test(v)) { - ParsedAttributes[R.$1] = R.$2; + let ThemeClasses = [], + HeaderClasses = [], + ParsedAttributes = {}; - return false; - } + // Theme(s) + for(let theme in __theme) + if((typeof __theme[theme]) == 'boolean') + ThemeClasses.push(theme); + else + ParsedAttributes[theme] = __theme[theme]; - return true; - }).join('.'); + if(!ThemeClasses.length) + ThemeClasses = ''; + else + ThemeClasses = '.' + ThemeClasses.join('.'); - // Header(s) - for(let header in headers) - if(headers[header]) - HeaderClasses.push( header ); + // Header(s) + for(let header in headers) + if(headers[header]) + HeaderClasses.push( header ); - if(!HeaderClasses.length) - HeaderClasses = ''; - else - HeaderClasses = '.' + HeaderClasses.join('.'); + if(!HeaderClasses.length) + HeaderClasses = ''; + else + HeaderClasses = '.' + HeaderClasses.join('.'); //