├── LICENSE ├── README.md ├── about ├── ChromeWebStore_Badge_v2_496x150.png ├── demo.gif ├── icon.png ├── large-tile.png ├── marquee.png ├── screenshot.png └── tile.png └── src ├── Sortable.min.js ├── background.js ├── content-script.js ├── devtools.html ├── devtools.js ├── handler.svg ├── manifest.json ├── sidebar.html ├── sidebar.js └── styles.css /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Jaume Sanchez 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # css-filters-devtools-extension 2 | CSS Filters DevTools Extension 3 | 4 | ![](https://raw.githubusercontent.com/spite/css-filters-devtools-extension/master/about/demo.gif) 5 | 6 | An extension for Chrome DevTools that adds a sidebar to the Elements panel to easily edit and arrange CSS Filters. 7 | 8 | #### How to install #### 9 | 10 | [![npm](https://raw.githubusercontent.com/spite/css-filters-devtools-extension/master/about/ChromeWebStore_Badge_v2_496x150.png)](https://chrome.google.com/webstore/detail/jeidmdcnbpcnjmmeelolmhldaboickfn) 11 | 12 | [Install the extension from the Chrome Store](https://chrome.google.com/webstore/detail/jeidmdcnbpcnjmmeelolmhldaboickfn) 13 | 14 | #### Features #### 15 | 16 | Supported filters: 17 | - Blur 18 | - Grayscale 19 | - Brightness 20 | - Contrast 21 | - Sepia 22 | - Saturation 23 | - HueRotate 24 | - Invert 25 | - Opacity 26 | - DropShadow 27 | - Url 28 | 29 | Add and remove 30 | Edit with sliders and input boxes 31 | Drag and drop to arrange the filter stack 32 | 33 | #### TO DO #### 34 | 35 | Improve color selector in ``drop-shadow`` 36 | 37 | #### Credits #### 38 | 39 | Inspired on [Firefox's DevTools CSS filters editor](https://developer.mozilla.org/en-US/docs/Tools/Page_Inspector/How_to/Edit_CSS_filters) 40 | 41 | Drag and drop sort by [Sortable](http://rubaxa.github.io/Sortable/) 42 | 43 | RegExp foo developed with [regular expressions 101](https://regex101.com/) 44 | 45 | [MDN CSS Filter](https://developer.mozilla.org/en/docs/Web/CSS/filter) 46 | 47 | [Understanding CSS Filter Effects (HTML5Rocks)](http://www.html5rocks.com/en/tutorials/filters/understanding-css/) 48 | 49 | Tested in the amazing [CSSGram](http://una.im/CSSgram/) 50 | 51 | #### License #### 52 | 53 | MIT licensed 54 | 55 | Copyright (C) 2015 Jaume Sanchez Elias [@thespite](https://twitter.com/thespite), [clicktorelease.com](https://www.clicktorelease.com) 56 | -------------------------------------------------------------------------------- /about/ChromeWebStore_Badge_v2_496x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spite/css-filters-devtools-extension/5752807c9531378529e7fce150dfd3fca0c1806a/about/ChromeWebStore_Badge_v2_496x150.png -------------------------------------------------------------------------------- /about/demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spite/css-filters-devtools-extension/5752807c9531378529e7fce150dfd3fca0c1806a/about/demo.gif -------------------------------------------------------------------------------- /about/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spite/css-filters-devtools-extension/5752807c9531378529e7fce150dfd3fca0c1806a/about/icon.png -------------------------------------------------------------------------------- /about/large-tile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spite/css-filters-devtools-extension/5752807c9531378529e7fce150dfd3fca0c1806a/about/large-tile.png -------------------------------------------------------------------------------- /about/marquee.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spite/css-filters-devtools-extension/5752807c9531378529e7fce150dfd3fca0c1806a/about/marquee.png -------------------------------------------------------------------------------- /about/screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spite/css-filters-devtools-extension/5752807c9531378529e7fce150dfd3fca0c1806a/about/screenshot.png -------------------------------------------------------------------------------- /about/tile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spite/css-filters-devtools-extension/5752807c9531378529e7fce150dfd3fca0c1806a/about/tile.png -------------------------------------------------------------------------------- /src/Sortable.min.js: -------------------------------------------------------------------------------- 1 | /*! Sortable 1.4.2 - MIT | git://github.com/rubaxa/Sortable.git */ 2 | !function(a){"use strict";"function"==typeof define&&define.amd?define(a):"undefined"!=typeof module&&"undefined"!=typeof module.exports?module.exports=a():"undefined"!=typeof Package?Sortable=a():window.Sortable=a()}(function(){"use strict";function a(a,b){if(!a||!a.nodeType||1!==a.nodeType)throw"Sortable: `el` must be HTMLElement, and not "+{}.toString.call(a);this.el=a,this.options=b=r({},b),a[L]=this;var c={group:Math.random(),sort:!0,disabled:!1,store:null,handle:null,scroll:!0,scrollSensitivity:30,scrollSpeed:10,draggable:/[uo]l/i.test(a.nodeName)?"li":">*",ghostClass:"sortable-ghost",chosenClass:"sortable-chosen",ignore:"a, img",filter:null,animation:0,setData:function(a,b){a.setData("Text",b.textContent)},dropBubble:!1,dragoverBubble:!1,dataIdAttr:"data-id",delay:0,forceFallback:!1,fallbackClass:"sortable-fallback",fallbackOnBody:!1};for(var d in c)!(d in b)&&(b[d]=c[d]);V(b);for(var f in this)"_"===f.charAt(0)&&(this[f]=this[f].bind(this));this.nativeDraggable=b.forceFallback?!1:P,e(a,"mousedown",this._onTapStart),e(a,"touchstart",this._onTapStart),this.nativeDraggable&&(e(a,"dragover",this),e(a,"dragenter",this)),T.push(this._onDragOver),b.store&&this.sort(b.store.get(this))}function b(a){v&&v.state!==a&&(h(v,"display",a?"none":""),!a&&v.state&&w.insertBefore(v,s),v.state=a)}function c(a,b,c){if(a){c=c||N,b=b.split(".");var d=b.shift().toUpperCase(),e=new RegExp("\\s("+b.join("|")+")(?=\\s)","g");do if(">*"===d&&a.parentNode===c||(""===d||a.nodeName.toUpperCase()==d)&&(!b.length||((" "+a.className+" ").match(e)||[]).length==b.length))return a;while(a!==c&&(a=a.parentNode))}return null}function d(a){a.dataTransfer&&(a.dataTransfer.dropEffect="move"),a.preventDefault()}function e(a,b,c){a.addEventListener(b,c,!1)}function f(a,b,c){a.removeEventListener(b,c,!1)}function g(a,b,c){if(a)if(a.classList)a.classList[c?"add":"remove"](b);else{var d=(" "+a.className+" ").replace(K," ").replace(" "+b+" "," ");a.className=(d+(c?" "+b:"")).replace(K," ")}}function h(a,b,c){var d=a&&a.style;if(d){if(void 0===c)return N.defaultView&&N.defaultView.getComputedStyle?c=N.defaultView.getComputedStyle(a,""):a.currentStyle&&(c=a.currentStyle),void 0===b?c:c[b];b in d||(b="-webkit-"+b),d[b]=c+("string"==typeof c?"":"px")}}function i(a,b,c){if(a){var d=a.getElementsByTagName(b),e=0,f=d.length;if(c)for(;f>e;e++)c(d[e],e);return d}return[]}function j(a,b,c,d,e,f,g){var h=N.createEvent("Event"),i=(a||b[L]).options,j="on"+c.charAt(0).toUpperCase()+c.substr(1);h.initEvent(c,!0,!0),h.to=b,h.from=e||b,h.item=d||b,h.clone=v,h.oldIndex=f,h.newIndex=g,b.dispatchEvent(h),i[j]&&i[j].call(a,h)}function k(a,b,c,d,e,f){var g,h,i=a[L],j=i.options.onMove;return g=N.createEvent("Event"),g.initEvent("move",!0,!0),g.to=b,g.from=a,g.dragged=c,g.draggedRect=d,g.related=e||b,g.relatedRect=f||b.getBoundingClientRect(),a.dispatchEvent(g),j&&(h=j.call(i,g)),h}function l(a){a.draggable=!1}function m(){R=!1}function n(a,b){var c=a.lastElementChild,d=c.getBoundingClientRect();return(b.clientY-(d.top+d.height)>5||b.clientX-(d.right+d.width)>5)&&c}function o(a){for(var b=a.tagName+a.className+a.src+a.href+a.textContent,c=b.length,d=0;c--;)d+=b.charCodeAt(c);return d.toString(36)}function p(a){var b=0;if(!a||!a.parentNode)return-1;for(;a&&(a=a.previousElementSibling);)"TEMPLATE"!==a.nodeName.toUpperCase()&&b++;return b}function q(a,b){var c,d;return function(){void 0===c&&(c=arguments,d=this,setTimeout(function(){1===c.length?a.call(d,c[0]):a.apply(d,c),c=void 0},b))}}function r(a,b){if(a&&b)for(var c in b)b.hasOwnProperty(c)&&(a[c]=b[c]);return a}var s,t,u,v,w,x,y,z,A,B,C,D,E,F,G,H,I,J={},K=/\s+/g,L="Sortable"+(new Date).getTime(),M=window,N=M.document,O=M.parseInt,P=!!("draggable"in N.createElement("div")),Q=function(a){return a=N.createElement("x"),a.style.cssText="pointer-events:auto","auto"===a.style.pointerEvents}(),R=!1,S=Math.abs,T=([].slice,[]),U=q(function(a,b,c){if(c&&b.scroll){var d,e,f,g,h=b.scrollSensitivity,i=b.scrollSpeed,j=a.clientX,k=a.clientY,l=window.innerWidth,m=window.innerHeight;if(z!==c&&(y=b.scroll,z=c,y===!0)){y=c;do if(y.offsetWidth=l-j)-(h>=j),g=(h>=m-k)-(h>=k),(f||g)&&(d=M)),(J.vx!==f||J.vy!==g||J.el!==d)&&(J.el=d,J.vx=f,J.vy=g,clearInterval(J.pid),d&&(J.pid=setInterval(function(){d===M?M.scrollTo(M.pageXOffset+f*i,M.pageYOffset+g*i):(g&&(d.scrollTop+=g*i),f&&(d.scrollLeft+=f*i))},24)))}},30),V=function(a){var b=a.group;b&&"object"==typeof b||(b=a.group={name:b}),["pull","put"].forEach(function(a){a in b||(b[a]=!0)}),a.groups=" "+b.name+(b.put.join?" "+b.put.join(" "):"")+" "};return a.prototype={constructor:a,_onTapStart:function(a){var b=this,d=this.el,e=this.options,f=a.type,g=a.touches&&a.touches[0],h=(g||a).target,i=h,k=e.filter;if(!("mousedown"===f&&0!==a.button||e.disabled)&&(h=c(h,e.draggable,d))){if(D=p(h),"function"==typeof k){if(k.call(this,a,h,this))return j(b,i,"filter",h,d,D),void a.preventDefault()}else if(k&&(k=k.split(",").some(function(a){return a=c(i,a.trim(),d),a?(j(b,a,"filter",h,d,D),!0):void 0})))return void a.preventDefault();(!e.handle||c(i,e.handle,d))&&this._prepareDragStart(a,g,h)}},_prepareDragStart:function(a,b,c){var d,f=this,h=f.el,j=f.options,k=h.ownerDocument;c&&!s&&c.parentNode===h&&(G=a,w=h,s=c,t=s.parentNode,x=s.nextSibling,F=j.group,d=function(){f._disableDelayedDrag(),s.draggable=!0,g(s,f.options.chosenClass,!0),f._triggerDragStart(b)},j.ignore.split(",").forEach(function(a){i(s,a.trim(),l)}),e(k,"mouseup",f._onDrop),e(k,"touchend",f._onDrop),e(k,"touchcancel",f._onDrop),j.delay?(e(k,"mouseup",f._disableDelayedDrag),e(k,"touchend",f._disableDelayedDrag),e(k,"touchcancel",f._disableDelayedDrag),e(k,"mousemove",f._disableDelayedDrag),e(k,"touchmove",f._disableDelayedDrag),f._dragStartTimer=setTimeout(d,j.delay)):d())},_disableDelayedDrag:function(){var a=this.el.ownerDocument;clearTimeout(this._dragStartTimer),f(a,"mouseup",this._disableDelayedDrag),f(a,"touchend",this._disableDelayedDrag),f(a,"touchcancel",this._disableDelayedDrag),f(a,"mousemove",this._disableDelayedDrag),f(a,"touchmove",this._disableDelayedDrag)},_triggerDragStart:function(a){a?(G={target:s,clientX:a.clientX,clientY:a.clientY},this._onDragStart(G,"touch")):this.nativeDraggable?(e(s,"dragend",this),e(w,"dragstart",this._onDragStart)):this._onDragStart(G,!0);try{N.selection?N.selection.empty():window.getSelection().removeAllRanges()}catch(b){}},_dragStarted:function(){w&&s&&(g(s,this.options.ghostClass,!0),a.active=this,j(this,w,"start",s,w,D))},_emulateDragOver:function(){if(H){if(this._lastX===H.clientX&&this._lastY===H.clientY)return;this._lastX=H.clientX,this._lastY=H.clientY,Q||h(u,"display","none");var a=N.elementFromPoint(H.clientX,H.clientY),b=a,c=" "+this.options.group.name,d=T.length;if(b)do{if(b[L]&&b[L].options.groups.indexOf(c)>-1){for(;d--;)T[d]({clientX:H.clientX,clientY:H.clientY,target:a,rootEl:b});break}a=b}while(b=b.parentNode);Q||h(u,"display","")}},_onTouchMove:function(b){if(G){a.active||this._dragStarted(),this._appendGhost();var c=b.touches?b.touches[0]:b,d=c.clientX-G.clientX,e=c.clientY-G.clientY,f=b.touches?"translate3d("+d+"px,"+e+"px,0)":"translate("+d+"px,"+e+"px)";I=!0,H=c,h(u,"webkitTransform",f),h(u,"mozTransform",f),h(u,"msTransform",f),h(u,"transform",f),b.preventDefault()}},_appendGhost:function(){if(!u){var a,b=s.getBoundingClientRect(),c=h(s),d=this.options;u=s.cloneNode(!0),g(u,d.ghostClass,!1),g(u,d.fallbackClass,!0),h(u,"top",b.top-O(c.marginTop,10)),h(u,"left",b.left-O(c.marginLeft,10)),h(u,"width",b.width),h(u,"height",b.height),h(u,"opacity","0.8"),h(u,"position","fixed"),h(u,"zIndex","100000"),h(u,"pointerEvents","none"),d.fallbackOnBody&&N.body.appendChild(u)||w.appendChild(u),a=u.getBoundingClientRect(),h(u,"width",2*b.width-a.width),h(u,"height",2*b.height-a.height)}},_onDragStart:function(a,b){var c=a.dataTransfer,d=this.options;this._offUpEvents(),"clone"==F.pull&&(v=s.cloneNode(!0),h(v,"display","none"),w.insertBefore(v,s)),b?("touch"===b?(e(N,"touchmove",this._onTouchMove),e(N,"touchend",this._onDrop),e(N,"touchcancel",this._onDrop)):(e(N,"mousemove",this._onTouchMove),e(N,"mouseup",this._onDrop)),this._loopId=setInterval(this._emulateDragOver,50)):(c&&(c.effectAllowed="move",d.setData&&d.setData.call(this,c,s)),e(N,"drop",this),setTimeout(this._dragStarted,0))},_onDragOver:function(a){var d,e,f,g=this.el,i=this.options,j=i.group,l=j.put,o=F===j,p=i.sort;if(void 0!==a.preventDefault&&(a.preventDefault(),!i.dragoverBubble&&a.stopPropagation()),I=!0,F&&!i.disabled&&(o?p||(f=!w.contains(s)):F.pull&&l&&(F.name===j.name||l.indexOf&&~l.indexOf(F.name)))&&(void 0===a.rootEl||a.rootEl===this.el)){if(U(a,i,this.el),R)return;if(d=c(a.target,i.draggable,g),e=s.getBoundingClientRect(),f)return b(!0),void(v||x?w.insertBefore(s,v||x):p||w.appendChild(s));if(0===g.children.length||g.children[0]===u||g===a.target&&(d=n(g,a))){if(d){if(d.animated)return;r=d.getBoundingClientRect()}b(o),k(w,g,s,e,d,r)!==!1&&(s.contains(g)||(g.appendChild(s),t=g),this._animate(e,s),d&&this._animate(r,d))}else if(d&&!d.animated&&d!==s&&void 0!==d.parentNode[L]){A!==d&&(A=d,B=h(d),C=h(d.parentNode));var q,r=d.getBoundingClientRect(),y=r.right-r.left,z=r.bottom-r.top,D=/left|right|inline/.test(B.cssFloat+B.display)||"flex"==C.display&&0===C["flex-direction"].indexOf("row"),E=d.offsetWidth>s.offsetWidth,G=d.offsetHeight>s.offsetHeight,H=(D?(a.clientX-r.left)/y:(a.clientY-r.top)/z)>.5,J=d.nextElementSibling,K=k(w,g,s,e,d,r);if(K!==!1){if(R=!0,setTimeout(m,30),b(o),1===K||-1===K)q=1===K;else if(D){var M=s.offsetTop,N=d.offsetTop;q=M===N?d.previousElementSibling===s&&!E||H&&E:N>M}else q=J!==s&&!G||H&&G;s.contains(g)||(q&&!J?g.appendChild(s):d.parentNode.insertBefore(s,q?J:d)),t=s.parentNode,this._animate(e,s),this._animate(r,d)}}}},_animate:function(a,b){var c=this.options.animation;if(c){var d=b.getBoundingClientRect();h(b,"transition","none"),h(b,"transform","translate3d("+(a.left-d.left)+"px,"+(a.top-d.top)+"px,0)"),b.offsetWidth,h(b,"transition","all "+c+"ms"),h(b,"transform","translate3d(0,0,0)"),clearTimeout(b.animated),b.animated=setTimeout(function(){h(b,"transition",""),h(b,"transform",""),b.animated=!1},c)}},_offUpEvents:function(){var a=this.el.ownerDocument;f(N,"touchmove",this._onTouchMove),f(a,"mouseup",this._onDrop),f(a,"touchend",this._onDrop),f(a,"touchcancel",this._onDrop)},_onDrop:function(b){var c=this.el,d=this.options;clearInterval(this._loopId),clearInterval(J.pid),clearTimeout(this._dragStartTimer),f(N,"mousemove",this._onTouchMove),this.nativeDraggable&&(f(N,"drop",this),f(c,"dragstart",this._onDragStart)),this._offUpEvents(),b&&(I&&(b.preventDefault(),!d.dropBubble&&b.stopPropagation()),u&&u.parentNode.removeChild(u),s&&(this.nativeDraggable&&f(s,"dragend",this),l(s),g(s,this.options.ghostClass,!1),g(s,this.options.chosenClass,!1),w!==t?(E=p(s),E>=0&&(j(null,t,"sort",s,w,D,E),j(this,w,"sort",s,w,D,E),j(null,t,"add",s,w,D,E),j(this,w,"remove",s,w,D,E))):(v&&v.parentNode.removeChild(v),s.nextSibling!==x&&(E=p(s),E>=0&&(j(this,w,"update",s,w,D,E),j(this,w,"sort",s,w,D,E)))),a.active&&((null===E||-1===E)&&(E=D),j(this,w,"end",s,w,D,E),this.save())),w=s=t=u=x=v=y=z=G=H=I=E=A=B=F=a.active=null)},handleEvent:function(a){var b=a.type;"dragover"===b||"dragenter"===b?s&&(this._onDragOver(a),d(a)):("drop"===b||"dragend"===b)&&this._onDrop(a)},toArray:function(){for(var a,b=[],d=this.el.children,e=0,f=d.length,g=this.options;f>e;e++)a=d[e],c(a,g.draggable,this.el)&&b.push(a.getAttribute(g.dataIdAttr)||o(a));return b},sort:function(a){var b={},d=this.el;this.toArray().forEach(function(a,e){var f=d.children[e];c(f,this.options.draggable,d)&&(b[a]=f)},this),a.forEach(function(a){b[a]&&(d.removeChild(b[a]),d.appendChild(b[a]))})},save:function(){var a=this.options.store;a&&a.set(this)},closest:function(a,b){return c(a,b||this.options.draggable,this.el)},option:function(a,b){var c=this.options;return void 0===b?c[a]:(c[a]=b,void("group"===a&&V(c)))},destroy:function(){var a=this.el;a[L]=null,f(a,"mousedown",this._onTapStart),f(a,"touchstart",this._onTapStart),this.nativeDraggable&&(f(a,"dragover",this),f(a,"dragenter",this)),Array.prototype.forEach.call(a.querySelectorAll("[draggable]"),function(a){a.removeAttribute("draggable")}),T.splice(T.indexOf(this._onDragOver),1),this._onDrop(),this.el=a=null}},a.utils={on:e,off:f,css:h,find:i,is:function(a,b){return!!c(a,b,a)},extend:r,throttle:q,closest:c,toggleClass:g,index:p},a.create=function(b,c){return new a(b,c)},a.version="1.4.2",a}); -------------------------------------------------------------------------------- /src/background.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spite/css-filters-devtools-extension/5752807c9531378529e7fce150dfd3fca0c1806a/src/background.js -------------------------------------------------------------------------------- /src/content-script.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spite/css-filters-devtools-extension/5752807c9531378529e7fce150dfd3fca0c1806a/src/content-script.js -------------------------------------------------------------------------------- /src/devtools.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/devtools.js: -------------------------------------------------------------------------------- 1 | chrome.devtools.panels.elements.createSidebarPane("Filters", 2 | function(sidebar) { 3 | 4 | sidebar.setPage( 'sidebar.html' ); 5 | sidebar.setHeight( '100vh' ); 6 | sidebar.onShown.addListener( function() { 7 | } ); 8 | chrome.runtime.sendMessage({"message": "open_new_tab" }); 9 | 10 | }); -------------------------------------------------------------------------------- /src/handler.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "CSS Filters Editor", 3 | "short_name": "Filters", 4 | "version": "1.3", 5 | "manifest_version": 2, 6 | "minimum_chrome_version": "10.0", 7 | "devtools_page": "devtools.html", 8 | "permissions": [ 9 | "tabs", "activeTab", "" 10 | ] 11 | } -------------------------------------------------------------------------------- /src/sidebar.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |

No filters in selected node

13 | 14 | 15 | -------------------------------------------------------------------------------- /src/sidebar.js: -------------------------------------------------------------------------------- 1 | var registeredFilters = {}; 2 | var styleFilters = []; 3 | 4 | function registerFilter( id, filter ) { 5 | 6 | registeredFilters[ id ] = filter; 7 | 8 | } 9 | 10 | function interpretValue( value ) { 11 | 12 | value = value.toLowerCase(); 13 | 14 | var re = /([-]*[\d\.]+)(px|%)*/gmi 15 | var m; 16 | 17 | re.lastIndex = 0; 18 | while( ( m = re.exec( value ) ) !== null) { 19 | if( m.index === re.lastIndex ) { 20 | re.lastIndex++; 21 | } 22 | var v = parseFloat( m[ 1 ] ); 23 | if( m[ 2 ] === undefined ) return v * 100; 24 | if( m[ 2 ] === 'px' ) return v; 25 | if( m[ 2 ] === '%' ) return v 26 | } 27 | 28 | 29 | } 30 | 31 | // https://developer.mozilla.org/en/docs/Web/CSS/filter 32 | 33 | function FilterParam( name ) { 34 | 35 | this.name = name; 36 | this.value; 37 | this.input; 38 | 39 | } 40 | 41 | function RangeFilterParam( name, min, max, step, unit ) { 42 | 43 | FilterParam.apply( this, arguments ); 44 | this.min = min; 45 | this.max = max; 46 | this.step = step; 47 | this.units = unit; 48 | 49 | this.init(); 50 | 51 | } 52 | 53 | RangeFilterParam.prototype = Object.create( FilterParam.prototype ); 54 | 55 | RangeFilterParam.prototype.init = function() { 56 | 57 | var line = document.createElement( 'div' ); 58 | line.className = 'line'; 59 | 60 | var handle = document.createElement( 'div' ); 61 | handle.className = 'handle'; 62 | 63 | var n = document.createElement( 'h4' ); 64 | n.textContent = this.name; 65 | 66 | var label = document.createElement( 'input' ); 67 | label.type = 'text'; 68 | label.className = 'label'; 69 | 70 | label.addEventListener( 'change', function( e ) { 71 | this.setValue( label.value ); 72 | onValueUpdated(); 73 | }.bind( this ) ); 74 | label.addEventListener( 'keyup', function( e ) { 75 | this.setValue( label.value ); 76 | onValueUpdated(); 77 | }.bind( this ) ); 78 | 79 | var units = document.createElement( 'span' ); 80 | units.className = 'units'; 81 | units.textContent = this.units; 82 | 83 | var i = document.createElement( 'input' ); 84 | i.setAttribute( 'type', 'range' ); 85 | i.setAttribute( 'min', this.min ); 86 | i.setAttribute( 'max', this.max ); 87 | i.setAttribute( 'step', this.step ); 88 | 89 | i.value = this.value; 90 | 91 | i.addEventListener( 'change', function( e ) { 92 | this.setValue( i.value ); 93 | onValueUpdated(); 94 | }.bind( this ) ); 95 | i.addEventListener( 'input', function( e ) { 96 | this.setValue( i.value ); 97 | onValueUpdated(); 98 | }.bind( this ) ); 99 | 100 | line.appendChild( handle ); 101 | line.appendChild( n ); 102 | line.appendChild( i ); 103 | line.appendChild( label ); 104 | line.appendChild( units ); 105 | 106 | this.line = line; 107 | this.input = i; 108 | this.label = label; 109 | 110 | } 111 | 112 | RangeFilterParam.prototype.getWidget = function() { 113 | 114 | return this.line; 115 | 116 | } 117 | 118 | RangeFilterParam.prototype.setValue = function( v ) { 119 | 120 | this.value = v; 121 | this.input.value = v; 122 | this.input.style.backgroundSize = ( this.value - this.min ) * 100 / ( this.max - this.min ) + '% 100%'; 123 | this.label.value = this.value; 124 | 125 | } 126 | 127 | function TextFilterParam( name ) { 128 | 129 | FilterParam.apply( this, arguments ); 130 | 131 | this.init(); 132 | 133 | } 134 | 135 | TextFilterParam.prototype = Object.create( FilterParam.prototype ); 136 | 137 | TextFilterParam.prototype.init = function() { 138 | 139 | var line = document.createElement( 'div' ); 140 | line.className = 'line'; 141 | 142 | var handle = document.createElement( 'div' ); 143 | handle.className = 'handle'; 144 | 145 | var n = document.createElement( 'h4' ); 146 | n.textContent = this.name; 147 | 148 | var i = document.createElement( 'input' ); 149 | i.setAttribute( 'type', 'text' ); 150 | i.className = 'text'; 151 | 152 | i.value = this.value; 153 | 154 | i.addEventListener( 'change', function( e ) { 155 | this.setValue( i.value ); 156 | onValueUpdated(); 157 | }.bind( this ) ); 158 | i.addEventListener( 'input', function( e ) { 159 | this.setValue( i.value ); 160 | onValueUpdated(); 161 | }.bind( this ) ); 162 | 163 | line.appendChild( handle ); 164 | line.appendChild( n ); 165 | line.appendChild( i ); 166 | 167 | this.line = line; 168 | this.input = i; 169 | 170 | } 171 | 172 | TextFilterParam.prototype.getWidget = function() { 173 | 174 | return this.line; 175 | 176 | } 177 | 178 | TextFilterParam.prototype.setValue = function( v ) { 179 | 180 | this.value = v; 181 | this.input.value = v; 182 | 183 | } 184 | 185 | function Filter() { 186 | 187 | this.name = 'filter'; 188 | this.params = {}; 189 | this.paramsOrder = []; 190 | 191 | } 192 | 193 | Filter.prototype.getValue = function() { 194 | 195 | } 196 | 197 | Filter.prototype.addParam = function( param ) { 198 | 199 | this.params[ param.name ] = param; 200 | this.paramsOrder.push( param.name ); 201 | 202 | } 203 | 204 | Filter.prototype.composeUI = function() { 205 | 206 | var panel = document.createElement( 'li' ); 207 | panel.className = 'panel'; 208 | 209 | var handle = document.createElement( 'div' ); 210 | handle.className = 'handle handle-icon'; 211 | 212 | var h3 = document.createElement( 'h3' ); 213 | h3.textContent = this.name; 214 | var values = document.createElement( 'div' ); 215 | values.className = 'lines'; 216 | 217 | var remove = document.createElement( 'a' ); 218 | remove.className = 'remove'; 219 | remove.setAttribute( 'href', '#' ); 220 | remove.textContent = 'remove'; 221 | remove.addEventListener( 'click', function( e ) { 222 | styleFilters.forEach( function( f, n ) { 223 | if( f === this ) { 224 | //styleFilters.splice( n, 1 ); 225 | panel.parentElement.removeChild( panel ); 226 | onValueUpdated(); 227 | } 228 | }.bind( this ) ) 229 | }.bind( this ) ); 230 | 231 | this.paramsOrder.forEach( function( pname ) { 232 | var param = this.params[ pname ]; 233 | values.appendChild( param.getWidget() ); 234 | }.bind( this ) ); 235 | 236 | panel.appendChild( handle ); 237 | panel.appendChild( h3 ); 238 | panel.appendChild( remove ) 239 | panel.appendChild( values ); 240 | 241 | return panel; 242 | 243 | } 244 | 245 | function Blur() { 246 | 247 | Filter.call( this ); 248 | 249 | this.name = 'blur'; 250 | this.addParam( new RangeFilterParam( 'radius', 0, 100, .5, 'px' ) ) 251 | this.params.radius.setValue( 10 ); 252 | 253 | } 254 | 255 | Blur.prototype = Object.create( Filter.prototype ); 256 | 257 | Blur.prototype.parseValue = function( value ) { 258 | 259 | this.params.radius.setValue( parseFloat( value ) ); 260 | 261 | } 262 | 263 | Blur.prototype.getValue = function() { 264 | 265 | return 'blur(' + this.params.radius.value + 'px)'; 266 | 267 | } 268 | 269 | function Grayscale() { 270 | 271 | Filter.call( this ); 272 | 273 | this.name = 'grayscale'; 274 | this.addParam( new RangeFilterParam( 'weight', 0, 100, .1, '%' ) ) 275 | this.params.weight.setValue( 100 ); 276 | 277 | } 278 | 279 | Grayscale.prototype = Object.create( Filter.prototype ); 280 | 281 | Grayscale.prototype.parseValue = function( value ) { 282 | 283 | this.params.weight.setValue( interpretValue( value ) ); 284 | 285 | } 286 | 287 | Grayscale.prototype.getValue = function() { 288 | 289 | return 'grayscale(' + this.params.weight.value + '%)'; 290 | 291 | } 292 | 293 | function Brightness() { 294 | 295 | Filter.call( this ); 296 | 297 | this.name = 'brightness'; 298 | this.addParam( new RangeFilterParam( 'weight', 0, 1000, .1, '%' ) ) 299 | this.params.weight.setValue( 150 ); 300 | 301 | } 302 | 303 | Brightness.prototype = Object.create( Filter.prototype ); 304 | 305 | Brightness.prototype.parseValue = function( value ) { 306 | 307 | this.params.weight.setValue( interpretValue( value ) ); 308 | 309 | } 310 | 311 | Brightness.prototype.getValue = function() { 312 | 313 | return 'brightness(' + this.params.weight.value + '%)'; 314 | 315 | } 316 | 317 | function Contrast() { 318 | 319 | Filter.call( this ); 320 | 321 | this.name = 'contrast'; 322 | this.addParam( new RangeFilterParam( 'weight', 0, 1000, .1, '%' ) ) 323 | this.params.weight.setValue( 150 ); 324 | 325 | } 326 | 327 | Contrast.prototype = Object.create( Filter.prototype ); 328 | 329 | Contrast.prototype.parseValue = function( value ) { 330 | 331 | this.params.weight.setValue( interpretValue( value ) ); 332 | 333 | } 334 | 335 | Contrast.prototype.getValue = function() { 336 | 337 | return 'contrast(' + this.params.weight.value + '%)'; 338 | 339 | } 340 | 341 | function Sepia() { 342 | 343 | Filter.call( this ); 344 | 345 | this.name = 'sepia'; 346 | this.addParam( new RangeFilterParam( 'weight', 0, 100, .1, '%' ) ) 347 | this.params.weight.setValue( 100 ); 348 | 349 | } 350 | 351 | Sepia.prototype = Object.create( Filter.prototype ); 352 | 353 | Sepia.prototype.parseValue = function( value ) { 354 | 355 | this.params.weight.setValue( interpretValue( value ) ); 356 | 357 | } 358 | 359 | Sepia.prototype.getValue = function() { 360 | 361 | return 'sepia(' + this.params.weight.value + '%)'; 362 | 363 | } 364 | 365 | function Saturation() { 366 | 367 | Filter.call( this ); 368 | 369 | this.name = 'saturate'; 370 | this.addParam( new RangeFilterParam( 'weight', 0, 1000, .1, '%' ) ) 371 | this.params.weight.setValue( 200 ); 372 | 373 | } 374 | 375 | Saturation.prototype = Object.create( Filter.prototype ); 376 | 377 | Saturation.prototype.parseValue = function( value ) { 378 | 379 | this.params.weight.setValue( interpretValue( value ) ); 380 | 381 | } 382 | 383 | Saturation.prototype.getValue = function() { 384 | 385 | return 'saturate(' + this.params.weight.value + '%)'; 386 | 387 | } 388 | 389 | function HueRotate() { 390 | 391 | Filter.call( this ); 392 | 393 | this.name = 'hue-rotate'; 394 | this.addParam( new RangeFilterParam( 'angle', 0, 360, .5, '°' ) ) 395 | this.params.angle.setValue( 180 ); 396 | 397 | } 398 | 399 | HueRotate.prototype = Object.create( Filter.prototype ); 400 | 401 | HueRotate.prototype.parseValue = function( value ) { 402 | 403 | this.params.angle.setValue( parseFloat( value ) ); 404 | 405 | } 406 | 407 | HueRotate.prototype.getValue = function() { 408 | 409 | return 'hue-rotate(' + this.params.angle.value + 'deg)'; 410 | 411 | } 412 | 413 | function Invert() { 414 | 415 | Filter.call( this ); 416 | 417 | this.name = 'invert'; 418 | this.addParam( new RangeFilterParam( 'weight', 0, 100, .1, '%' ) ) 419 | this.params.weight.setValue( 100 ); 420 | 421 | } 422 | 423 | Invert.prototype = Object.create( Filter.prototype ); 424 | 425 | Invert.prototype.parseValue = function( value ) { 426 | 427 | this.params.weight.setValue( interpretValue( value ) ); 428 | 429 | } 430 | 431 | Invert.prototype.getValue = function() { 432 | 433 | return 'invert(' + this.params.weight.value + '%)'; 434 | 435 | } 436 | 437 | function Opacity() { 438 | 439 | Filter.call( this ); 440 | 441 | this.name = 'opacity'; 442 | this.addParam( new RangeFilterParam( 'weight', 0, 100, .1, '%' ) ) 443 | this.params.weight.setValue( 50 ); 444 | 445 | } 446 | 447 | Opacity.prototype = Object.create( Filter.prototype ); 448 | 449 | Opacity.prototype.parseValue = function( value ) { 450 | 451 | this.params.weight.setValue( interpretValue( value ) ); 452 | 453 | } 454 | 455 | Opacity.prototype.getValue = function() { 456 | 457 | return 'opacity(' + this.params.weight.value + '%)'; 458 | 459 | } 460 | 461 | function DropShadow() { 462 | 463 | Filter.call( this ); 464 | 465 | this.name = 'drop-shadow'; 466 | this.addParam( new RangeFilterParam( 'left', -100, 100, .5, 'px' ) ) 467 | this.addParam( new RangeFilterParam( 'top', -100, 100, .5, 'px' ) ) 468 | this.addParam( new RangeFilterParam( 'radius', 0, 100, .5, 'px' ) ) 469 | this.addParam( new TextFilterParam( 'color' ) ) 470 | 471 | this.params.left.setValue( 5 ); 472 | this.params.top.setValue( 5 ); 473 | this.params.radius.setValue( 10 ); 474 | this.params.color.setValue( '' ); 475 | 476 | } 477 | 478 | DropShadow.prototype = Object.create( Filter.prototype ); 479 | 480 | DropShadow.prototype.parseValue = function( value ) { 481 | 482 | var re = /(-*[\d]+)(px)*[\s]+(-*[\d]+)(px)*[\s]+(-*[\d]+)(px)*/gmi 483 | var m = re.exec( value ); 484 | 485 | this.params.left.setValue( parseFloat( m[ 1 ] ) ); 486 | this.params.top.setValue( parseFloat( m[ 3 ] ) ); 487 | this.params.radius.setValue( parseFloat( m[ 5 ] ) ); 488 | 489 | var color = value.replace( m[ 0 ], '' ) 490 | 491 | this.params.color.setValue( color ); 492 | 493 | } 494 | 495 | DropShadow.prototype.getValue = function() { 496 | 497 | return 'drop-shadow(' + this.params.left.value + 'px ' + this.params.top.value + 'px ' + this.params.radius.value + 'px ' + this.params.color.value + ')'; 498 | 499 | } 500 | 501 | function Url() { 502 | 503 | Filter.call( this ); 504 | 505 | this.name = 'url'; 506 | this.addParam( new TextFilterParam( 'url' ) ) 507 | 508 | this.params.url.setValue( '' ); 509 | 510 | } 511 | 512 | Url.prototype = Object.create( Filter.prototype ); 513 | 514 | Url.prototype.parseValue = function( value ) { 515 | 516 | var url = value.replace( /\'/gmi, '' ); 517 | this.params.url.setValue( url ); 518 | 519 | } 520 | 521 | Url.prototype.getValue = function() { 522 | 523 | return 'url(' + this.params.url.value + ')'; 524 | 525 | } 526 | 527 | registerFilter( 'blur', Blur ); 528 | registerFilter( 'grayscale', Grayscale ); 529 | registerFilter( 'brightness', Brightness ); 530 | registerFilter( 'contrast', Contrast ); 531 | registerFilter( 'sepia', Sepia ); 532 | registerFilter( 'saturate', Saturation ); 533 | registerFilter( 'hue-rotate', HueRotate ); 534 | registerFilter( 'invert', Invert ); 535 | registerFilter( 'opacity', Opacity ); 536 | registerFilter( 'drop-shadow', DropShadow ); 537 | registerFilter( 'url', Url ); 538 | 539 | var toolbar = null; 540 | var filtersPanel = null; 541 | var noFilters = null; 542 | var sort = null; 543 | 544 | function onValueUpdated() { 545 | 546 | var values = []; 547 | 548 | for( var j = 0; j < filtersPanel.children.length; j++ ) { 549 | var c = filtersPanel.children[ j ]; 550 | var n = c.getAttribute( 'data-filter' ); 551 | var f = styleFilters[ parseInt( n, 10 ) ]; 552 | values.push( f.getValue() ); 553 | } 554 | 555 | if( values.length === 0 ) { 556 | noFilters.style.display = 'block'; 557 | values.push( 'none' ); 558 | } else { 559 | noFilters.style.display = 'none'; 560 | } 561 | 562 | chrome.devtools.inspectedWindow.eval( '$0.style.webkitFilter="' + values.join( ' ' ) + '"' ); 563 | 564 | } 565 | 566 | window.addEventListener( 'load', onLoad ); 567 | 568 | function onLoad() { 569 | 570 | var links = document.querySelectorAll( 'a[rel=external]' ); 571 | for( var j = 0; j < links.length; j++ ) { 572 | var a = links[ j ]; 573 | a.addEventListener( 'click', function( e ) { 574 | window.open( this.href, '_blank' ); 575 | e.preventDefault(); 576 | }, false ); 577 | } 578 | 579 | filtersPanel = document.getElementById( 'filters-panel' ); 580 | noFilters = document.getElementById( 'no-filters' ); 581 | toolbar = document.getElementById( 'toolbar' ); 582 | 583 | for( var j in registeredFilters ) { 584 | 585 | var f = registeredFilters[ j ]; 586 | 587 | var li = document.createElement( 'li' ); 588 | 589 | var b = document.createElement( 'a' ); 590 | b.textContent = f.name; 591 | b.setAttribute( 'href', '#' ); 592 | li.appendChild( b ); 593 | toolbar.appendChild( li ); 594 | 595 | b.addEventListener( 'click', function( e ) { 596 | 597 | var filter = new registeredFilters[ this ](); 598 | var ui = filter.composeUI(); 599 | ui.setAttribute( 'data-filter', styleFilters.length ); 600 | filtersPanel.appendChild( ui ); 601 | styleFilters.push( filter ); 602 | onValueUpdated(); 603 | 604 | if( sort ) { 605 | sort.destroy(); 606 | sort = null; 607 | } 608 | 609 | sort = Sortable.create( filtersPanel, { 610 | animation: 150, 611 | handle: '.handle', 612 | onUpdate: onValueUpdated 613 | } ); 614 | 615 | }.bind( j ) ) 616 | 617 | } 618 | 619 | update(); 620 | 621 | } 622 | 623 | chrome.devtools.panels.elements.onSelectionChanged.addListener(function(){ 624 | 625 | update(); 626 | 627 | } ); 628 | 629 | function update() { 630 | 631 | chrome.devtools.inspectedWindow.eval( 'window.getComputedStyle( $0 ).getPropertyValue( "-webkit-filter" )', 632 | 633 | function( result, isException ) { 634 | 635 | var filters = processDeclaration( result ); 636 | 637 | if( sort ) { 638 | sort.destroy(); 639 | sort = null; 640 | } 641 | 642 | filtersPanel.innerHTML = ''; 643 | styleFilters = []; 644 | 645 | if( filters.length ) { 646 | 647 | noFilters.style.display = 'none'; 648 | 649 | var str = ''; 650 | filters.forEach( function( f ) { 651 | if( registeredFilters[ f.name ] ){ 652 | var filter = new registeredFilters[ f.name ](); 653 | filter.parseValue( f.value ); 654 | var ui = filter.composeUI(); 655 | ui.setAttribute( 'data-filter', styleFilters.length ); 656 | filtersPanel.appendChild( ui ); 657 | styleFilters.push( filter ); 658 | } 659 | } ); 660 | 661 | sort = Sortable.create( filtersPanel, { 662 | animation: 150, 663 | handle: '.handle', 664 | onUpdate: onValueUpdated 665 | } ); 666 | 667 | } else { 668 | 669 | noFilters.style.display = 'block'; 670 | 671 | } 672 | 673 | } 674 | 675 | ); 676 | 677 | } 678 | 679 | function processDeclaration( str ) { 680 | 681 | var level = 0; 682 | var p = 0; 683 | var name = ''; 684 | var res = []; 685 | for( var j = 0; j < str.length; j++ ) { 686 | var c = str[ j ]; 687 | if( c === '(' ){ 688 | if( level === 0 ) { 689 | name = str.substr( p, j - p ).trim(); 690 | p = j + 1; 691 | } 692 | level++; 693 | }else if( c === ')' ){ 694 | level--; 695 | if( level === 0 ){ 696 | var value = str.substr( p, j - p ); 697 | res.push( { 'name': name, 'value': value } ); 698 | p = j + 1; 699 | } 700 | }else{ 701 | 702 | } 703 | } 704 | 705 | return res; 706 | 707 | } 708 | 709 | function getFilters() { 710 | 711 | var computedStyle = getComputedStyle( $0 ); 712 | return computedStyle.getPropertyValue( 'filter' ) || computedStyle.getPropertyValue( '-webkit-filter' ); 713 | 714 | } 715 | 716 | chrome.extension.onMessage.addListener(function (msg, _, sendResponse) { 717 | alert(msg, _, sendResponse); 718 | }); 719 | -------------------------------------------------------------------------------- /src/styles.css: -------------------------------------------------------------------------------- 1 | *{ box-sizing: border-box; margin :0; padding: 0; font-family: Menlo, monospace; font-size: 11px; color: #222; font-weight: normal;} 2 | html{ height: 100%; } 3 | body{ height: 100%; } 4 | 5 | input[type=range] { 6 | margin: auto; 7 | outline: none; 8 | padding: 0; 9 | width: 50%; 10 | height: 6px; 11 | background-color: #ECECEC; 12 | background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #473e4f), color-stop(100%, #51475a)); 13 | background-image: -webkit-linear-gradient(#473e4f, #51475a); 14 | background-image: -moz-linear-gradient(#473e4f, #51475a); 15 | background-image: -o-linear-gradient(#473e4f, #51475a); 16 | background-image: linear-gradient(#C80000, #C80000); 17 | background-size: 50% 100%; 18 | background-repeat: no-repeat; 19 | border-radius: 10px; 20 | cursor: pointer; 21 | -webkit-appearance: none; 22 | } 23 | 24 | input[type=range]::-webkit-slider-runnable-track { 25 | box-shadow: none; 26 | border: none; 27 | background: transparent; 28 | -webkit-appearance: none; 29 | } 30 | 31 | input[type=range]::-moz-range-track { 32 | box-shadow: none; 33 | border: none; 34 | background: transparent; 35 | } 36 | 37 | input[type=range]::-moz-focus-outer { 38 | border: 0; 39 | } 40 | 41 | input[type=range]::-webkit-slider-thumb { 42 | width: 14px; 43 | height: 14px; 44 | border: 0; 45 | background: #fff; 46 | border-radius: 100%; 47 | box-shadow: 0 0 3px 0px rgba(0,0,0,.5); 48 | -webkit-appearance: none; 49 | } 50 | 51 | input[type=range]::-moz-range-thumb { 52 | width: 14px; 53 | height: 14px; 54 | border: 0; 55 | background: #fff; 56 | border-radius: 100%; 57 | box-shadow: 0 0 3px 0px rgba(0,0,0,.5); 58 | } 59 | 60 | #toolbar{ list-style-type: none; padding: 5px 5px; background-color: #f3f3f3; border-bottom: 1px solid #ccc; font-family: Lucida Grande, sans-serif; font-size: 12px;} 61 | #toolbar:after{ visibility: hidden; display: block; font-size: 0; content: " "; clear: both; height: 0;} 62 | #toolbar li{ float: left; padding: 5px;} 63 | #toolbar li:hover{ color: #333; background-color: #e5e5e5;} 64 | #toolbar a{ font-family: 'Lucida Grande', sans-serif; font-size: 12px; color: #5a5a5a; text-decoration: none} 65 | #filters-panel{ clear: both;} 66 | .panel{ position: relative; border-bottom: 1px solid #ccc; background-color: white;} 67 | .panel .handle{ position: absolute; left: 0; top: 0; width: 30px; bottom: 0; background-color: #ddd; cursor: move; } 68 | .handle-icon{ background-image: url(handler.svg); background-repeat: no-repeat; background-position: 50% 8px; background-size: 20%;} 69 | .panel h3{ position: absolute; left: 40px; top: 5px;} 70 | .panel .lines{ padding-top: 10px;} 71 | .remove{ position: absolute; right: 10px; top: 5px; border-bottom: 1px inherit dotted; opacity: .5; transition: opacity 150ms ease-out;} 72 | .remove:hover{ opacity: 1;} 73 | .line{ position: relative; width: 100%; height: 20px;} 74 | .line h4{ position: absolute; left: 40px; color: #C80000; } 75 | .line input[type=range]{ position: absolute; left: 120px; right: 60px; width: calc(100% - 200px); margin: 5px 0; } 76 | .line input[type=text].text{ position: absolute; left: 120px; right: 60px; width: calc(100% - 200px); margin:; 0; outline: none; border: 1px solid #f3f3f3; padding: 0 5px } 77 | .line .label{ position: absolute; right: 25px; width: 50px; text-align: right; outline: none; border: 1px solid #f3f3f3; padding: 0 5px;} 78 | .line .units{ position: absolute; right: 10px; width: 20px; text-align: right;} 79 | #no-filters{ padding: 10px; text-align: center;} 80 | footer{ padding: 5px 5px; background-color: #f3f3f3; border-bottom: 1px solid #ccc; font-family: Lucida Grande, sans-serif; font-size: 12px; text-align: right;} 81 | 82 | --------------------------------------------------------------------------------