├── .DS_Store ├── .bowerrc ├── .editorconfig ├── .gitignore ├── .jshintrc ├── .sass-cache └── d092eda4ac72474b686870cd866fbade096fa91f │ └── main.scssc ├── Async plotting.ipynb ├── Different series formats.ipynb ├── Javascript functions in options.ipynb ├── LICENSE.md ├── MANIFEST.in ├── Quickstart-nbv4.ipynb ├── README.md ├── README.rst ├── Tutorial.ipynb ├── app ├── chart.svg ├── favicon.ico ├── index.html ├── robots.txt ├── scripts │ ├── app.js │ └── ui │ │ └── Timer.js └── styles │ └── main.scss ├── bower.json ├── chart.svg ├── charts ├── .DS_Store ├── __init__.py ├── app.css ├── async.js ├── chart.py ├── core.py ├── data.py ├── fonts │ ├── Post-Creator-Icons.eot │ ├── Post-Creator-Icons.svg │ ├── Post-Creator-Icons.ttf │ └── Post-Creator-Icons.woff ├── images │ ├── gulp.png │ └── jsoneditor-icons.png ├── index-async.html ├── index.html ├── jsonencoder.py ├── lib.js ├── page.js ├── plot.py ├── require.js ├── server.py └── settings.py ├── gulpfile.js ├── index.html ├── log.txt ├── my-settings.json ├── package.json ├── preprocessor.js ├── scripts ├── app.js └── require-config.js ├── settings.json ├── setup.cfg ├── setup.py ├── small ├── AAPL.json ├── MSFT.json ├── OHLC.json ├── index.html └── keys.json ├── style.css └── view ├── .gitignore ├── LICENSE.md ├── README.md ├── gulp ├── config.js ├── tasks │ ├── browserSync.js │ ├── browserify.js │ ├── default.js │ ├── iconFont │ │ ├── generateIconSass.js │ │ └── index.js │ ├── images.js │ ├── inline.js │ ├── karma.js │ ├── markup.js │ ├── minifyCss.js │ ├── production.js │ ├── removedep.js │ ├── replace.js │ ├── sass.js │ ├── test.js │ ├── uglifyJs.js │ ├── watch.js │ └── watchify.js └── util │ ├── bundleLogger.js │ └── handleErrors.js ├── gulpfile.js ├── karma.conf.js ├── package.json └── src ├── htdocs ├── index-async.html └── index.html ├── icons ├── uE001-facebook.svg ├── uE002-linkedin.svg ├── uE003-pinterest.svg └── uE004-twitter.svg ├── images ├── gulp.png └── jsoneditor-icons.png ├── javascript ├── __tests__ │ └── page-test.coffee ├── async.js ├── lib.js ├── page.js ├── require.js └── vendor │ └── jquery-plugin.js └── sass ├── _icons.sass ├── _scss-mixins.scss ├── _typography.sass ├── app.sass ├── bootstrap ├── _bootstrap.custom.min.scss ├── bootstrap-theme.css ├── bootstrap-theme.css.map ├── bootstrap-theme.min.css ├── bootstrap.css └── bootstrap.css.map ├── jsoneditor └── _jsoneditor.min.scss └── selectize ├── _selectize.bootstrap3.scss ├── _selectize.default.scss ├── selectize.bootstrap2.css ├── selectize.css └── selectize.legacy.css /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arnoutaertgeerts/python-highcharts/6bd433cf53e851e9697639e88fcec453b7b63076/.DS_Store -------------------------------------------------------------------------------- /.bowerrc: -------------------------------------------------------------------------------- 1 | { 2 | "directory": "app/bower_components" 3 | } 4 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_style = space 6 | indent_size = 4 7 | end_of_line = lf 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | 12 | [*.md] 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Other 2 | temp.* 3 | temp* 4 | chart 5 | 6 | *.mat 7 | node_modules 8 | bower_components 9 | 10 | # Created by .ignore support plugin (hsz.mobi) 11 | ### JetBrains template 12 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm 13 | 14 | *.iml 15 | 16 | ## Directory-based project format: 17 | .idea/ 18 | # if you remove the above rule, at least ignore the following: 19 | 20 | # User-specific stuff: 21 | # .idea/workspace.xml 22 | # .idea/tasks.xml 23 | # .idea/dictionaries 24 | 25 | # Sensitive or high-churn files: 26 | # .idea/dataSources.ids 27 | # .idea/dataSources.xml 28 | # .idea/sqlDataSources.xml 29 | # .idea/dynamic.xml 30 | # .idea/uiDesigner.xml 31 | 32 | # Gradle: 33 | # .idea/gradle.xml 34 | # .idea/libraries 35 | 36 | # Mongo Explorer plugin: 37 | # .idea/mongoSettings.xml 38 | 39 | ## File-based project format: 40 | *.ipr 41 | *.iws 42 | 43 | ## Plugin-specific files: 44 | 45 | # IntelliJ 46 | out/ 47 | 48 | # mpeltonen/sbt-idea plugin 49 | .idea_modules/ 50 | 51 | # JIRA plugin 52 | atlassian-ide-plugin.xml 53 | 54 | # Crashlytics plugin (for Android Studio and IntelliJ) 55 | com_crashlytics_export_strings.xml 56 | crashlytics.properties 57 | crashlytics-build.properties 58 | 59 | 60 | ### IPythonNotebook template 61 | # Temporary data 62 | .ipynb_checkpoints/ 63 | 64 | 65 | ### Python template 66 | # Byte-compiled / optimized / DLL files 67 | __pycache__/ 68 | *.py[cod] 69 | 70 | # C extensions 71 | *.so 72 | 73 | # Distribution / packaging 74 | .Python 75 | env/ 76 | build/ 77 | develop-eggs/ 78 | dist/ 79 | downloads/ 80 | eggs/ 81 | .eggs/ 82 | lib/ 83 | lib64/ 84 | parts/ 85 | sdist/ 86 | var/ 87 | *.egg-info/ 88 | .installed.cfg 89 | *.egg 90 | 91 | # PyInstaller 92 | # Usually these files are written by a python script from a template 93 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 94 | *.manifest 95 | *.spec 96 | 97 | # Installer logs 98 | pip-log.txt 99 | pip-delete-this-directory.txt 100 | 101 | # Unit test / coverage reports 102 | htmlcov/ 103 | .tox/ 104 | .coverage 105 | .coverage.* 106 | .cache 107 | nosetests.xml 108 | coverage.xml 109 | 110 | # Translations 111 | *.mo 112 | *.pot 113 | 114 | # Django stuff: 115 | *.log 116 | 117 | # Sphinx documentation 118 | docs/_build/ 119 | 120 | # PyBuilder 121 | target/ 122 | 123 | large 124 | .pypirc -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "node": true, 3 | "esnext": true, 4 | "bitwise": true, 5 | "camelcase": true, 6 | "curly": true, 7 | "eqeqeq": true, 8 | "immed": true, 9 | "indent": 2, 10 | "latedef": true, 11 | "newcap": true, 12 | "noarg": true, 13 | "quotmark": "single", 14 | "regexp": true, 15 | "undef": true, 16 | "unused": true, 17 | "strict": true, 18 | "trailing": true, 19 | "smarttabs": true, 20 | "white": true 21 | } 22 | -------------------------------------------------------------------------------- /.sass-cache/d092eda4ac72474b686870cd866fbade096fa91f/main.scssc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arnoutaertgeerts/python-highcharts/6bd433cf53e851e9697639e88fcec453b7b63076/.sass-cache/d092eda4ac72474b686870cd866fbade096fa91f/main.scssc -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arnoutaertgeerts/python-highcharts/6bd433cf53e851e9697639e88fcec453b7b63076/MANIFEST.in -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Python-highcharts 2 | Use the excellent [highcharts/highstock](http://www.highcharts.com/stock/demo) library in Python or even in an IPython notebook as an interactive alternative to maplotlib. 3 | 4 | ## Install 5 | ```shell 6 | pip install charts 7 | ``` 8 | 9 | ## Quick start 10 | 11 | First import the library: 12 | ```python 13 | import charts 14 | ``` 15 | 16 | Second load some example data from the `data` module and some default options from the `options` module: 17 | ```python 18 | aapl = charts.data.aapl() 19 | msft = charts.data.msft() 20 | ohlc = charts.data.ohlc() 21 | 22 | ohlc['display'] = False 23 | 24 | series = [ 25 | aapl, 26 | msft, 27 | ohlc 28 | ] 29 | ``` 30 | 31 | And finally plot the chart! Use `show='inline'` if you are in an IPython notebook and `show='tab'` otherwise. 32 | ```python 33 | charts.plot(series, options, height=500, stock=True, show='inline') 34 | ``` 35 | 36 | Don't be affraid to play with the chart, it's interactive :) Try typing in `OHLC` in the variable selector or viewing a different time period by squeezing the bottom scroll bar! 37 | 38 | For more, checkout this [notebook](http://nbviewer.ipython.org/github/arnoutaertgeerts/python-highcharts/blob/master/Tutorial.ipynb)! 39 | 40 | ## Use javascript functions in your option dictionary 41 | 42 | If you want to use a javascript function in your python option dictionary to for example dynamically update a tooltip, you should pre and affix your function statements with `@#`. The procedure is explained in [this notebook](http://nbviewer.ipython.org/github/arnoutaertgeerts/python-highcharts/blob/master/Javascript%20functions%20in%20options.ipynb). 43 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | Python-Highcharts 2 | ================= 3 | 4 | A python module that allows you to use the highchart javascript library in python or inline in IPython notebooks. -------------------------------------------------------------------------------- /app/chart.svg: -------------------------------------------------------------------------------- 1 | Created with Highstock 2.1.5ValuesChart titletemperature 1temperature 200.511.522.530246810Highcharts.com -------------------------------------------------------------------------------- /app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arnoutaertgeerts/python-highcharts/6bd433cf53e851e9697639e88fcec453b7b63076/app/favicon.ico -------------------------------------------------------------------------------- /app/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | python highcharts 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 24 | 25 | 26 |
27 |
28 |

'Allo, 'Allo!

29 |

You now have

30 | 35 |
36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /app/robots.txt: -------------------------------------------------------------------------------- 1 | # robotstxt.org/ 2 | 3 | User-agent: * 4 | -------------------------------------------------------------------------------- /app/scripts/app.js: -------------------------------------------------------------------------------- 1 | 2 | var React = window.React = require('react'), 3 | Timer = require("./ui/Timer"), 4 | mountNode = document.getElementById("app"); 5 | 6 | var TodoList = React.createClass({ 7 | render: function() { 8 | var createItem = function(itemText) { 9 | return
  • {itemText}
  • ; 10 | }; 11 | return ; 12 | } 13 | }); 14 | var TodoApp = React.createClass({ 15 | getInitialState: function() { 16 | return {items: [], text: ''}; 17 | }, 18 | onChange: function(e) { 19 | this.setState({text: e.target.value}); 20 | }, 21 | handleSubmit: function(e) { 22 | e.preventDefault(); 23 | var nextItems = this.state.items.concat([this.state.text]); 24 | var nextText = ''; 25 | this.setState({items: nextItems, text: nextText}); 26 | }, 27 | render: function() { 28 | return ( 29 |
    30 |

    TODO

    31 | 32 |
    33 | 34 | 35 |
    36 | 37 |
    38 | ); 39 | } 40 | }); 41 | 42 | 43 | React.render(, mountNode); 44 | 45 | -------------------------------------------------------------------------------- /app/scripts/ui/Timer.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var React = require('react'); 4 | 5 | var Timer = React.createClass({ 6 | getInitialState: function() { 7 | return {secondsElapsed: 0}; 8 | }, 9 | tick: function() { 10 | this.setState({secondsElapsed: this.state.secondsElapsed + 1}); 11 | }, 12 | componentDidMount: function() { 13 | this.interval = setInterval(this.tick, 1000); 14 | }, 15 | componentWillUnmount: function() { 16 | clearInterval(this.interval); 17 | }, 18 | render: function() { 19 | return ( 20 |
    Seconds Elapsed: {this.state.secondsElapsed}
    21 | ); 22 | } 23 | }); 24 | 25 | 26 | module.exports = Timer; 27 | -------------------------------------------------------------------------------- /app/styles/main.scss: -------------------------------------------------------------------------------- 1 | body { 2 | background: #fafafa; 3 | font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; 4 | color: #333; 5 | } 6 | 7 | .hero-unit { 8 | margin: 50px auto 0 auto; 9 | width: 300px; 10 | font-size: 18px; 11 | font-weight: 200; 12 | line-height: 30px; 13 | background-color: #eee; 14 | border-radius: 6px; 15 | padding: 60px; 16 | h1 { 17 | font-size: 60px; 18 | line-height: 1; 19 | letter-spacing: -1px; 20 | } 21 | } 22 | 23 | .browsehappy { 24 | margin: 0.2em 0; 25 | background: #ccc; 26 | color: #000; 27 | padding: 0.2em 0; 28 | } 29 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "charts", 3 | "version": "0.0.1", 4 | "dependencies": { 5 | "jquery": "~2.1.1", 6 | "bootstrap-sass-official": ">=3.3.0", 7 | "modernizr": "^2.8.3" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /chart.svg: -------------------------------------------------------------------------------- 1 | Created with Highstock 2.1.5ValuesChart titletemperature 1temperature 200.511.522.530246810Highcharts.com -------------------------------------------------------------------------------- /charts/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arnoutaertgeerts/python-highcharts/6bd433cf53e851e9697639e88fcec453b7b63076/charts/.DS_Store -------------------------------------------------------------------------------- /charts/__init__.py: -------------------------------------------------------------------------------- 1 | __author__ = 'arnout' 2 | 3 | __version__ = '0.4.6' 4 | 5 | from plot import plot, plotasync, line, area, spline, pie 6 | from server import run_server 7 | 8 | from IPython.core import getipython 9 | from IPython.core.display import display, HTML 10 | 11 | from settings import default_settings, default_options, load_options 12 | import data 13 | import jsonencoder 14 | -------------------------------------------------------------------------------- /charts/app.css: -------------------------------------------------------------------------------- 1 | body{-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;color:#555;font-family:sans-serif} 2 | small{font-weight:400;display:block;font-size:14px} 3 | code{background-color:#d3d3d3;border-radius:3px;font-family:monospace;padding:0 .5em} 4 | .icon.-facebook:before,.icon.-linkedin:before,.icon.-pinterest:before,.icon.-twitter:before{-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;font-family:Post-Creator-Icons;font-style:normal;font-variant:normal;font-weight:400;line-height:1;speak:none;text-transform:none} 5 | @font-face{font-family:Post-Creator-Icons;src:url(fonts/Post-Creator-Icons.eot);src:url(fonts/Post-Creator-Icons.eot?#iefix) format('embedded-opentype'),url(fonts/Post-Creator-Icons.woff) format('woff'),url(fonts/Post-Creator-Icons.ttf) format('truetype'),url(fonts/Post-Creator-Icons.svg#Post-Creator-Icons) format('svg');font-weight:400;font-style:normal} 6 | .icon.-facebook:before{content:"\e001"} 7 | .icon.-linkedin:before{content:"\e002"} 8 | .icon.-pinterest:before{content:"\e003"} 9 | .icon.-twitter:before{content:"\e004"} 10 | .social-icons h4{display:inline-block;margin:20px 10px 0 0} 11 | .social-icons .icon{display:inline-block;margin:0 5px} 12 | body.modal-open{overflow:hidden} 13 | .jsoneditor table,.jsoneditor td,.jsoneditor td.tree,.jsoneditor tr{border:none;margin:0} -------------------------------------------------------------------------------- /charts/async.js: -------------------------------------------------------------------------------- 1 | (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o -1) { 227 | var newSeries = cachedSeries[index]; 228 | renderedSeries.push(newSeries); 229 | return $.when() 230 | } else { 231 | return $.get(path + '/' + key + '.json').done(function (data) { 232 | if (key.color) { 233 | data['color'] = key.color 234 | } else { 235 | data['color'] = colors[colorIndex % 10]; 236 | colorIndex = colorIndex + 1; 237 | } 238 | 239 | renderedSeries.push(data); 240 | cachedSeries.push(data); 241 | return null; 242 | }); 243 | } 244 | } 245 | 246 | function deleteSeries(key) { 247 | var index = findSeries(renderedSeries, key); 248 | renderedSeries.splice(index, 1) 249 | } 250 | 251 | function saveSVG(url, name) { 252 | $.ajax({ 253 | type: "POST", 254 | url: url, 255 | data: JSON.stringify({ 256 | svg: chart.getSVG(), 257 | name: name 258 | }) 259 | }); 260 | } 261 | } 262 | }); 263 | 264 | },{}]},{},[1]); 265 | -------------------------------------------------------------------------------- /charts/chart.py: -------------------------------------------------------------------------------- 1 | __author__ = 'arnoutaertgeerts' 2 | 3 | 4 | from core import to_series, to_json_files, show_plot, set_display 5 | 6 | 7 | class Chart(): 8 | def __init__(self, inline, html, path, show): 9 | self.inline = inline 10 | self.html = html 11 | self.path = path 12 | self.show_property = show 13 | 14 | def plot(self, *args, **kwargs): 15 | if len(args) == 2: 16 | self._plot_single(*args, **kwargs) 17 | else: 18 | self._plot_multi(*args, **kwargs) 19 | 20 | def _plot_multi(self, series, display): 21 | series = to_series(series) 22 | series = set_display(series, display) 23 | 24 | to_json_files(series, self.path) 25 | 26 | def _plot_single(self, data, name, display): 27 | series = to_series(dict(data=data, name=name)) 28 | series = set_display(series, display) 29 | 30 | to_json_files(series, self.path, ) 31 | 32 | def show(self): 33 | return show_plot(self.inline, self.html, self.show_property, async=self.path) -------------------------------------------------------------------------------- /charts/core.py: -------------------------------------------------------------------------------- 1 | __author__ = 'arnoutaertgeerts' 2 | 3 | from string import Template 4 | from jsonencoder import ChartsJSONEncoder 5 | from server import url 6 | 7 | import os 8 | import json 9 | import shutil 10 | import webbrowser 11 | import re 12 | 13 | 14 | class MyTemplate(Template): 15 | delimiter = '$#' 16 | idpattern = r'[a-z][_a-z0-9]*' 17 | 18 | 19 | def show_plot(html, saveHTML, show, async=False): 20 | if show == 'inline': 21 | from IPython.display import HTML 22 | return HTML(html) 23 | 24 | elif show == 'tab': 25 | print 'Opening new tab...' 26 | if async: 27 | address = url(async) 28 | webbrowser.open_new_tab(address) 29 | else: 30 | webbrowser.open_new_tab('file://' + os.path.realpath(saveHTML)) 31 | 32 | elif show == 'window': 33 | print 'Trying to open a window. If this fails we will open a tab...' 34 | if async: 35 | address = url(async) 36 | webbrowser.open_new(address) 37 | else: 38 | webbrowser.open_new('file://' + os.path.realpath(saveHTML)) 39 | 40 | elif show == 'string': 41 | return html 42 | 43 | 44 | def clean_dir(path): 45 | if os.path.exists(path): 46 | shutil.rmtree(path) 47 | os.makedirs(path) 48 | 49 | 50 | def make_dir(path): 51 | if not os.path.exists(path): 52 | os.makedirs(path) 53 | 54 | 55 | def to_json_files(series, path): 56 | try: 57 | with open(os.path.join(path, 'keys.json'), "r") as keys_file: 58 | keys = json.loads(keys_file.read()) 59 | except IOError: 60 | keys = [] 61 | 62 | for k in keys: 63 | k["display"] = False 64 | 65 | for s in series: 66 | if s["name"] not in map(lambda x: x["name"], keys): 67 | keys.append(dict(name=s["name"], display=s["display"], value=s["name"], text=s["name"])) 68 | with open(os.path.join(path, s["name"] + ".json"), "w") as json_file: 69 | json_file.write(json.dumps(s, cls=ChartsJSONEncoder)) 70 | else: 71 | i = find(keys, "name", s["name"]) 72 | keys[i] = dict(name=s["name"], display=s["display"], value=s["name"], text=s["name"]) 73 | 74 | with open(os.path.join(path, 'keys.json'), "w") as keys_file: 75 | keys_file.write(json.dumps(keys)) 76 | 77 | 78 | def find(col, key, value): 79 | for i, c in enumerate(col): 80 | if c[key] == value: 81 | return i 82 | 83 | 84 | def set_display(series, display): 85 | if display is True: 86 | for s in series: 87 | s['display'] = True 88 | 89 | elif display is False: 90 | for s in series: 91 | s['display'] = False 92 | 93 | else: 94 | for s in series: 95 | if s['name'] in display: 96 | s['display'] = True 97 | else: 98 | s['display'] = False 99 | 100 | return series 101 | 102 | 103 | def df_to_series(df): 104 | """Prepare data from dataframe for plotting with python-highcharts. 105 | all columns in df are entries in the returned series. 106 | 107 | The returned series is in the format suitable for python-highcharts: list of dicts with: 108 | data:list of [index, value]-lists. 109 | name:name of variable. 110 | """ 111 | 112 | import pandas as pd 113 | import numpy as np 114 | 115 | df = df.where((pd.notnull(df)), None) 116 | 117 | if isinstance(df.index, pd.DatetimeIndex): 118 | index = df.index.asi8 / (1e6) 119 | else: 120 | index = df.index 121 | 122 | series = [] 123 | for col in df: 124 | series.append( 125 | dict(name=col, 126 | data=np.array([index, df[col].values]).T, 127 | display=False) 128 | ) 129 | 130 | return series 131 | 132 | 133 | def list_to_series(array): 134 | return dict( 135 | data=array 136 | ) 137 | 138 | 139 | def to_series(series, name=False): 140 | # Dictionary? 141 | if isinstance(series, dict): 142 | return [series] 143 | 144 | # List of dictionaries?: 145 | try: 146 | if isinstance(series[0], dict): 147 | return series 148 | except KeyError: 149 | pass 150 | 151 | # List? 152 | try: 153 | if isinstance(series, list): 154 | return [dict(data=series, name=name)] 155 | except KeyError: 156 | pass 157 | 158 | # Numpy array? 159 | try: 160 | import numpy as np 161 | if isinstance(series, np.ndarray): 162 | return [dict(data=series, name=name)] 163 | except ImportError: 164 | pass 165 | 166 | # pandas DataFrame or series? 167 | try: 168 | import pandas as pd 169 | if isinstance(series, pd.DataFrame): 170 | return df_to_series(series) 171 | if isinstance(series, pd.Series): 172 | return df_to_series(pd.DataFrame(series)) 173 | except ImportError: 174 | pass 175 | 176 | raise ValueError('Your data is not in the right format!') 177 | 178 | def remove_quotes(options): 179 | 180 | options = json.dumps(options) 181 | 182 | ix = [m.start() for m in re.finditer('@#', options)] 183 | 184 | for j, i in enumerate(ix): 185 | k = i-3*j 186 | if options[k-1] == '"': 187 | options = options[:k-1] + options[k+2:] 188 | else: 189 | options = options[:k] + options[k+3:] 190 | 191 | return options 192 | -------------------------------------------------------------------------------- /charts/fonts/Post-Creator-Icons.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arnoutaertgeerts/python-highcharts/6bd433cf53e851e9697639e88fcec453b7b63076/charts/fonts/Post-Creator-Icons.eot -------------------------------------------------------------------------------- /charts/fonts/Post-Creator-Icons.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 9 | 10 | 13 | 16 | 19 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /charts/fonts/Post-Creator-Icons.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arnoutaertgeerts/python-highcharts/6bd433cf53e851e9697639e88fcec453b7b63076/charts/fonts/Post-Creator-Icons.ttf -------------------------------------------------------------------------------- /charts/fonts/Post-Creator-Icons.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arnoutaertgeerts/python-highcharts/6bd433cf53e851e9697639e88fcec453b7b63076/charts/fonts/Post-Creator-Icons.woff -------------------------------------------------------------------------------- /charts/images/gulp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arnoutaertgeerts/python-highcharts/6bd433cf53e851e9697639e88fcec453b7b63076/charts/images/gulp.png -------------------------------------------------------------------------------- /charts/images/jsoneditor-icons.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arnoutaertgeerts/python-highcharts/6bd433cf53e851e9697639e88fcec453b7b63076/charts/images/jsoneditor-icons.png -------------------------------------------------------------------------------- /charts/jsonencoder.py: -------------------------------------------------------------------------------- 1 | __author__ = 'Arnout Aertgeerts' 2 | 3 | import json 4 | 5 | 6 | class ChartsJSONEncoder(json.JSONEncoder): 7 | def default(self, obj): 8 | try: 9 | import numpy 10 | if isinstance(obj, numpy.ndarray): 11 | if obj.ndim == 1: 12 | return obj.tolist() 13 | else: 14 | return [self.default(obj[i]) for i in range(obj.shape[0])] 15 | elif isinstance(obj, numpy.generic): 16 | return obj.item() 17 | except ImportError: 18 | pass 19 | 20 | return json.JSONEncoder.default(self, obj) 21 | -------------------------------------------------------------------------------- /charts/lib.js: -------------------------------------------------------------------------------- 1 | (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o>> 0; 13 | var thisArg = arguments[1]; 14 | var value; 15 | 16 | for (var i = 0; i < length; i++) { 17 | value = list[i]; 18 | if (predicate.call(thisArg, value, i, list)) { 19 | return i; 20 | } 21 | } 22 | return -1; 23 | }; 24 | } 25 | 26 | if (!Array.prototype.filter) { 27 | Array.prototype.filter = function(fun/*, thisArg*/) { 28 | 'use strict'; 29 | 30 | if (this === void 0 || this === null) { 31 | throw new TypeError(); 32 | } 33 | 34 | var t = Object(this); 35 | var len = t.length >>> 0; 36 | if (typeof fun !== 'function') { 37 | throw new TypeError(); 38 | } 39 | 40 | var res = []; 41 | var thisArg = arguments.length >= 2 ? arguments[1] : void 0; 42 | for (var i = 0; i < len; i++) { 43 | if (i in t) { 44 | var val = t[i]; 45 | 46 | // NOTE: Technically this should Object.defineProperty at 47 | // the next index, as push can be affected by 48 | // properties on Object.prototype and Array.prototype. 49 | // But that method's new, and collisions should be 50 | // rare, so use the more-compatible alternative. 51 | if (fun.call(thisArg, val, i, t)) { 52 | res.push(val); 53 | } 54 | } 55 | } 56 | 57 | return res; 58 | }; 59 | } 60 | 61 | function guid() { 62 | function s4() { 63 | return Math.floor((1 + Math.random()) * 0x10000) 64 | .toString(16) 65 | .substring(1); 66 | } 67 | return s4() + s4() + '-' + s4() + '-' + s4() + '-' + 68 | s4() + '-' + s4() + s4() + s4(); 69 | } 70 | 71 | Array.prototype.equals = function (array) { 72 | // if the other array is a false value, return 73 | if (!array) 74 | return false; 75 | 76 | // compare lengths - can save a lot of time 77 | if (this.length != array.length) 78 | return false; 79 | 80 | for (var i = 0, l = this.length; i < l; i++) { 81 | // Check if we have nested arrays 82 | if (this[i] instanceof Array && array[i] instanceof Array) { 83 | // recurse into the nested arrays 84 | if (!this[i].equals(array[i])) 85 | return false; 86 | } 87 | else if (this[i] != array[i]) { 88 | // Warning - two different object instances will never be equal: {x:20} != {x:20} 89 | return false; 90 | } 91 | } 92 | return true; 93 | }; 94 | 95 | },{}]},{},[1]); 96 | -------------------------------------------------------------------------------- /charts/page.js: -------------------------------------------------------------------------------- 1 | (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o 16 | 17 | """ 18 | 19 | 20 | def line(*args, **kwargs): 21 | return plot(*args, type='line', **kwargs) 22 | 23 | 24 | def area(*args, **kwargs): 25 | return plot(*args, type='area', **kwargs) 26 | 27 | 28 | def spline(*args, **kwargs): 29 | return plot(*args, type='spline', **kwargs) 30 | 31 | 32 | def pie(*args, **kwargs): 33 | return plot(*args, type='pie', **kwargs) 34 | 35 | 36 | def stock(*args, **kwargs): 37 | return plot(*args, stock=True, **kwargs) 38 | 39 | 40 | def plot(series, options=dict(), **kwargs): 41 | """ 42 | Make a highchart plot with all data embedded in the HTML 43 | :param type: Type of the chart (will overwrite options['chart']['type'] if specified). 44 | :param series: The necessary data, can be a list of dictionaries or a dataframe 45 | :param options: Options for the chart. This can one of the following: 46 | - A Dictionary 47 | - The path to a json file 48 | - A json string 49 | :param height: Chart height 50 | :param save: Specify a filename to save the HTML file if wanted. 51 | :param stock: Set to False to use Highcharts instead of highstock 52 | :param show: Determines how the chart is shown. Can be one of the following options: 53 | - 'tab': Show the chart in a new tab of the default browser 54 | - 'window': Show the chart in a new window of the default browser 55 | - 'inline': Show the chart inline (only works in IPython notebook) 56 | :param display: A list containing the keys of the variables you want to show initially in the plot 57 | :return: The chart to display 58 | """ 59 | 60 | # Check if options is a json string or file 61 | if isinstance(options, str): 62 | if '.json' in options: 63 | options = load_options(options) 64 | else: 65 | try: 66 | options = json.loads(options) 67 | except ValueError: 68 | raise ValueError('Your options string is not valid JSON!') 69 | 70 | chart_settings = default_settings.copy() 71 | chart_options = default_options.copy() 72 | 73 | chart_settings.update(kwargs) 74 | chart_options.update(options) 75 | 76 | keys = chart_settings.keys() 77 | 78 | for key in keys: 79 | if key not in ['options', 'name', 'display', 'save', 'show', 'height', 'type', 'stock', 'width']: 80 | raise AttributeError(key + ' is not a valid option!') 81 | 82 | options = chart_options 83 | name = chart_settings['name'] 84 | display = chart_settings['display'] 85 | save = chart_settings['save'] 86 | show = chart_settings['show'] 87 | type = chart_settings['type'] 88 | stock = chart_settings['stock'] 89 | 90 | try: 91 | if options['chart']: 92 | options['chart'].update(dict(type=type)) 93 | except KeyError: 94 | options['chart'] = dict(type=type) 95 | 96 | try: 97 | if not options['height']: 98 | options['chart'].update(dict(type=type)) 99 | except KeyError: 100 | options['chart'] = dict(type=type) 101 | 102 | # Convert to a legitimate series object 103 | series = to_series(series, name) 104 | 105 | # Set the display option 106 | series = set_display(series, display) 107 | 108 | # Get the save extension 109 | if save: 110 | extension = os.path.splitext(save)[1] 111 | else: 112 | extension = False 113 | 114 | saveSVG = False 115 | saveHTML = False 116 | 117 | if extension == '.svg': 118 | saveSVG = save 119 | if show != 'inline': 120 | saveHTML = 'index.html' 121 | if extension == '.html': 122 | saveHTML = save 123 | 124 | if 'settingsFile' in options: 125 | settings_file = options['settingsFile'][:-5] 126 | else: 127 | settings_file = 'settings' 128 | 129 | with open(os.path.join(package_directory, "index.html"), "r") as html: 130 | html = MyTemplate(html.read()).substitute( 131 | path=package_directory, 132 | series=json.dumps(series, cls=ChartsJSONEncoder), 133 | options=remove_quotes(options), 134 | highstock=json.dumps(stock), 135 | url=json.dumps(address), 136 | save=json.dumps(saveSVG), 137 | settingsFile=json.dumps(settings_file) 138 | ) 139 | 140 | if saveHTML: 141 | with open(saveHTML, "w") as text_file: 142 | text_file.write(html + TABDEPS) 143 | 144 | return show_plot(html, saveHTML, show) 145 | 146 | 147 | def plotasync( 148 | series=None, options=dict(), type='line', 149 | height=400, save="temp", stock=False, show='tab', display=False, purge=False, live=False): 150 | """ 151 | :param type: Type of the chart. Can be line, area, spline, pie, bar, ... 152 | :param display: Set to true to display all, False to display none or an array of names for a specific selection 153 | :param purge: Set to true to clean the directory 154 | :param live: Set to true to keep the chart in sync with data in the directory. Currently only works for show='tab' 155 | :param series: The series object which contains the data. If this is not specified, the plot will look for json 156 | files in the save directory. 157 | :param options: The chart display options 158 | :param height: Height of the chart 159 | :param save: Name of the directory to store the data 160 | :param stock: Set to true to use highstock 161 | :param show: Determines how the chart is shown. Can be one of the following options: 162 | - 'tab': Show the chart in a new tab of the default browser 163 | - 'window': Show the chart in a new window of the default browser 164 | - 'inline': Show the chart inline (only works in IPython notebook) 165 | :return: A chart object 166 | """ 167 | 168 | try: 169 | if not options['chart']: 170 | options['chart'] = dict(type=type) 171 | except KeyError: 172 | options['chart'] = dict(type=type) 173 | 174 | if 'height' not in options: 175 | options['height'] = 400 176 | 177 | # Clean the directory 178 | if purge: 179 | clean_dir(save) 180 | else: 181 | make_dir(save) 182 | 183 | if series is not None: 184 | # Convert to a legitimate series object 185 | series = to_series(series) 186 | series = set_display(series, display) 187 | 188 | # Convert to json files 189 | to_json_files(series, save) 190 | 191 | if show == 'inline': 192 | live = False 193 | 194 | with open(os.path.join(package_directory, "index-async.html"), "r") as index: 195 | read = index.read() 196 | 197 | html = MyTemplate(read).substitute( 198 | path=json.dumps('/' + save), 199 | options=json.dumps(options), 200 | highstock=json.dumps(stock), 201 | height=str(height) + "px", 202 | live=json.dumps(live), 203 | url=json.dumps(address), 204 | save=json.dumps(False) 205 | ) 206 | 207 | inline = MyTemplate(read).substitute( 208 | path=json.dumps(save), 209 | options=json.dumps(options), 210 | highstock=json.dumps(stock), 211 | height=str(height) + "px", 212 | live=json.dumps(live), 213 | url=json.dumps(address), 214 | save=json.dumps(False) 215 | ) 216 | 217 | html_path = os.path.join(save, 'index.html') 218 | with open(html_path, "w") as html_file: 219 | html_file.write(html + TABDEPS) 220 | 221 | return Chart(inline, html_path, save, show) 222 | -------------------------------------------------------------------------------- /charts/require.js: -------------------------------------------------------------------------------- 1 | (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;oCreated with Highstock 2.1.5ValuesChart titletemperature 1temperature 200.511.522.530246810Highcharts.com -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "charts", 3 | "version": "0.0.0", 4 | "dependencies": {}, 5 | "devDependencies": { 6 | "del": "~0.1.3", 7 | "gulp": "~3.8.8", 8 | "gulp-autoprefixer": "~1.0.1", 9 | "gulp-bower": "0.0.6", 10 | "gulp-cache": "~0.2.4", 11 | "gulp-imagemin": "latest", 12 | "gulp-jshint": "~1.8.5", 13 | "gulp-load-plugins": "~0.7.0", 14 | "gulp-ruby-sass": "~0.7.1", 15 | "browser-sync": "latest", 16 | "gulp-size": "~1.1.0", 17 | "gulp-useref": "~0.4.4", 18 | "gulp-util": "~3.0.1", 19 | "gulp-webserver": "latest", 20 | "react": "latest", 21 | "react-tools": "latest", 22 | "reactify": "latest", 23 | "watchify": "~2.1", 24 | "browserify-shim": "^3.8.0", 25 | "gulp-uglify": "^1.0.2", 26 | "strip-debug": "^1.0.1", 27 | "gulp-strip-debug": "^1.0.2", 28 | "vinyl-source-stream": "^1.0.0", 29 | "main-bower-files":"~2.6.2" 30 | }, 31 | "engines": { 32 | "node": ">=0.10.0" 33 | }, 34 | "scripts": { 35 | "test": "jest" 36 | }, 37 | "jest": { 38 | "scriptPreprocessor": "/preprocessor.js", 39 | "unmockedModulePathPatterns": [ 40 | "/node_modules/react" 41 | ] 42 | }, 43 | "browserify": { 44 | "transform": [ 45 | "browserify-shim", 46 | [ 47 | "reactify", 48 | { 49 | "es6": true 50 | } 51 | ] 52 | ] 53 | }, 54 | "browser": { 55 | "jquery": "./app/bower_components/jquery/dist/jquery.js" 56 | }, 57 | "browserify-shim": { 58 | "jquery": "$" 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /preprocessor.js: -------------------------------------------------------------------------------- 1 | var ReactTools = require('react-tools'); 2 | module.exports = { 3 | process: function(src) { 4 | return ReactTools.transform(src, { 5 | harmony: true 6 | }); 7 | } 8 | }; 9 | -------------------------------------------------------------------------------- /scripts/app.js: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /scripts/require-config.js: -------------------------------------------------------------------------------- 1 | requirejs.config({ 2 | paths: { 3 | 'highstock': "http://code.highcharts.com/stock/highstock", 4 | 'standalone': "http://code.highcharts.com/adapters/standalone-framework", 5 | 'export': "http://code.highcharts.com/stock/modules/exporting", 6 | 'angular': "https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.3.15/angular" 7 | }, 8 | 9 | shim: { 10 | 'highstock': { 11 | deps: ['standalone'], 12 | exports: 'Highcharts' 13 | } 14 | } 15 | }); 16 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arnoutaertgeerts/python-highcharts/6bd433cf53e851e9697639e88fcec453b7b63076/setup.cfg -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | __author__ = 'arnout' 2 | 3 | from setuptools import setup, find_packages 4 | 5 | setup( 6 | name='charts', 7 | version='0.4.6', 8 | description='Use the highcharts js library in Python', 9 | url='https://github.com/arnoutaertgeerts/python-highcharts', 10 | author='Arnout Aertgeerts', 11 | author_email='arnoutaertgeerts@gmail.com', 12 | license='MIT', 13 | keywords='highcharts highstock plotting', 14 | packages=find_packages(), 15 | install_requires=[], 16 | package_data={ 17 | 'charts': ['*.html'] 18 | } 19 | ) 20 | -------------------------------------------------------------------------------- /small/keys.json: -------------------------------------------------------------------------------- 1 | [{ 2 | "name": "AAPL", 3 | "display": true 4 | }, { 5 | "name": "MSFT", 6 | "display": true 7 | }, { 8 | "name": "OHLC", 9 | "display": false 10 | } 11 | ] 12 | -------------------------------------------------------------------------------- /style.css: -------------------------------------------------------------------------------- 1 | .md-chips .md-chip-input-container input:focus { 2 | outline: none; 3 | border: none; 4 | box-shadow: none; 5 | } 6 | 7 | 8 | .chipsdemoContactChips .md-item-text.compact { 9 | padding-top: 8px; 10 | padding-bottom: 8px; } 11 | .chipsdemoContactChips .contact-item { 12 | box-sizing: border-box; } 13 | .chipsdemoContactChips .contact-item.selected { 14 | opacity: 0.5; } 15 | .chipsdemoContactChips .contact-item.selected h3 { 16 | opacity: 0.5; } 17 | .chipsdemoContactChips .contact-item .md-list-item-text { 18 | padding: 14px 0; } 19 | .chipsdemoContactChips .contact-item .md-list-item-text h3 { 20 | margin: 0 !important; 21 | padding: 0; 22 | line-height: 1.2em !important; } 23 | .chipsdemoContactChips .contact-item .md-list-item-text h3, .chipsdemoContactChips .contact-item .md-list-item-text p { 24 | text-overflow: ellipsis; 25 | white-space: nowrap; 26 | overflow: hidden; } 27 | @media (min-width: 900px) { 28 | .chipsdemoContactChips .contact-item { 29 | float: left; 30 | width: 33%; } } 31 | .chipsdemoContactChips md-contact-chips { 32 | margin-bottom: 10px; } 33 | .chipsdemoContactChips .md-chips { 34 | padding: 5px 0 8px; } 35 | .chipsdemoContactChips .fixedRows { 36 | height: 250px; 37 | overflow: hidden; } -------------------------------------------------------------------------------- /view/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | build 3 | Desktop.ini 4 | node_modules 5 | -------------------------------------------------------------------------------- /view/LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Daniel Tello 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 | -------------------------------------------------------------------------------- /view/README.md: -------------------------------------------------------------------------------- 1 | **Lots of new stuff happening in the [2.0 branch](https://github.com/greypants/gulp-starter/tree/2.0)** 2 | 3 | **Use Rails?** Check out http://viget.com/extend/gulp-rails-asset-pipeline and https://github.com/vigetlabs/gulp-rails-pipeline 4 | 5 | gulp-starter 6 | ============ 7 | 8 | Starter Gulp + Browserify project with examples of how to accomplish some common tasks and workflows. Read the [blog post](http://viget.com/extend/gulp-browserify-starter-faq) for more context, and check out the [Wiki](https://github.com/greypants/gulp-starter/wiki) for some good background knowledge. 9 | 10 | Includes the following tools, tasks, and workflows: 11 | 12 | - [Browserify](http://browserify.org/) (with [browserify-shim](https://github.com/thlorenz/browserify-shim)) 13 | - [Watchify](https://github.com/substack/watchify) (caching version of browserify for super fast rebuilds) 14 | - [SASS](http://sass-lang.com/) (super fast libsass with [source maps](https://github.com/sindresorhus/gulp-ruby-sass#sourcemap), and [autoprefixer](https://github.com/sindresorhus/gulp-autoprefixer)) 15 | - [CoffeeScript](http://coffeescript.org/) (with source maps!) 16 | - [BrowserSync](http://browsersync.io) for live reloading and a static server 17 | - [Image optimization](https://www.npmjs.com/package/gulp-imagemin) 18 | - Error handling in the console [and in Notification Center](https://github.com/mikaelbr/gulp-notify) 19 | - Shimming non common-js vendor code with other dependencies (like a jQuery plugin) 20 | - **New** Multiple bundles with shared dependencies 21 | - **New** Separate compression task for production builds 22 | - **New** Icon Font generation 23 | 24 | If you've never used Node or npm before, you'll need to install Node. 25 | If you use homebrew, do: 26 | 27 | ``` 28 | brew install node 29 | ``` 30 | 31 | Otherwise, you can download and install from [here](http://nodejs.org/download/). 32 | 33 | ### Install npm dependencies 34 | ``` 35 | npm install 36 | ``` 37 | 38 | This runs through all dependencies listed in `package.json` and downloads them to a `node_modules` folder in your project directory. 39 | 40 | ### The `gulp` command 41 | To run the version of gulp installed local to the project, in the root of your this project, you'd run 42 | 43 | ``` 44 | ./node_modules/.bin/gulp 45 | ``` 46 | 47 | **WAT.** Why can't I just run `gulp`? Well, you could install gulp globally with `npm install -g gulp`, which will add the gulp script to your global bin folder, but it's always better to use the version that's specified in your project's package.json. My solution to this is to simply alias `./node_modules/.bin/gulp` to `gulp`. Open up `~/.zshrc` or `~./bashrc` and add the following line: 48 | 49 | ``` 50 | alias gulp='node_modules/.bin/gulp' 51 | ``` 52 | Now, running `gulp` in the project directory will use the version specified and installed from the `package.json` file. 53 | 54 | ### Run gulp and be amazed. 55 | The first time you run the app, you'll also need to generate the iconFont, since this is not something we want to run every time with our `default` task. 56 | ``` 57 | gulp iconFont 58 | ``` 59 | 60 | After that, just run the `default` gulp task with: 61 | ``` 62 | gulp 63 | ``` 64 | 65 | This will run the `default` gulp task defined in `gulp/tasks/default.js`, which has the following task dependencies: `['sass', 'images', 'markup', 'watch']` 66 | - The `sass` task compiles your css files. 67 | - `images` moves images copies images from a source folder, performs optimizations, the outputs them into the build folder 68 | - `markup` doesn't do anything but copy an html file over from src to build, but here is where you could do additional templating work. 69 | - `watch` has `watchify` as a dependency, which will run the browserifyTask with a `devMode` flag that enables sourcemaps and watchify, a browserify add-on that enables caching for super fast recompiling. The task itself starts watching source files and will re-run the appropriate tasks when those files change. 70 | 71 | ### Configuration 72 | All paths and plugin settings have been abstracted into a centralized config object in `gulp/config.js`. Adapt the paths and settings to the structure and needs of your project. 73 | 74 | ### Additional Features and Tasks 75 | 76 | #### Icon Fonts 77 | 78 | ``` 79 | gulp iconFont 80 | ``` 81 | 82 | Generating and re-generating icon fonts is an every once and a while task, so it's not included in `tasks/default.js`. Run the task separately any time you add an svg to your icons folder. This task has a couple of parts. 83 | 84 | ##### The task 85 | The task calls `gulp-iconfont` and passes the options we've configured in [`gulp/config.js`](https://github.com/greypants/gulp-starter/blob/icon-font/gulp/config.js#L27). Then it listens for a `codepoints` that triggers the generation of the sass file you'll be importing into your stylesheets. [`gulp/iconFont/generateIconSass`](./gulp/tasks/iconFont/generateIconSass.js) passes the icon data to [a template](./gulp/tasks/iconFont/template.sass.swig), then outputs the resulting file to your sass directory. See the [gulp-iconFont docs](https://github.com/nfroidure/gulp-iconfont) for more config details. You may reconfigure the template to output whatever you'd like. The way it's currently set up will make icons usable as both class names and mixins. 86 | 87 | ```sass 88 | .twitter-button 89 | +icon--twitter // (@include in .scss syntax) 90 | ``` 91 | 92 | or 93 | 94 | ```html 95 | 96 | ``` 97 | 98 | #### Production files 99 | 100 | There is also a `production` task you can run: 101 | ``` 102 | gulp production 103 | ``` 104 | This will run JavaScript tests, then re-build optimized, compressed css and js files to the build folder, as well as output their file sizes to the console. It's a shortcut for running the following tasks: `karma`, `images`, `iconFont` `minifyCss`, `uglifyJs`. 105 | 106 | #### JavaScript Tests with Karma 107 | This repo includes a basic js testing setup with the following: [Karma](http://karma-runner.github.io/0.12/index.html), [Mocha](http://mochajs.org/), [Chai](http://chaijs.com/), and [Sinon](http://sinonjs.org/). There is `karma` gulp task, which the `production` task uses to run the tests before compiling. If any tests fail, the `production` task will abort. 108 | 109 | To run the tests and start monitoring files: 110 | ``` 111 | ./node_modules/karma/bin/karma start 112 | ``` 113 | 114 | Want to just run `karma start`? Either add `alias karma="./node_modules/karma/bin/karma"` to your shell config or install the karma command line interface globally with `npm install -g karma-cli`. 115 | 116 | 117 | -- 118 | 119 | Social icons courtesy of [icomoon.io](https://icomoon.io/#icons-icomoon) 120 | 121 | Made with ♥ at [Viget](http://viget.com)! 122 | -------------------------------------------------------------------------------- /view/gulp/config.js: -------------------------------------------------------------------------------- 1 | var dest = "../charts"; 2 | //var dest = "./build"; 3 | var src = './src'; 4 | 5 | module.exports = { 6 | browserSync: { 7 | server: { 8 | // Serve up our build folder 9 | baseDir: dest 10 | } 11 | }, 12 | sass: { 13 | src: src + "/sass/**/*.{sass,scss}", 14 | dest: dest, 15 | settings: { 16 | indentedSyntax: true, // Enable .sass syntax! 17 | imagePath: 'images' // Used by the image-url helper 18 | } 19 | }, 20 | images: { 21 | src: src + "/images/**", 22 | dest: dest + "/images" 23 | }, 24 | markup: { 25 | src: src + "/htdocs/**", 26 | dest: dest 27 | }, 28 | iconFonts: { 29 | name: 'Gulp Starter Icons', 30 | src: src + '/icons/*.svg', 31 | dest: dest + '/fonts', 32 | sassDest: src + '/sass', 33 | template: './gulp/tasks/iconFont/template.sass.swig', 34 | sassOutputName: '_icons.sass', 35 | fontPath: 'fonts', 36 | className: 'icon', 37 | options: { 38 | fontName: 'Post-Creator-Icons', 39 | appendCodepoints: true, 40 | normalize: false 41 | } 42 | }, 43 | browserify: { 44 | // A separate bundle will be generated for each 45 | // bundle config in the list below 46 | bundleConfigs: [ 47 | { 48 | entries: src + '/javascript/page.js', 49 | dest: dest, 50 | outputName: 'page.js' 51 | // list of externally available modules to exclude from the bundle 52 | //external: ['jquery'] 53 | }, 54 | { 55 | entries: src + '/javascript/async.js', 56 | dest: dest, 57 | outputName: 'async.js' 58 | // list of externally available modules to exclude from the bundle 59 | //external: ['jquery'] 60 | }, 61 | { 62 | entries: src + '/javascript/require.js', 63 | dest: dest, 64 | outputName: 'require.js' 65 | }, 66 | { 67 | entries: src + '/javascript/lib.js', 68 | dest: dest, 69 | outputName: 'lib.js' 70 | } 71 | ] 72 | }, 73 | production: { 74 | cssSrc: dest + '/*.css', 75 | jsSrc: dest + '/*.js', 76 | htmlSrc: dest + '/*.html', 77 | dest: dest 78 | } 79 | }; 80 | -------------------------------------------------------------------------------- /view/gulp/tasks/browserSync.js: -------------------------------------------------------------------------------- 1 | var browserSync = require('browser-sync'); 2 | var gulp = require('gulp'); 3 | var config = require('../config').browserSync; 4 | 5 | gulp.task('browserSync', function() { 6 | browserSync(config); 7 | }); 8 | -------------------------------------------------------------------------------- /view/gulp/tasks/browserify.js: -------------------------------------------------------------------------------- 1 | /* browserify task 2 | --------------- 3 | Bundle javascripty things with browserify! 4 | 5 | This task is set up to generate multiple separate bundles, from 6 | different sources, and to use Watchify when run from the default task. 7 | 8 | See browserify.bundleConfigs in gulp/config.js 9 | */ 10 | 11 | var browserify = require('browserify'); 12 | var browserSync = require('browser-sync'); 13 | var watchify = require('watchify'); 14 | var mergeStream = require('merge-stream'); 15 | var bundleLogger = require('../util/bundleLogger'); 16 | var gulp = require('gulp'); 17 | var handleErrors = require('../util/handleErrors'); 18 | var source = require('vinyl-source-stream'); 19 | var config = require('../config').browserify; 20 | var _ = require('lodash'); 21 | 22 | var browserifyTask = function(devMode) { 23 | 24 | var browserifyThis = function(bundleConfig) { 25 | 26 | if(devMode) { 27 | // Add watchify args and debug (sourcemaps) option 28 | _.extend(bundleConfig, watchify.args, { debug: true }); 29 | // A watchify require/external bug that prevents proper recompiling, 30 | // so (for now) we'll ignore these options during development. Running 31 | // `gulp browserify` directly will properly require and externalize. 32 | bundleConfig = _.omit(bundleConfig, ['external', 'require']); 33 | } 34 | 35 | var b = browserify(bundleConfig); 36 | 37 | var bundle = function() { 38 | // Log when bundling starts 39 | bundleLogger.start(bundleConfig.outputName); 40 | 41 | return b 42 | .bundle() 43 | // Report compile errors 44 | .on('error', handleErrors) 45 | // Use vinyl-source-stream to make the 46 | // stream gulp compatible. Specify the 47 | // desired output filename here. 48 | .pipe(source(bundleConfig.outputName)) 49 | // Specify the output destination 50 | .pipe(gulp.dest(bundleConfig.dest)) 51 | .pipe(browserSync.reload({ 52 | stream: true 53 | })); 54 | }; 55 | 56 | if(devMode) { 57 | // Wrap with watchify and rebundle on changes 58 | b = watchify(b); 59 | // Rebundle on update 60 | b.on('update', bundle); 61 | bundleLogger.watch(bundleConfig.outputName); 62 | } else { 63 | // Sort out shared dependencies. 64 | // b.require exposes modules externally 65 | if(bundleConfig.require) b.require(bundleConfig.require); 66 | // b.external excludes modules from the bundle, and expects 67 | // they'll be available externally 68 | if(bundleConfig.external) b.external(bundleConfig.external); 69 | } 70 | 71 | return bundle(); 72 | }; 73 | 74 | // Start bundling with Browserify for each bundleConfig specified 75 | return mergeStream.apply(gulp, _.map(config.bundleConfigs, browserifyThis)); 76 | 77 | }; 78 | 79 | gulp.task('browserify', function() { 80 | return browserifyTask() 81 | }); 82 | 83 | // Exporting the task so we can call it directly in our watch task, with the 'devMode' option 84 | module.exports = browserifyTask; 85 | -------------------------------------------------------------------------------- /view/gulp/tasks/default.js: -------------------------------------------------------------------------------- 1 | var gulp = require('gulp'); 2 | 3 | gulp.task('default', ['sass', 'images', 'markup', 'watch']); 4 | -------------------------------------------------------------------------------- /view/gulp/tasks/iconFont/generateIconSass.js: -------------------------------------------------------------------------------- 1 | var gulp = require('gulp'); 2 | var config = require('../../config').iconFonts; 3 | var swig = require('gulp-swig'); 4 | var rename = require('gulp-rename'); 5 | 6 | module.exports = function(codepoints, options) { 7 | gulp.src(config.template) 8 | .pipe(swig({ 9 | data: { 10 | icons: codepoints.map(function(icon) { 11 | return { 12 | name: icon.name, 13 | code: icon.codepoint.toString(16) 14 | } 15 | }), 16 | 17 | fontName: config.options.fontName, 18 | fontPath: config.fontPath, 19 | className: config.className, 20 | comment: 'DO NOT EDIT DIRECTLY!\n Generated by gulp/tasks/iconFont.js\n from ' + config.template 21 | } 22 | })) 23 | .pipe(rename(config.sassOutputName)) 24 | .pipe(gulp.dest(config.sassDest)); 25 | }; 26 | -------------------------------------------------------------------------------- /view/gulp/tasks/iconFont/index.js: -------------------------------------------------------------------------------- 1 | var gulp = require('gulp'); 2 | var iconfont = require('gulp-iconfont'); 3 | var config = require('../../config').iconFonts; 4 | var generateIconSass = require('./generateIconSass'); 5 | 6 | gulp.task('iconFont', function() { 7 | return gulp.src(config.src) 8 | .pipe(iconfont(config.options)) 9 | .on('codepoints', generateIconSass) 10 | .pipe(gulp.dest(config.dest)); 11 | }); 12 | -------------------------------------------------------------------------------- /view/gulp/tasks/images.js: -------------------------------------------------------------------------------- 1 | var changed = require('gulp-changed'); 2 | var gulp = require('gulp'); 3 | var imagemin = require('gulp-imagemin'); 4 | var config = require('../config').images; 5 | var browserSync = require('browser-sync'); 6 | 7 | gulp.task('images', function() { 8 | return gulp.src(config.src) 9 | .pipe(changed(config.dest)) // Ignore unchanged files 10 | .pipe(imagemin()) // Optimize 11 | .pipe(gulp.dest(config.dest)) 12 | .pipe(browserSync.reload({stream:true})); 13 | }); 14 | -------------------------------------------------------------------------------- /view/gulp/tasks/inline.js: -------------------------------------------------------------------------------- 1 | var gulp = require('gulp'); 2 | var config = require('../config').production; 3 | var inline = require('gulp-inline-source'); 4 | var size = require('gulp-filesize'); 5 | 6 | gulp.task('inline', ['markup', 'images', 'iconFont', 'minifyCss', 'replace-pre'], function() { 7 | var options = { 8 | 'compress' : false 9 | }; 10 | 11 | return gulp.src(config.htmlSrc) 12 | .pipe(inline(options)) 13 | .pipe(gulp.dest(config.dest)) 14 | .pipe(size()); 15 | }); 16 | -------------------------------------------------------------------------------- /view/gulp/tasks/karma.js: -------------------------------------------------------------------------------- 1 | var gulp = require('gulp'); 2 | var karma = require('karma'); 3 | 4 | var karmaTask = function(done) { 5 | karma.server.start({ 6 | configFile: process.cwd() + '/karma.conf.js', 7 | singleRun: true 8 | }, done); 9 | }; 10 | 11 | gulp.task('karma', karmaTask); 12 | 13 | module.exports = karmaTask; 14 | -------------------------------------------------------------------------------- /view/gulp/tasks/markup.js: -------------------------------------------------------------------------------- 1 | var gulp = require('gulp'); 2 | var config = require('../config').markup; 3 | var browserSync = require('browser-sync'); 4 | 5 | gulp.task('markup', function() { 6 | return gulp.src(config.src) 7 | .pipe(gulp.dest(config.dest)) 8 | .pipe(browserSync.reload({stream:true})); 9 | }); 10 | -------------------------------------------------------------------------------- /view/gulp/tasks/minifyCss.js: -------------------------------------------------------------------------------- 1 | var gulp = require('gulp'); 2 | var config = require('../config').production; 3 | var minifyCSS = require('gulp-minify-css'); 4 | var size = require('gulp-filesize'); 5 | 6 | gulp.task('minifyCss', ['sass'], function() { 7 | return gulp.src(config.cssSrc) 8 | .pipe(minifyCSS({keepBreaks:true})) 9 | .pipe(gulp.dest(config.dest)) 10 | .pipe(size()); 11 | }); -------------------------------------------------------------------------------- /view/gulp/tasks/production.js: -------------------------------------------------------------------------------- 1 | var gulp = require('gulp'); 2 | 3 | // Run this to compress all the things! 4 | gulp.task('production', ['replace-post']); 5 | -------------------------------------------------------------------------------- /view/gulp/tasks/removedep.js: -------------------------------------------------------------------------------- 1 | var gulp = require('gulp'); 2 | var config = require('../config').production; 3 | var size = require('gulp-filesize'); 4 | var replace = require('gulp-html-replace'); 5 | 6 | gulp.task('removedep', ['replace-post'], function() { 7 | return gulp.src(config.htmlSrc) 8 | .pipe(replace({bootstrap: ''})) 9 | .pipe(gulp.dest(config.dest)) 10 | .pipe(size()); 11 | }); 12 | -------------------------------------------------------------------------------- /view/gulp/tasks/replace.js: -------------------------------------------------------------------------------- 1 | var gulp = require('gulp'); 2 | var config = require('../config').production; 3 | var size = require('gulp-filesize'); 4 | var replace = require('gulp-replace'); 5 | 6 | gulp.task('replace-pre', ['browserify'], function() { 7 | return gulp.src(config.jsSrc) 8 | .pipe(replace('//replace-series', "var series = '$#series'")) 9 | .pipe(replace('//replace-options', "var options = '$#options'")) 10 | .pipe(replace('//replace-save', "var save = '$#save'")) 11 | .pipe(replace('//replace-url', "var url = '$#url'")) 12 | .pipe(replace('//replace-highstock', "var useHighStock = '$#highstock'")) 13 | .pipe(replace('//replace-path', "var path = '$#path'")) 14 | .pipe(replace('//replace-height', "var height = '$#height'")) 15 | .pipe(replace('//replace-width', "var width = '$#width'")) 16 | .pipe(replace('//replace-settings', "var settingsFile = '$#settingsFile'")) 17 | .pipe(gulp.dest(config.dest)) 18 | .pipe(size()); 19 | }); 20 | 21 | gulp.task('replace-post', ['inline'], function() { 22 | return gulp.src(config.htmlSrc) 23 | .pipe(replace("'$#series'", "$#series")) 24 | .pipe(replace("'$#options'", "$#options")) 25 | .pipe(replace("'$#highstock'", "$#highstock")) 26 | .pipe(replace("'$#save'", "$#save")) 27 | .pipe(replace("'$#height'", "$#height")) 28 | .pipe(replace("'$#width'", "$#width")) 29 | .pipe(replace("'$#url'", "$#url")) 30 | .pipe(replace("'$#path'", "$#path")) 31 | .pipe(replace("'$#settingsFile'", "$#settingsFile")) 32 | .pipe(gulp.dest(config.dest)) 33 | .pipe(size()); 34 | }); 35 | -------------------------------------------------------------------------------- /view/gulp/tasks/sass.js: -------------------------------------------------------------------------------- 1 | var gulp = require('gulp'); 2 | var browserSync = require('browser-sync'); 3 | var sass = require('gulp-sass'); 4 | var sourcemaps = require('gulp-sourcemaps'); 5 | var handleErrors = require('../util/handleErrors'); 6 | var config = require('../config').sass; 7 | var autoprefixer = require('gulp-autoprefixer'); 8 | 9 | gulp.task('sass', function () { 10 | return gulp.src(config.src) 11 | .pipe(sourcemaps.init()) 12 | .pipe(sass(config.settings)) 13 | .on('error', handleErrors) 14 | .pipe(sourcemaps.write()) 15 | .pipe(autoprefixer({ browsers: ['last 2 version'] })) 16 | .pipe(gulp.dest(config.dest)) 17 | .pipe(browserSync.reload({stream:true})); 18 | }); 19 | -------------------------------------------------------------------------------- /view/gulp/tasks/test.js: -------------------------------------------------------------------------------- 1 | var gulp = require('gulp'); 2 | 3 | // Run this to compress all the things! 4 | gulp.task('production', ['inline']); 5 | -------------------------------------------------------------------------------- /view/gulp/tasks/uglifyJs.js: -------------------------------------------------------------------------------- 1 | var gulp = require('gulp'); 2 | var config = require('../config').production; 3 | var size = require('gulp-filesize'); 4 | var uglify = require('gulp-uglify'); 5 | 6 | gulp.task('uglifyJs', ['browserify', 'replace'], function() { 7 | return gulp.src(config.jsSrc) 8 | .pipe(uglify()) 9 | .pipe(gulp.dest(config.dest)) 10 | .pipe(size()); 11 | }); 12 | -------------------------------------------------------------------------------- /view/gulp/tasks/watch.js: -------------------------------------------------------------------------------- 1 | /* Notes: 2 | - gulp/tasks/browserify.js handles js recompiling with watchify 3 | - gulp/tasks/browserSync.js watches and reloads compiled files 4 | */ 5 | 6 | var gulp = require('gulp'); 7 | var config = require('../config'); 8 | 9 | gulp.task('watch', ['watchify','browserSync'], function() { 10 | gulp.watch(config.sass.src, ['sass']); 11 | gulp.watch(config.images.src, ['images']); 12 | gulp.watch(config.markup.src, ['markup']); 13 | // Watchify will watch and recompile our JS, so no need to gulp.watch it 14 | }); 15 | -------------------------------------------------------------------------------- /view/gulp/tasks/watchify.js: -------------------------------------------------------------------------------- 1 | var gulp = require('gulp'); 2 | var browserifyTask = require('./browserify'); 3 | 4 | gulp.task('watchify', function() { 5 | // Start browserify task with devMode === true 6 | return browserifyTask(true); 7 | }); 8 | -------------------------------------------------------------------------------- /view/gulp/util/bundleLogger.js: -------------------------------------------------------------------------------- 1 | /* bundleLogger 2 | ------------ 3 | Provides gulp style logs to the bundle method in browserify.js 4 | */ 5 | 6 | var gutil = require('gulp-util'); 7 | var prettyHrtime = require('pretty-hrtime'); 8 | var startTime; 9 | 10 | module.exports = { 11 | start: function(filepath) { 12 | startTime = process.hrtime(); 13 | gutil.log('Bundling', gutil.colors.green(filepath) + '...'); 14 | }, 15 | 16 | watch: function(bundleName) { 17 | gutil.log('Watching files required by', gutil.colors.yellow(bundleName)); 18 | }, 19 | 20 | end: function(filepath) { 21 | var taskTime = process.hrtime(startTime); 22 | var prettyTime = prettyHrtime(taskTime); 23 | gutil.log('Bundled', gutil.colors.green(filepath), 'in', gutil.colors.magenta(prettyTime)); 24 | } 25 | }; 26 | -------------------------------------------------------------------------------- /view/gulp/util/handleErrors.js: -------------------------------------------------------------------------------- 1 | var notify = require("gulp-notify"); 2 | 3 | module.exports = function() { 4 | 5 | var args = Array.prototype.slice.call(arguments); 6 | 7 | // Send error to notification center with gulp-notify 8 | notify.onError({ 9 | title: "Compile Error", 10 | message: "<%= error %>" 11 | }).apply(this, args); 12 | 13 | // Keep gulp from hanging on this task 14 | this.emit('end'); 15 | }; -------------------------------------------------------------------------------- /view/gulpfile.js: -------------------------------------------------------------------------------- 1 | /* 2 | gulpfile.js 3 | =========== 4 | Rather than manage one giant configuration file responsible 5 | for creating multiple tasks, each task has been broken out into 6 | its own file in gulp/tasks. Any files in that directory get 7 | automatically required below. 8 | 9 | To add a new task, simply add a new task file that directory. 10 | gulp/tasks/default.js specifies the default set of tasks to run 11 | when you run `gulp`. 12 | */ 13 | 14 | var requireDir = require('require-dir'); 15 | 16 | // Require all tasks in gulp/tasks, including subfolders 17 | requireDir('./gulp/tasks', { recurse: true }); 18 | -------------------------------------------------------------------------------- /view/karma.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration 2 | // Generated on Fri Jan 23 2015 17:22:58 GMT-0500 (EST) 3 | 4 | module.exports = function(config) { 5 | config.set({ 6 | 7 | // base path that will be used to resolve all patterns (eg. files, exclude) 8 | basePath: '', 9 | 10 | // frameworks to use 11 | // available frameworks: https://npmjs.org/browse/keyword/karma-adapter 12 | frameworks: ['mocha', 'sinon-chai', 'browserify'], 13 | 14 | // list of files / patterns to load in the browser 15 | files: [ 16 | 'src/javascript/**/__tests__/*' 17 | ], 18 | 19 | // list of files to exclude 20 | exclude: [ 21 | ], 22 | 23 | // preprocess matching files before serving them to the browser 24 | // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor 25 | preprocessors: { 26 | 'src/javascript/**/__tests__/*': ['browserify'] 27 | }, 28 | 29 | browserify: { 30 | debug: true, 31 | extensions: ['.js', '.coffee', '.hbs'] 32 | }, 33 | 34 | // test results reporter to use 35 | // possible values: 'dots', 'progress' 36 | // available reporters: https://npmjs.org/browse/keyword/karma-reporter 37 | reporters: ['nyan'], 38 | 39 | // web server port 40 | port: 9876, 41 | 42 | // enable / disable colors in the output (reporters and logs) 43 | colors: true, 44 | 45 | // level of logging 46 | // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG 47 | logLevel: config.LOG_INFO, 48 | 49 | // enable / disable watching file and executing tests whenever any file changes 50 | autoWatch: true, 51 | 52 | // start these browsers 53 | // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher 54 | browsers: ['Chrome'], 55 | 56 | // Continuous Integration mode 57 | // if true, Karma captures browsers, runs the tests and exits 58 | singleRun: false, 59 | 60 | // Helps to address an issue on TravisCI where activity can time out 61 | browserNoActivityTimeout: 30000 62 | 63 | }); 64 | }; 65 | -------------------------------------------------------------------------------- /view/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gulp-starter", 3 | "version": "0.1.1", 4 | "description": "Gulp starter with common tasks and scenarios", 5 | "repository": { 6 | "type": "git", 7 | "url": "git://github.com/greypants/gulp-starter.git" 8 | }, 9 | "//": [ 10 | "The following 'underscore' example demonstrates exposing a module included ", 11 | "by another module. If you were to npm install underscore separately and ", 12 | "require('underscore'), you'd end up with two copies in your bundle. The one", 13 | "you installed, and the one that shipped with another package (backbone in ", 14 | "this example). This is an edge case and should rarely happen.", 15 | "", 16 | "The 'plugin' example makes that file requireable with `require('plugin')`,", 17 | "and available to browserify-shim as 'plugin' on line 30." 18 | ], 19 | "browserify": { 20 | "transform": [ 21 | "browserify-shim", 22 | "coffeeify", 23 | "hbsfy" 24 | ] 25 | }, 26 | "browserify-shim": { 27 | "plugin": { 28 | "exports": "plugin", 29 | "depends": [ 30 | "jquery:$" 31 | ] 32 | }, 33 | "bootstrap": { 34 | "depends": [ 35 | "jquery:jQuery" 36 | ] 37 | } 38 | }, 39 | "devDependencies": { 40 | "browser-sync": "~2.2.2", 41 | "browserify": "^9.0.3", 42 | "browserify-shim": "^3.8.2", 43 | "coffeeify": "~1.0.0", 44 | "gulp": "^3.8.11", 45 | "gulp-autoprefixer": "^2.1.0", 46 | "gulp-changed": "^1.1.1", 47 | "gulp-filesize": "0.0.6", 48 | "gulp-html-replace": "^1.5.0", 49 | "gulp-iconfont": "^1.0.0", 50 | "gulp-imagemin": "^2.2.1", 51 | "gulp-minify-css": "~0.5.1", 52 | "gulp-notify": "^2.2.0", 53 | "gulp-rename": "^1.2.0", 54 | "gulp-sass": "~1.3.3", 55 | "gulp-sourcemaps": "^1.5.0", 56 | "gulp-swig": "^0.7.4", 57 | "gulp-uglify": "^1.1.0", 58 | "gulp-util": "^3.0.4", 59 | "handlebars": "^3.0.0", 60 | "hbsfy": "~2.2.1", 61 | "karma": "^0.12.31", 62 | "karma-browserify": "^4.0.0", 63 | "karma-chrome-launcher": "^0.1.7", 64 | "karma-coffee-preprocessor": "^0.2.1", 65 | "karma-mocha": "^0.1.10", 66 | "karma-nyan-reporter": "0.0.51", 67 | "karma-sinon-chai": "^0.3.0", 68 | "lodash": "^3.3.1", 69 | "merge-stream": "^0.1.7", 70 | "pretty-hrtime": "~1.0.0", 71 | "require-dir": "^0.1.0", 72 | "vinyl-source-stream": "~1.0.0", 73 | "watchify": "^2.4.0" 74 | }, 75 | "dependencies": { 76 | "backbone": "~1.1.2", 77 | "bootstrap": "^3.3.4", 78 | "gulp-inline-source": "^1.3.0", 79 | "gulp-replace": "^0.5.3", 80 | "highstock-browserify": "^1.0.1", 81 | "jquery": "^2.1.4", 82 | "jsoneditor": "^4.2.0", 83 | "selectize": "^0.12.1", 84 | "underscore": "^1.8.3" 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /view/src/htdocs/index-async.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 |
    6 |
    7 |
    8 | 9 |
    10 |
    11 |
    12 | 13 | 14 |
    15 | 16 |
    17 |
    18 | 19 |
    20 |
    21 |
    22 |

    Adjust chart settings

    23 |
    24 |
    25 |
    26 |
    27 |
    28 |
    29 | 32 |
    33 |
    34 |
    35 |
    36 |
    37 | 39 |
    .json
    40 |
    41 |
    42 | 43 |
    44 | 45 |
    46 |
    47 |
    48 |
    49 |
    50 |
    51 | 52 |
    53 |
    54 |
    55 |
    56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | -------------------------------------------------------------------------------- /view/src/htdocs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 |
    6 |
    7 |
    8 | 9 |
    10 |
    11 | 12 |
    13 |
    14 | 15 |
    16 |
    17 |
    18 |

    Adjust chart settings

    19 |
    20 |
    21 |
    22 |
    23 |
    24 |
    25 | 28 |
    29 |
    30 |
    31 |
    32 |
    33 | 35 |
    .json
    36 |
    37 |
    38 | 39 |
    40 | 41 |
    42 |
    43 |
    44 |
    45 |
    46 |
    47 | 48 |
    49 |
    50 |
    51 |
    52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /view/src/icons/uE001-facebook.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /view/src/icons/uE002-linkedin.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /view/src/icons/uE003-pinterest.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /view/src/icons/uE004-twitter.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /view/src/images/gulp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arnoutaertgeerts/python-highcharts/6bd433cf53e851e9697639e88fcec453b7b63076/view/src/images/gulp.png -------------------------------------------------------------------------------- /view/src/images/jsoneditor-icons.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arnoutaertgeerts/python-highcharts/6bd433cf53e851e9697639e88fcec453b7b63076/view/src/images/jsoneditor-icons.png -------------------------------------------------------------------------------- /view/src/javascript/__tests__/page-test.coffee: -------------------------------------------------------------------------------- 1 | require '../page' 2 | $ = require 'jquery' 3 | 4 | describe 'page.js', -> 5 | it 'contains a love letter', -> 6 | loveLetter = $('.love-letter').length 7 | loveLetter.should.equal 1 8 | -------------------------------------------------------------------------------- /view/src/javascript/async.js: -------------------------------------------------------------------------------- 1 | 2 | requirejs([ 3 | 'jquery', 4 | 'selectize', 5 | 'jsoneditor', 6 | 'highstock', 7 | 'export' 8 | ], function($, selectize, JSONEditor) { 9 | 10 | function guid() { 11 | function s4() { 12 | return Math.floor((1 + Math.random()) * 0x10000) 13 | .toString(16) 14 | .substring(1); 15 | } 16 | return s4() + s4() + '-' + s4() + '-' + s4() + '-' + 17 | s4() + '-' + s4() + s4() + s4(); 18 | } 19 | 20 | var id = guid(); 21 | plot(id); 22 | 23 | function plot(id) { 24 | var options = {}; 25 | //replace-options 26 | var useHighStock = true; 27 | //replace-highstock 28 | var save = 'app/chart.svg'; 29 | //replace-save 30 | var url = 'http://127.0.0.1:37759'; 31 | //replace-url 32 | var path = 'small'; 33 | //replace-path 34 | var settingsFile = 'settings'; 35 | //replace-settings 36 | 37 | //Create different containers for the charts 38 | var chartContainer = document.getElementById("chart-container"); 39 | chartContainer.id = "chart-container" + id; 40 | chartContainer.style.height = options.height.toString() + 'px'; 41 | 42 | var selectorContainer = $("#variable-selector"); 43 | selectorContainer.attr('id', "variable-selector" + id); 44 | 45 | var settings = $("#settings-collapse"); 46 | settings.attr('id', "settings-collapse" + id); 47 | 48 | var button = $("#settings-button"); 49 | button.attr('id', "settings-button" + id); 50 | 51 | var saveButton = $("#save-settings"); 52 | saveButton.attr('id', "save-settings" + id); 53 | 54 | var optionsInput = $("#options-input"); 55 | optionsInput.attr('id', "options-input" + id); 56 | optionsInput.val(settingsFile); 57 | 58 | var optionsButton = $("#options-button"); 59 | optionsButton.attr('id', "options-button" + id); 60 | 61 | var updateButton = $("#update-button"); 62 | updateButton.attr('id', "update-button" + id); 63 | 64 | var liveToggle = $("#live-toggle"); 65 | liveToggle.attr('id', "live-toggle" + id); 66 | 67 | // create the editor 68 | var editorContainer = document.getElementById("jsoneditor"); 69 | editorContainer.id = "jsoneditor" + id; 70 | var editor = new JSONEditor(editorContainer); 71 | 72 | updateButton.on('click', update); 73 | button.on('click', showSettings); 74 | saveButton.on('click', applyOptions); 75 | optionsButton.on('click', saveOptions); 76 | 77 | function applyOptions() { 78 | var newOptions = editor.get(); 79 | setOptions(newOptions); 80 | settings.collapse('hide'); 81 | } 82 | 83 | function saveOptions(event) { 84 | event.preventDefault(); 85 | 86 | applyOptions(); 87 | 88 | var options = chart.options; 89 | delete options.exporting; 90 | 91 | var name = optionsInput.val() ? optionsInput.val() + '.json' : 'settings.json'; 92 | 93 | options['settingsFile'] = name; 94 | 95 | $.ajax({ 96 | type: "POST", 97 | url: url, 98 | data: JSON.stringify({ 99 | options: options, 100 | name: name 101 | }) 102 | }); 103 | } 104 | 105 | function showSettings() { 106 | settings.collapse('toggle'); 107 | } 108 | 109 | //Choose a chart type 110 | var ChartType = useHighStock ? Highcharts.StockChart : Highcharts.Chart; 111 | 112 | //Default highchart colors 113 | var colors = ['#7cb5ec', '#434348', '#90ed7d', '#f7a35c', '#8085e9', 114 | '#f15c80', '#e4d354', '#2b908f', '#f45b5b', '#91e8e1']; 115 | var colorIndex = 0; 116 | 117 | var keys = []; 118 | 119 | //Set initial chart options 120 | var chartOptions; 121 | if (typeof options.chart === "undefined") { 122 | chartOptions = {renderTo: chartContainer.id}; 123 | } else { 124 | chartOptions = $.extend(options["chart"], {renderTo: chartContainer.id}); 125 | } 126 | 127 | //Initial rendered series 128 | var renderedSeries = []; 129 | var cachedSeries = []; 130 | options = $.extend(options, {chart: chartOptions}, {series: renderedSeries}); 131 | var chart = new ChartType(options); 132 | editor.set(chart.options); 133 | 134 | selectorContainer.selectize({ 135 | plugins: ['remove_button', 'restore_on_backspace'], 136 | delimiter: ',', 137 | options: [], 138 | onItemAdd: function (key) { 139 | console.log('series added'); 140 | addSeries(key).done(function() { 141 | newChart(chart.options, renderedSeries) 142 | }); 143 | }, 144 | onItemRemove: function (key) { 145 | console.log('series removed'); 146 | deleteSeries(key); 147 | newChart(chart.options, renderedSeries); 148 | }, 149 | onInitialize: update 150 | }); 151 | 152 | var selector = selectorContainer[0].selectize; 153 | 154 | //Check for new keys 155 | function update() { 156 | $.get(path + '/keys.json').done(function (data) { 157 | //Get the new keys 158 | var oldKeys = keys.map(function (element) { 159 | return element.name 160 | }); 161 | 162 | //Get the new key objects 163 | var newKeys = data.filter(function (element) { 164 | return oldKeys.indexOf(element.name) == -1 165 | }); 166 | 167 | //Set the keys equal to all keys and update the selector 168 | keys = data; 169 | 170 | var showNewKeys = newKeys.filter(function(obj) { 171 | return obj.display == true; 172 | }); 173 | 174 | selector.addOption(keys); 175 | 176 | $.each(showNewKeys, function(index, obj) { 177 | selector.addItem(obj.value) 178 | }) 179 | }) 180 | } 181 | 182 | function setOptions(options) { 183 | //Prevent export from breaking 184 | delete options.exporting; 185 | options['exporting'] = {scale: options.scale}; 186 | 187 | chartContainer.style.height = options.height.toString() + 'px'; 188 | 189 | if (options.width != 'auto') { 190 | chartContainer.style.width = options.width.toString() + 'px'; 191 | } 192 | 193 | newChart(options, renderedSeries); 194 | 195 | } 196 | 197 | function findSeries(series, key) { 198 | return series.findIndex(function (obj) { 199 | return obj.name == key; 200 | }) 201 | } 202 | 203 | function newChart(options, series) { 204 | //Disable animation 205 | var newOptions = $.extend(options, {series: series}); 206 | newOptions.plotOptions['series'] = {animation: false}; 207 | 208 | //Get zoom 209 | var xExtremes = chart.xAxis[0].getExtremes(); 210 | 211 | //Re-plot the chart 212 | chart.destroy(); 213 | chart = new ChartType(newOptions); 214 | 215 | //Reset the zoom 216 | chart.xAxis[0].setExtremes(xExtremes.min, xExtremes.max, false, false); 217 | 218 | //Re-draw chart 219 | chart.redraw(); 220 | } 221 | 222 | function addSeries(key) { 223 | 224 | var index = findSeries(cachedSeries, key); 225 | if (index > -1) { 226 | var newSeries = cachedSeries[index]; 227 | renderedSeries.push(newSeries); 228 | return $.when() 229 | } else { 230 | return $.get(path + '/' + key + '.json').done(function (data) { 231 | if (key.color) { 232 | data['color'] = key.color 233 | } else { 234 | data['color'] = colors[colorIndex % 10]; 235 | colorIndex = colorIndex + 1; 236 | } 237 | 238 | renderedSeries.push(data); 239 | cachedSeries.push(data); 240 | return null; 241 | }); 242 | } 243 | } 244 | 245 | function deleteSeries(key) { 246 | var index = findSeries(renderedSeries, key); 247 | renderedSeries.splice(index, 1) 248 | } 249 | 250 | function saveSVG(url, name) { 251 | $.ajax({ 252 | type: "POST", 253 | url: url, 254 | data: JSON.stringify({ 255 | svg: chart.getSVG(), 256 | name: name 257 | }) 258 | }); 259 | } 260 | } 261 | }); 262 | -------------------------------------------------------------------------------- /view/src/javascript/lib.js: -------------------------------------------------------------------------------- 1 | //findIndex polyfill 2 | if (!Array.prototype.findIndex) { 3 | Array.prototype.findIndex = function(predicate) { 4 | if (this == null) { 5 | throw new TypeError('Array.prototype.findIndex called on null or undefined'); 6 | } 7 | if (typeof predicate !== 'function') { 8 | throw new TypeError('predicate must be a function'); 9 | } 10 | var list = Object(this); 11 | var length = list.length >>> 0; 12 | var thisArg = arguments[1]; 13 | var value; 14 | 15 | for (var i = 0; i < length; i++) { 16 | value = list[i]; 17 | if (predicate.call(thisArg, value, i, list)) { 18 | return i; 19 | } 20 | } 21 | return -1; 22 | }; 23 | } 24 | 25 | if (!Array.prototype.filter) { 26 | Array.prototype.filter = function(fun/*, thisArg*/) { 27 | 'use strict'; 28 | 29 | if (this === void 0 || this === null) { 30 | throw new TypeError(); 31 | } 32 | 33 | var t = Object(this); 34 | var len = t.length >>> 0; 35 | if (typeof fun !== 'function') { 36 | throw new TypeError(); 37 | } 38 | 39 | var res = []; 40 | var thisArg = arguments.length >= 2 ? arguments[1] : void 0; 41 | for (var i = 0; i < len; i++) { 42 | if (i in t) { 43 | var val = t[i]; 44 | 45 | // NOTE: Technically this should Object.defineProperty at 46 | // the next index, as push can be affected by 47 | // properties on Object.prototype and Array.prototype. 48 | // But that method's new, and collisions should be 49 | // rare, so use the more-compatible alternative. 50 | if (fun.call(thisArg, val, i, t)) { 51 | res.push(val); 52 | } 53 | } 54 | } 55 | 56 | return res; 57 | }; 58 | } 59 | 60 | function guid() { 61 | function s4() { 62 | return Math.floor((1 + Math.random()) * 0x10000) 63 | .toString(16) 64 | .substring(1); 65 | } 66 | return s4() + s4() + '-' + s4() + '-' + s4() + '-' + 67 | s4() + '-' + s4() + s4() + s4(); 68 | } 69 | 70 | Array.prototype.equals = function (array) { 71 | // if the other array is a false value, return 72 | if (!array) 73 | return false; 74 | 75 | // compare lengths - can save a lot of time 76 | if (this.length != array.length) 77 | return false; 78 | 79 | for (var i = 0, l = this.length; i < l; i++) { 80 | // Check if we have nested arrays 81 | if (this[i] instanceof Array && array[i] instanceof Array) { 82 | // recurse into the nested arrays 83 | if (!this[i].equals(array[i])) 84 | return false; 85 | } 86 | else if (this[i] != array[i]) { 87 | // Warning - two different object instances will never be equal: {x:20} != {x:20} 88 | return false; 89 | } 90 | } 91 | return true; 92 | }; 93 | -------------------------------------------------------------------------------- /view/src/javascript/page.js: -------------------------------------------------------------------------------- 1 | //Count the number of charts on the page 2 | if (window.counter == undefined) { 3 | window.counter = 0; 4 | } else { 5 | window.counter++; 6 | } 7 | 8 | requirejs([ 9 | 'jquery', 10 | 'selectize', 11 | 'jsoneditor', 12 | 'highstock', 13 | 'export', 14 | 'more' 15 | ], function ($, selectize, JSONEditor) { 16 | 17 | function guid() { 18 | function s4() { 19 | return Math.floor((1 + Math.random()) * 0x10000) 20 | .toString(16) 21 | .substring(1); 22 | } 23 | 24 | return s4() + s4() + '-' + s4() + '-' + s4() + '-' + 25 | s4() + '-' + s4() + s4() + s4(); 26 | } 27 | 28 | var id = guid(); 29 | plot(id); 30 | 31 | function plot(id) { 32 | var series = [ 33 | {data: [1, 2, 4, 9], name: "temperature 1", display: true, color: '#2b908f'}, 34 | {data: [9, 4, 2, 1], name: "temperature 2", display: true}, 35 | {data: [0, 3, 5, 6], name: "temperature 3", display: false} 36 | ]; 37 | //replace-series 38 | var options = {}; 39 | //replace-options 40 | var useHighStock = false; 41 | //replace-highstock 42 | var save = 'app/chart.svg'; 43 | //replace-save 44 | var url = 'http://127.0.0.1:65079'; 45 | //replace-url 46 | var settingsFile = 'settings'; 47 | //replace-settings 48 | var scale = options.scale; 49 | 50 | //Create different containers for the charts 51 | var chartContainer = document.getElementById("chart-container"); 52 | chartContainer.id = "chart-container" + id; 53 | chartContainer.style.height = options.height.toString() + 'px'; 54 | 55 | if (options.width != 'auto') { 56 | chartContainer.style.width = options.width.toString() + 'px'; 57 | } 58 | 59 | var selector = $("#variable-selector"); 60 | selector.attr('id', "variable-selector" + id); 61 | 62 | var settings = $("#settings-collapse"); 63 | settings.attr('id', "settings-collapse" + id); 64 | 65 | var button = $("#settings-button"); 66 | button.attr('id', "settings-button" + id); 67 | 68 | var saveButton = $("#save-settings"); 69 | saveButton.attr('id', "save-settings" + id); 70 | 71 | var optionsInput = $("#options-input"); 72 | optionsInput.attr('id', "options-input" + id); 73 | optionsInput.val(settingsFile); 74 | 75 | var optionsButton = $("#options-button"); 76 | optionsButton.attr('id', "options-button" + id); 77 | 78 | // create the editor 79 | var editorContainer = document.getElementById("jsoneditor"); 80 | editorContainer.id = "jsoneditor" + id; 81 | var editor = new JSONEditor(editorContainer); 82 | 83 | button.on('click', showSettings); 84 | saveButton.on('click', applyOptions); 85 | optionsButton.on('click', saveOptions); 86 | 87 | function applyOptions() { 88 | var newOptions = editor.get(); 89 | setOptions(newOptions); 90 | settings.collapse('hide'); 91 | } 92 | 93 | function saveOptions(event) { 94 | event.preventDefault(); 95 | 96 | applyOptions(); 97 | 98 | var options = chart.options; 99 | delete options.exporting; 100 | 101 | var name = optionsInput.val() ? optionsInput.val() + '.json' : 'settings.json'; 102 | 103 | options['settingsFile'] = name; 104 | 105 | $.ajax({ 106 | type: "POST", 107 | url: url, 108 | data: JSON.stringify({ 109 | options: options, 110 | name: name 111 | }) 112 | }); 113 | } 114 | 115 | function showSettings() { 116 | settings.collapse('toggle'); 117 | } 118 | 119 | //Choose a chart type 120 | var ChartType = useHighStock ? Highcharts.StockChart : Highcharts.Chart; 121 | 122 | //Default highchart colors 123 | var colors = ['#7cb5ec', '#434348', '#90ed7d', '#f7a35c', '#8085e9', 124 | '#f15c80', '#e4d354', '#2b908f', '#f45b5b', '#91e8e1']; 125 | 126 | series.map(function (serie, index) { 127 | if (!serie.color) { 128 | serie['color'] = colors[index % 10]; 129 | } 130 | 131 | return serie; 132 | }); 133 | 134 | //Get all the keys 135 | var keys = []; 136 | var initialKeys = []; 137 | $.each(series, function (index, serie) { 138 | keys.push({ 139 | display: serie.display, 140 | value: serie.name, 141 | text: serie.name 142 | }); 143 | 144 | if (serie.display) { 145 | initialKeys.push(serie.name) 146 | } 147 | }); 148 | 149 | selector.selectize({ 150 | plugins: ['remove_button', 'restore_on_backspace'], 151 | delimiter: ',', 152 | options: keys, 153 | items: initialKeys, 154 | onItemAdd: function (key) { 155 | console.log('series added'); 156 | addSeries(key); 157 | newChart(chart.options, renderedSeries); 158 | }, 159 | onItemRemove: function (key) { 160 | console.log('series removed'); 161 | deleteSeries(key); 162 | newChart(chart.options, renderedSeries); 163 | } 164 | }); 165 | 166 | //Set initial chart options 167 | var chartOptions; 168 | if (typeof options.chart === "undefined") { 169 | chartOptions = {renderTo: chartContainer.id}; 170 | } else { 171 | chartOptions = $.extend(options["chart"], {renderTo: chartContainer.id}); 172 | } 173 | 174 | //Initial rendered series 175 | var renderedSeries = []; 176 | options = $.extend(options, {chart: chartOptions}, {series: renderedSeries}); 177 | var chart = new ChartType(options); 178 | 179 | $.each(initialKeys, function (index, key) { 180 | addSeries(key); 181 | }); 182 | 183 | newChart(chart.options, renderedSeries); 184 | editor.set(chart.options); 185 | 186 | if (save) { 187 | saveSVG(url, save) 188 | } 189 | 190 | function setOptions(options) { 191 | //Prevent export from breaking 192 | delete options.exporting; 193 | options['exporting'] = {scale: options.scale}; 194 | 195 | chartContainer.style.height = options.height.toString() + 'px'; 196 | 197 | if (options.width != 'auto') { 198 | chartContainer.style.width = options.width.toString() + 'px'; 199 | } 200 | 201 | newChart(options, renderedSeries); 202 | 203 | } 204 | 205 | function findSeries(series, key) { 206 | return series.findIndex(function (obj) { 207 | return obj.name == key; 208 | }) 209 | } 210 | 211 | function newChart(options, series) { 212 | //Disable animation 213 | var newOptions = $.extend(options, {series: series}); 214 | newOptions.plotOptions['series'] = {animation: false}; 215 | 216 | //Get zoom 217 | var xExtremes = chart.xAxis[0].getExtremes(); 218 | 219 | //Re-plot the chart 220 | chart.destroy(); 221 | chart = new ChartType(newOptions); 222 | 223 | //Reset the zoom 224 | chart.xAxis[0].setExtremes(xExtremes.min, xExtremes.max, false, false); 225 | 226 | //Re-draw chart 227 | chart.redraw(); 228 | } 229 | 230 | function addSeries(key) { 231 | var index = findSeries(series, key); 232 | var newSeries = series[index]; 233 | renderedSeries.push(newSeries) 234 | } 235 | 236 | function deleteSeries(key) { 237 | var index = findSeries(renderedSeries, key); 238 | renderedSeries.splice(index, 1) 239 | } 240 | 241 | function saveSVG(url, name) { 242 | $.ajax({ 243 | type: "POST", 244 | url: url, 245 | data: JSON.stringify({ 246 | svg: chart.getSVG(), 247 | name: name 248 | }) 249 | }); 250 | } 251 | 252 | console.log('loaded!', Date()); 253 | } 254 | 255 | }); 256 | -------------------------------------------------------------------------------- /view/src/javascript/require.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | requirejs.config({ 4 | "paths": { 5 | "highstock": "https://cdnjs.cloudflare.com/ajax/libs/highstock/2.1.5/highstock", 6 | "export": "https://cdnjs.cloudflare.com/ajax/libs/highstock/2.1.5/modules/exporting", 7 | "more": "https://cdnjs.cloudflare.com/ajax/libs/highstock/2.1.7/highcharts-more", 8 | "jsoneditor": "https://cdnjs.cloudflare.com/ajax/libs/jsoneditor/4.2.0/jsoneditor.min", 9 | "selectize": "https://cdnjs.cloudflare.com/ajax/libs/selectize.js/0.12.1/js/standalone/selectize.min", 10 | "jquery": "https://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.4/jquery.min" 11 | }, 12 | "shim": { 13 | "export": ["highstock"], 14 | "more": ["highstock"] 15 | } 16 | }); 17 | 18 | //Define jquery here to use the pre-loaded version 19 | define('jquery', [], function() { 20 | return jQuery; 21 | }); 22 | -------------------------------------------------------------------------------- /view/src/javascript/vendor/jquery-plugin.js: -------------------------------------------------------------------------------- 1 | window.plugin = function() { 2 | $('body').append('

    This line was generated by a non common-js plugin that depends on jQuery!

    '); 3 | }; 4 | -------------------------------------------------------------------------------- /view/src/sass/_icons.sass: -------------------------------------------------------------------------------- 1 | // DO NOT EDIT DIRECTLY! 2 | Generated by gulp/tasks/iconFont.js 3 | from ./gulp/tasks/iconFont/template.sass.swig 4 | 5 | @font-face 6 | font-family: Post-Creator-Icons 7 | src: url("fonts/Post-Creator-Icons.eot") 8 | src: url("fonts/Post-Creator-Icons.eot?#iefix") format('embedded-opentype'), url("fonts/Post-Creator-Icons.woff") format('woff'), url("fonts/Post-Creator-Icons.ttf") format('truetype'), url("fonts/Post-Creator-Icons.svg#Post-Creator-Icons") format('svg') 9 | font-weight: normal 10 | font-style: normal 11 | 12 | =icon($content) 13 | &:before 14 | -moz-osx-font-smoothing: grayscale 15 | -webkit-font-smoothing: antialiased 16 | content: $content 17 | font-family: 'Post-Creator-Icons' 18 | font-style: normal 19 | font-variant: normal 20 | font-weight: normal 21 | line-height: 1 22 | speak: none 23 | text-transform: none 24 | @content 25 | 26 | =icon--facebook 27 | +icon("\e001") 28 | @content 29 | 30 | .icon 31 | &.-facebook 32 | +icon--facebook 33 | 34 | =icon--linkedin 35 | +icon("\e002") 36 | @content 37 | 38 | .icon 39 | &.-linkedin 40 | +icon--linkedin 41 | 42 | =icon--pinterest 43 | +icon("\e003") 44 | @content 45 | 46 | .icon 47 | &.-pinterest 48 | +icon--pinterest 49 | 50 | =icon--twitter 51 | +icon("\e004") 52 | @content 53 | 54 | .icon 55 | &.-twitter 56 | +icon--twitter 57 | 58 | 59 | -------------------------------------------------------------------------------- /view/src/sass/_scss-mixins.scss: -------------------------------------------------------------------------------- 1 | @mixin font-smoothing { 2 | -moz-osx-font-smoothing: grayscale; 3 | -webkit-font-smoothing: antialiased; 4 | } 5 | -------------------------------------------------------------------------------- /view/src/sass/_typography.sass: -------------------------------------------------------------------------------- 1 | body 2 | +font-smoothing // <- _mixins.scss 3 | color: #555 4 | font-family: sans-serif 5 | 6 | small 7 | font-weight: normal 8 | display: block 9 | font-size: 14px 10 | 11 | code 12 | background-color: lightgrey 13 | border-radius: 3px 14 | font-family: monospace 15 | padding: 0 .5em 16 | -------------------------------------------------------------------------------- /view/src/sass/app.sass: -------------------------------------------------------------------------------- 1 | @import scss-mixins 2 | @import typography 3 | @import icons 4 | 5 | //@import selectize/selectize.default 6 | //@import jsoneditor/jsoneditor.min 7 | 8 | .social-icons 9 | h4 10 | display: inline-block 11 | margin: 20px 10px 0 0 12 | 13 | .icon 14 | display: inline-block 15 | margin: 0 5px 16 | 17 | body.modal-open 18 | overflow: hidden 19 | 20 | .jsoneditor td.tree, .jsoneditor td, .jsoneditor tr, .jsoneditor table 21 | border: none 22 | margin: 0 23 | -------------------------------------------------------------------------------- /view/src/sass/jsoneditor/_jsoneditor.min.scss: -------------------------------------------------------------------------------- 1 | .jsoneditor .field,.jsoneditor .readonly,.jsoneditor .value{border:1px solid transparent;min-height:16px;min-width:32px;padding:2px;margin:1px;word-wrap:break-word;float:left}.jsoneditor .field p,.jsoneditor .value p{margin:0}.jsoneditor .value{word-break:break-word}.jsoneditor .readonly{min-width:16px;color:gray}.jsoneditor .empty{border-color:#d3d3d3;border-style:dashed;border-radius:2px}.jsoneditor .field.empty{background-image:url(images/jsoneditor-icons.png);background-position:0 -144px}.jsoneditor .value.empty{background-image:url(images/jsoneditor-icons.png);background-position:-48px -144px}.jsoneditor .value.url{color:green;text-decoration:underline}.jsoneditor a.value.url:focus,.jsoneditor a.value.url:hover{color:red}.jsoneditor .separator{padding:3px 0;vertical-align:top;color:gray}.jsoneditor .field.highlight,.jsoneditor .field[contenteditable=true]:focus,.jsoneditor .field[contenteditable=true]:hover,.jsoneditor .value.highlight,.jsoneditor .value[contenteditable=true]:focus,.jsoneditor .value[contenteditable=true]:hover{background-color:#FFFFAB;border:1px solid #ff0;border-radius:2px}.jsoneditor .field.highlight-active,.jsoneditor .field.highlight-active:focus,.jsoneditor .field.highlight-active:hover,.jsoneditor .value.highlight-active,.jsoneditor .value.highlight-active:focus,.jsoneditor .value.highlight-active:hover{background-color:#fe0;border:1px solid #ffc700;border-radius:2px}.jsoneditor div.tree button{width:24px;height:24px;padding:0;margin:0;border:none;cursor:pointer;background:url(images/jsoneditor-icons.png)}.jsoneditor div.tree button.collapsed{background-position:0 -48px}.jsoneditor div.tree button.expanded{background-position:0 -72px}.jsoneditor div.tree button.contextmenu{background-position:-48px -72px}.jsoneditor div.tree button.contextmenu.selected,.jsoneditor div.tree button.contextmenu:focus,.jsoneditor div.tree button.contextmenu:hover{background-position:-48px -48px}.jsoneditor div.tree :focus{outline:0}.jsoneditor div.tree button:focus{background-color:#f5f5f5;outline:#e5e5e5 solid 1px}.jsoneditor div.tree button.invisible{visibility:hidden;background:0 0}.jsoneditor{color:#1A1A1A;border:1px solid #97B0F8;box-sizing:border-box;width:100%;height:100%;overflow:auto;position:relative;padding:0;line-height:100%}.jsoneditor,.jsoneditor div.outer{-moz-box-sizing:border-box;-webkit-box-sizing:border-box}.jsoneditor div.tree table.tree{border-collapse:collapse;border-spacing:0;width:100%;margin:0}.jsoneditor div.outer{width:100%;height:100%;margin:-35px 0 0;padding:35px 0 0;box-sizing:border-box;overflow:hidden}.jsoneditor div.tree{width:100%;height:100%;position:relative;overflow:auto}.jsoneditor textarea.text{width:100%;height:100%;margin:0;box-sizing:border-box;border:none;background-color:#fff;resize:none}.jsoneditor .menu,.jsoneditor textarea.text{-moz-box-sizing:border-box;-webkit-box-sizing:border-box}.jsoneditor tr.highlight{background-color:#FFFFAB}.jsoneditor div.tree button.dragarea{background:url(images/jsoneditor-icons.png)-72px -72px;cursor:move}.jsoneditor div.tree button.dragarea:focus,.jsoneditor div.tree button.dragarea:hover{background-position:-72px -48px}.jsoneditor td,.jsoneditor th,.jsoneditor tr{padding:0;margin:0}.jsoneditor td,.jsoneditor td.tree{vertical-align:top}.jsoneditor .field,.jsoneditor .value,.jsoneditor td,.jsoneditor textarea,.jsoneditor th{font-family:droid sans mono,monospace,courier new,courier,sans-serif;font-size:10pt;color:#1A1A1A}.jsoneditor-contextmenu{position:absolute;z-index:99999}.jsoneditor-contextmenu ul{position:relative;left:0;top:0;width:124px;background:#fff;border:1px solid #d3d3d3;box-shadow:2px 2px 12px rgba(128,128,128,.3);list-style:none;margin:0;padding:0}.jsoneditor-contextmenu ul li button{padding:0;margin:0;width:124px;height:24px;border:none;cursor:pointer;color:#4d4d4d;background:0 0;line-height:26px;text-align:left}.jsoneditor-contextmenu ul li button::-moz-focus-inner{padding:0;border:0}.jsoneditor-contextmenu ul li button:focus,.jsoneditor-contextmenu ul li button:hover{color:#1a1a1a;background-color:#f5f5f5;outline:0}.jsoneditor-contextmenu ul li button.default{width:92px}.jsoneditor-contextmenu ul li button.expand{float:right;width:32px;height:24px;border-left:1px solid #e5e5e5}.jsoneditor-contextmenu div.icon{float:left;width:24px;height:24px;border:none;padding:0;margin:0;background-image:url(images/jsoneditor-icons.png)}.jsoneditor-contextmenu ul li button div.expand{float:right;width:24px;height:24px;padding:0;margin:0 4px 0 0;background:url(images/jsoneditor-icons.png)0 -72px;opacity:.4}.jsoneditor-contextmenu ul li button.expand:focus div.expand,.jsoneditor-contextmenu ul li button.expand:hover div.expand,.jsoneditor-contextmenu ul li button:focus div.expand,.jsoneditor-contextmenu ul li button:hover div.expand,.jsoneditor-contextmenu ul li.selected div.expand{opacity:1}.jsoneditor-contextmenu .separator{height:0;border-top:1px solid #e5e5e5;padding-top:5px;margin-top:5px}.jsoneditor-contextmenu button.remove>.icon{background-position:-24px -24px}.jsoneditor-contextmenu button.remove:focus>.icon,.jsoneditor-contextmenu button.remove:hover>.icon{background-position:-24px 0}.jsoneditor-contextmenu button.append>.icon{background-position:0 -24px}.jsoneditor-contextmenu button.append:focus>.icon,.jsoneditor-contextmenu button.append:hover>.icon{background-position:0 0}.jsoneditor-contextmenu button.insert>.icon{background-position:0 -24px}.jsoneditor-contextmenu button.insert:focus>.icon,.jsoneditor-contextmenu button.insert:hover>.icon{background-position:0 0}.jsoneditor-contextmenu button.duplicate>.icon{background-position:-48px -24px}.jsoneditor-contextmenu button.duplicate:focus>.icon,.jsoneditor-contextmenu button.duplicate:hover>.icon{background-position:-48px 0}.jsoneditor-contextmenu button.sort-asc>.icon{background-position:-168px -24px}.jsoneditor-contextmenu button.sort-asc:focus>.icon,.jsoneditor-contextmenu button.sort-asc:hover>.icon{background-position:-168px 0}.jsoneditor-contextmenu button.sort-desc>.icon{background-position:-192px -24px}.jsoneditor-contextmenu button.sort-desc:focus>.icon,.jsoneditor-contextmenu button.sort-desc:hover>.icon{background-position:-192px 0}.jsoneditor-contextmenu ul li .selected{background-color:#D5DDF6}.jsoneditor-contextmenu ul li{overflow:hidden}.jsoneditor-contextmenu ul li ul{display:none;position:relative;left:-10px;top:0;border:none;box-shadow:inset 0 0 10px rgba(128,128,128,.5);padding:0 10px;-webkit-transition:all .3s ease-out;-moz-transition:all .3s ease-out;-o-transition:all .3s ease-out;transition:all .3s ease-out}.jsoneditor-contextmenu ul li ul li button{padding-left:24px}.jsoneditor-contextmenu ul li ul li button:focus,.jsoneditor-contextmenu ul li ul li button:hover{background-color:#f5f5f5}.jsoneditor-contextmenu button.type-string>.icon{background-position:-144px -24px}.jsoneditor-contextmenu button.type-string.selected>.icon,.jsoneditor-contextmenu button.type-string:focus>.icon,.jsoneditor-contextmenu button.type-string:hover>.icon{background-position:-144px 0}.jsoneditor-contextmenu button.type-auto>.icon{background-position:-120px -24px}.jsoneditor-contextmenu button.type-auto.selected>.icon,.jsoneditor-contextmenu button.type-auto:focus>.icon,.jsoneditor-contextmenu button.type-auto:hover>.icon{background-position:-120px 0}.jsoneditor-contextmenu button.type-object>.icon{background-position:-72px -24px}.jsoneditor-contextmenu button.type-object.selected>.icon,.jsoneditor-contextmenu button.type-object:focus>.icon,.jsoneditor-contextmenu button.type-object:hover>.icon{background-position:-72px 0}.jsoneditor-contextmenu button.type-array>.icon{background-position:-96px -24px}.jsoneditor-contextmenu button.type-array.selected>.icon,.jsoneditor-contextmenu button.type-array:focus>.icon,.jsoneditor-contextmenu button.type-array:hover>.icon{background-position:-96px 0}.jsoneditor-contextmenu button.type-modes>.icon{background-image:none;width:6px}.jsoneditor .menu{width:100%;height:35px;padding:2px;margin:0;overflow:hidden;box-sizing:border-box;color:#1A1A1A;background-color:#D5DDF6;border-bottom:1px solid #97B0F8}.jsoneditor .menu button{width:26px;height:26px;margin:2px;padding:0;border-radius:2px;border:1px solid #aec0f8;background:url(images/jsoneditor-icons.png)#e3eaf6;color:#4D4D4D;opacity:.8;font-family:arial,sans-serif;font-size:10pt;float:left}.jsoneditor .menu button:hover{background-color:#f0f2f5}.jsoneditor .menu button:active,.jsoneditor .menu button:focus{background-color:#fff}.jsoneditor .menu button:disabled{background-color:#e3eaf6}.jsoneditor .menu button.collapse-all{background-position:0 -96px}.jsoneditor .menu button.expand-all{background-position:0 -120px}.jsoneditor .menu button.undo{background-position:-24px -96px}.jsoneditor .menu button.undo:disabled{background-position:-24px -120px}.jsoneditor .menu button.redo{background-position:-48px -96px}.jsoneditor .menu button.redo:disabled{background-position:-48px -120px}.jsoneditor .menu button.compact{background-position:-72px -96px}.jsoneditor .menu button.format{background-position:-72px -120px}.jsoneditor .menu button.modes{background-image:none;width:auto;padding-left:6px;padding-right:6px}.jsoneditor .menu button.separator{margin-left:10px}.jsoneditor .menu a{font-family:arial,sans-serif;font-size:10pt;color:#97B0F8;vertical-align:middle}.jsoneditor .menu a:hover{color:red}.jsoneditor .menu a.poweredBy{font-size:8pt;position:absolute;right:0;top:0;padding:10px}.jsoneditor .search .results,.jsoneditor .search input{font-family:arial,sans-serif;font-size:10pt;color:#1A1A1A;background:0 0}.jsoneditor .search{position:absolute;right:2px;top:2px}.jsoneditor .search .frame{border:1px solid #97B0F8;background-color:#fff;padding:0 2px;margin:0}.jsoneditor .search .frame table{border-collapse:collapse}.jsoneditor .search input{width:120px;border:none;outline:0;margin:1px}.jsoneditor .search .results{color:#4d4d4d;padding-right:5px;line-height:24px}.jsoneditor .search button{width:16px;height:24px;padding:0;margin:0;border:none;background:url(images/jsoneditor-icons.png);vertical-align:top}.jsoneditor .search button:hover{background-color:transparent}.jsoneditor .search button.refresh{width:18px;background-position:-99px -73px}.jsoneditor .search button.next{cursor:pointer;background-position:-124px -73px}.jsoneditor .search button.next:hover{background-position:-124px -49px}.jsoneditor .search button.previous{cursor:pointer;background-position:-148px -73px;margin-right:2px}.jsoneditor .search button.previous:hover{background-position:-148px -49px} -------------------------------------------------------------------------------- /view/src/sass/selectize/_selectize.bootstrap3.scss: -------------------------------------------------------------------------------- 1 | /** 2 | * selectize.bootstrap3.css (v0.12.1) - Bootstrap 3 Theme 3 | * Copyright (c) 2013–2015 Brian Reavis & contributors 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this 6 | * file except in compliance with the License. You may obtain a copy of the License at: 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under 10 | * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 11 | * ANY KIND, either express or implied. See the License for the specific language 12 | * governing permissions and limitations under the License. 13 | * 14 | * @author Brian Reavis 15 | */ 16 | .selectize-control.plugin-drag_drop.multi > .selectize-input > div.ui-sortable-placeholder { 17 | visibility: visible !important; 18 | background: #f2f2f2 !important; 19 | background: rgba(0, 0, 0, 0.06) !important; 20 | border: 0 none !important; 21 | -webkit-box-shadow: inset 0 0 12px 4px #ffffff; 22 | box-shadow: inset 0 0 12px 4px #ffffff; 23 | } 24 | .selectize-control.plugin-drag_drop .ui-sortable-placeholder::after { 25 | content: '!'; 26 | visibility: hidden; 27 | } 28 | .selectize-control.plugin-drag_drop .ui-sortable-helper { 29 | -webkit-box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2); 30 | box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2); 31 | } 32 | .selectize-dropdown-header { 33 | position: relative; 34 | padding: 3px 12px; 35 | border-bottom: 1px solid #d0d0d0; 36 | background: #f8f8f8; 37 | -webkit-border-radius: 4px 4px 0 0; 38 | -moz-border-radius: 4px 4px 0 0; 39 | border-radius: 4px 4px 0 0; 40 | } 41 | .selectize-dropdown-header-close { 42 | position: absolute; 43 | right: 12px; 44 | top: 50%; 45 | color: #333333; 46 | opacity: 0.4; 47 | margin-top: -12px; 48 | line-height: 20px; 49 | font-size: 20px !important; 50 | } 51 | .selectize-dropdown-header-close:hover { 52 | color: #000000; 53 | } 54 | .selectize-dropdown.plugin-optgroup_columns .optgroup { 55 | border-right: 1px solid #f2f2f2; 56 | border-top: 0 none; 57 | float: left; 58 | -webkit-box-sizing: border-box; 59 | -moz-box-sizing: border-box; 60 | box-sizing: border-box; 61 | } 62 | .selectize-dropdown.plugin-optgroup_columns .optgroup:last-child { 63 | border-right: 0 none; 64 | } 65 | .selectize-dropdown.plugin-optgroup_columns .optgroup:before { 66 | display: none; 67 | } 68 | .selectize-dropdown.plugin-optgroup_columns .optgroup-header { 69 | border-top: 0 none; 70 | } 71 | .selectize-control.plugin-remove_button [data-value] { 72 | position: relative; 73 | padding-right: 24px !important; 74 | } 75 | .selectize-control.plugin-remove_button [data-value] .remove { 76 | z-index: 1; 77 | /* fixes ie bug (see #392) */ 78 | position: absolute; 79 | top: 0; 80 | right: 0; 81 | bottom: 0; 82 | width: 17px; 83 | text-align: center; 84 | font-weight: bold; 85 | font-size: 12px; 86 | color: inherit; 87 | text-decoration: none; 88 | vertical-align: middle; 89 | display: inline-block; 90 | padding: 1px 0 0 0; 91 | border-left: 1px solid rgba(0, 0, 0, 0); 92 | -webkit-border-radius: 0 2px 2px 0; 93 | -moz-border-radius: 0 2px 2px 0; 94 | border-radius: 0 2px 2px 0; 95 | -webkit-box-sizing: border-box; 96 | -moz-box-sizing: border-box; 97 | box-sizing: border-box; 98 | } 99 | .selectize-control.plugin-remove_button [data-value] .remove:hover { 100 | background: rgba(0, 0, 0, 0.05); 101 | } 102 | .selectize-control.plugin-remove_button [data-value].active .remove { 103 | border-left-color: rgba(0, 0, 0, 0); 104 | } 105 | .selectize-control.plugin-remove_button .disabled [data-value] .remove:hover { 106 | background: none; 107 | } 108 | .selectize-control.plugin-remove_button .disabled [data-value] .remove { 109 | border-left-color: rgba(77, 77, 77, 0); 110 | } 111 | .selectize-control { 112 | position: relative; 113 | } 114 | .selectize-dropdown, 115 | .selectize-input, 116 | .selectize-input input { 117 | color: #333333; 118 | font-family: inherit; 119 | font-size: inherit; 120 | line-height: 20px; 121 | -webkit-font-smoothing: inherit; 122 | } 123 | .selectize-input, 124 | .selectize-control.single .selectize-input.input-active { 125 | background: #ffffff; 126 | cursor: text; 127 | display: inline-block; 128 | } 129 | .selectize-input { 130 | border: 1px solid #cccccc; 131 | padding: 6px 12px; 132 | display: inline-block; 133 | width: 100%; 134 | overflow: hidden; 135 | position: relative; 136 | z-index: 1; 137 | -webkit-box-sizing: border-box; 138 | -moz-box-sizing: border-box; 139 | box-sizing: border-box; 140 | -webkit-box-shadow: none; 141 | box-shadow: none; 142 | -webkit-border-radius: 4px; 143 | -moz-border-radius: 4px; 144 | border-radius: 4px; 145 | } 146 | .selectize-control.multi .selectize-input.has-items { 147 | padding: 5px 12px 2px; 148 | } 149 | .selectize-input.full { 150 | background-color: #ffffff; 151 | } 152 | .selectize-input.disabled, 153 | .selectize-input.disabled * { 154 | cursor: default !important; 155 | } 156 | .selectize-input.focus { 157 | -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.15); 158 | box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.15); 159 | } 160 | .selectize-input.dropdown-active { 161 | -webkit-border-radius: 4px 4px 0 0; 162 | -moz-border-radius: 4px 4px 0 0; 163 | border-radius: 4px 4px 0 0; 164 | } 165 | .selectize-input > * { 166 | vertical-align: baseline; 167 | display: -moz-inline-stack; 168 | display: inline-block; 169 | zoom: 1; 170 | *display: inline; 171 | } 172 | .selectize-control.multi .selectize-input > div { 173 | cursor: pointer; 174 | margin: 0 3px 3px 0; 175 | padding: 1px 3px; 176 | background: #efefef; 177 | color: #333333; 178 | border: 0 solid rgba(0, 0, 0, 0); 179 | } 180 | .selectize-control.multi .selectize-input > div.active { 181 | background: #428bca; 182 | color: #ffffff; 183 | border: 0 solid rgba(0, 0, 0, 0); 184 | } 185 | .selectize-control.multi .selectize-input.disabled > div, 186 | .selectize-control.multi .selectize-input.disabled > div.active { 187 | color: #808080; 188 | background: #ffffff; 189 | border: 0 solid rgba(77, 77, 77, 0); 190 | } 191 | .selectize-input > input { 192 | display: inline-block !important; 193 | padding: 0 !important; 194 | min-height: 0 !important; 195 | max-height: none !important; 196 | max-width: 100% !important; 197 | margin: 0 !important; 198 | text-indent: 0 !important; 199 | border: 0 none !important; 200 | background: none !important; 201 | line-height: inherit !important; 202 | -webkit-user-select: auto !important; 203 | -webkit-box-shadow: none !important; 204 | box-shadow: none !important; 205 | } 206 | .selectize-input > input::-ms-clear { 207 | display: none; 208 | } 209 | .selectize-input > input:focus { 210 | outline: none !important; 211 | } 212 | .selectize-input::after { 213 | content: ' '; 214 | display: block; 215 | clear: left; 216 | } 217 | .selectize-input.dropdown-active::before { 218 | content: ' '; 219 | display: block; 220 | position: absolute; 221 | background: #ffffff; 222 | height: 1px; 223 | bottom: 0; 224 | left: 0; 225 | right: 0; 226 | } 227 | .selectize-dropdown { 228 | position: absolute; 229 | z-index: 10; 230 | border: 1px solid #d0d0d0; 231 | background: #ffffff; 232 | margin: -1px 0 0 0; 233 | border-top: 0 none; 234 | -webkit-box-sizing: border-box; 235 | -moz-box-sizing: border-box; 236 | box-sizing: border-box; 237 | -webkit-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); 238 | box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); 239 | -webkit-border-radius: 0 0 4px 4px; 240 | -moz-border-radius: 0 0 4px 4px; 241 | border-radius: 0 0 4px 4px; 242 | } 243 | .selectize-dropdown [data-selectable] { 244 | cursor: pointer; 245 | overflow: hidden; 246 | } 247 | .selectize-dropdown [data-selectable] .highlight { 248 | background: rgba(255, 237, 40, 0.4); 249 | -webkit-border-radius: 1px; 250 | -moz-border-radius: 1px; 251 | border-radius: 1px; 252 | } 253 | .selectize-dropdown [data-selectable], 254 | .selectize-dropdown .optgroup-header { 255 | padding: 3px 12px; 256 | } 257 | .selectize-dropdown .optgroup:first-child .optgroup-header { 258 | border-top: 0 none; 259 | } 260 | .selectize-dropdown .optgroup-header { 261 | color: #777777; 262 | background: #ffffff; 263 | cursor: default; 264 | } 265 | .selectize-dropdown .active { 266 | background-color: #f5f5f5; 267 | color: #262626; 268 | } 269 | .selectize-dropdown .active.create { 270 | color: #262626; 271 | } 272 | .selectize-dropdown .create { 273 | color: rgba(51, 51, 51, 0.5); 274 | } 275 | .selectize-dropdown-content { 276 | overflow-y: auto; 277 | overflow-x: hidden; 278 | max-height: 200px; 279 | } 280 | .selectize-control.single .selectize-input, 281 | .selectize-control.single .selectize-input input { 282 | cursor: pointer; 283 | } 284 | .selectize-control.single .selectize-input.input-active, 285 | .selectize-control.single .selectize-input.input-active input { 286 | cursor: text; 287 | } 288 | .selectize-control.single .selectize-input:after { 289 | content: ' '; 290 | display: block; 291 | position: absolute; 292 | top: 50%; 293 | right: 17px; 294 | margin-top: -3px; 295 | width: 0; 296 | height: 0; 297 | border-style: solid; 298 | border-width: 5px 5px 0 5px; 299 | border-color: #333333 transparent transparent transparent; 300 | } 301 | .selectize-control.single .selectize-input.dropdown-active:after { 302 | margin-top: -4px; 303 | border-width: 0 5px 5px 5px; 304 | border-color: transparent transparent #333333 transparent; 305 | } 306 | .selectize-control.rtl.single .selectize-input:after { 307 | left: 17px; 308 | right: auto; 309 | } 310 | .selectize-control.rtl .selectize-input > input { 311 | margin: 0 4px 0 -2px !important; 312 | } 313 | .selectize-control .selectize-input.disabled { 314 | opacity: 0.5; 315 | background-color: #ffffff; 316 | } 317 | .selectize-dropdown, 318 | .selectize-dropdown.form-control { 319 | height: auto; 320 | padding: 0; 321 | margin: 2px 0 0 0; 322 | z-index: 1000; 323 | background: #ffffff; 324 | border: 1px solid #cccccc; 325 | border: 1px solid rgba(0, 0, 0, 0.15); 326 | -webkit-border-radius: 4px; 327 | -moz-border-radius: 4px; 328 | border-radius: 4px; 329 | -webkit-box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175); 330 | box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175); 331 | } 332 | .selectize-dropdown .optgroup-header { 333 | font-size: 12px; 334 | line-height: 1.42857143; 335 | } 336 | .selectize-dropdown .optgroup:first-child:before { 337 | display: none; 338 | } 339 | .selectize-dropdown .optgroup:before { 340 | content: ' '; 341 | display: block; 342 | height: 1px; 343 | margin: 9px 0; 344 | overflow: hidden; 345 | background-color: #e5e5e5; 346 | margin-left: -12px; 347 | margin-right: -12px; 348 | } 349 | .selectize-dropdown-content { 350 | padding: 5px 0; 351 | } 352 | .selectize-dropdown-header { 353 | padding: 6px 12px; 354 | } 355 | .selectize-input { 356 | min-height: 34px; 357 | } 358 | .selectize-input.dropdown-active { 359 | -webkit-border-radius: 4px; 360 | -moz-border-radius: 4px; 361 | border-radius: 4px; 362 | } 363 | .selectize-input.dropdown-active::before { 364 | display: none; 365 | } 366 | .selectize-input.focus { 367 | border-color: #66afe9; 368 | outline: 0; 369 | -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102, 175, 233, 0.6); 370 | box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102, 175, 233, 0.6); 371 | } 372 | .has-error .selectize-input { 373 | border-color: #a94442; 374 | -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); 375 | box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); 376 | } 377 | .has-error .selectize-input:focus { 378 | border-color: #843534; 379 | -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #ce8483; 380 | box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #ce8483; 381 | } 382 | .selectize-control.multi .selectize-input.has-items { 383 | padding-left: 9px; 384 | padding-right: 9px; 385 | } 386 | .selectize-control.multi .selectize-input > div { 387 | -webkit-border-radius: 3px; 388 | -moz-border-radius: 3px; 389 | border-radius: 3px; 390 | } 391 | .form-control.selectize-control { 392 | padding: 0; 393 | height: auto; 394 | border: none; 395 | background: none; 396 | -webkit-box-shadow: none; 397 | box-shadow: none; 398 | -webkit-border-radius: 0; 399 | -moz-border-radius: 0; 400 | border-radius: 0; 401 | } 402 | -------------------------------------------------------------------------------- /view/src/sass/selectize/_selectize.default.scss: -------------------------------------------------------------------------------- 1 | /** 2 | * selectize.default.css (v0.12.1) - Default Theme 3 | * Copyright (c) 2013–2015 Brian Reavis & contributors 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this 6 | * file except in compliance with the License. You may obtain a copy of the License at: 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under 10 | * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 11 | * ANY KIND, either express or implied. See the License for the specific language 12 | * governing permissions and limitations under the License. 13 | * 14 | * @author Brian Reavis 15 | */ 16 | .selectize-control.plugin-drag_drop.multi > .selectize-input > div.ui-sortable-placeholder { 17 | visibility: visible !important; 18 | background: #f2f2f2 !important; 19 | background: rgba(0, 0, 0, 0.06) !important; 20 | border: 0 none !important; 21 | -webkit-box-shadow: inset 0 0 12px 4px #ffffff; 22 | box-shadow: inset 0 0 12px 4px #ffffff; 23 | } 24 | .selectize-control.plugin-drag_drop .ui-sortable-placeholder::after { 25 | content: '!'; 26 | visibility: hidden; 27 | } 28 | .selectize-control.plugin-drag_drop .ui-sortable-helper { 29 | -webkit-box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2); 30 | box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2); 31 | } 32 | .selectize-dropdown-header { 33 | position: relative; 34 | padding: 5px 8px; 35 | border-bottom: 1px solid #d0d0d0; 36 | background: #f8f8f8; 37 | -webkit-border-radius: 3px 3px 0 0; 38 | -moz-border-radius: 3px 3px 0 0; 39 | border-radius: 3px 3px 0 0; 40 | } 41 | .selectize-dropdown-header-close { 42 | position: absolute; 43 | right: 8px; 44 | top: 50%; 45 | color: #303030; 46 | opacity: 0.4; 47 | margin-top: -12px; 48 | line-height: 20px; 49 | font-size: 20px !important; 50 | } 51 | .selectize-dropdown-header-close:hover { 52 | color: #000000; 53 | } 54 | .selectize-dropdown.plugin-optgroup_columns .optgroup { 55 | border-right: 1px solid #f2f2f2; 56 | border-top: 0 none; 57 | float: left; 58 | -webkit-box-sizing: border-box; 59 | -moz-box-sizing: border-box; 60 | box-sizing: border-box; 61 | } 62 | .selectize-dropdown.plugin-optgroup_columns .optgroup:last-child { 63 | border-right: 0 none; 64 | } 65 | .selectize-dropdown.plugin-optgroup_columns .optgroup:before { 66 | display: none; 67 | } 68 | .selectize-dropdown.plugin-optgroup_columns .optgroup-header { 69 | border-top: 0 none; 70 | } 71 | .selectize-control.plugin-remove_button [data-value] { 72 | position: relative; 73 | padding-right: 24px !important; 74 | } 75 | .selectize-control.plugin-remove_button [data-value] .remove { 76 | z-index: 1; 77 | /* fixes ie bug (see #392) */ 78 | position: absolute; 79 | top: 0; 80 | right: 0; 81 | bottom: 0; 82 | width: 17px; 83 | text-align: center; 84 | font-weight: bold; 85 | font-size: 12px; 86 | color: inherit; 87 | text-decoration: none; 88 | vertical-align: middle; 89 | display: inline-block; 90 | padding: 2px 0 0 0; 91 | border-left: 1px solid #0073bb; 92 | -webkit-border-radius: 0 2px 2px 0; 93 | -moz-border-radius: 0 2px 2px 0; 94 | border-radius: 0 2px 2px 0; 95 | -webkit-box-sizing: border-box; 96 | -moz-box-sizing: border-box; 97 | box-sizing: border-box; 98 | } 99 | .selectize-control.plugin-remove_button [data-value] .remove:hover { 100 | background: rgba(0, 0, 0, 0.05); 101 | } 102 | .selectize-control.plugin-remove_button [data-value].active .remove { 103 | border-left-color: #00578d; 104 | } 105 | .selectize-control.plugin-remove_button .disabled [data-value] .remove:hover { 106 | background: none; 107 | } 108 | .selectize-control.plugin-remove_button .disabled [data-value] .remove { 109 | border-left-color: #aaaaaa; 110 | } 111 | .selectize-control { 112 | position: relative; 113 | } 114 | .selectize-dropdown, 115 | .selectize-input, 116 | .selectize-input input { 117 | color: #303030; 118 | font-family: inherit; 119 | font-size: 13px; 120 | line-height: 18px; 121 | -webkit-font-smoothing: inherit; 122 | } 123 | .selectize-input, 124 | .selectize-control.single .selectize-input.input-active { 125 | background: #ffffff; 126 | cursor: text; 127 | display: inline-block; 128 | } 129 | .selectize-input { 130 | border: 1px solid #d0d0d0; 131 | padding: 8px 8px; 132 | display: inline-block; 133 | width: 100%; 134 | overflow: hidden; 135 | position: relative; 136 | z-index: 1; 137 | -webkit-box-sizing: border-box; 138 | -moz-box-sizing: border-box; 139 | box-sizing: border-box; 140 | -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.1); 141 | box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.1); 142 | -webkit-border-radius: 3px; 143 | -moz-border-radius: 3px; 144 | border-radius: 3px; 145 | } 146 | .selectize-control.multi .selectize-input.has-items { 147 | padding: 5px 8px 2px; 148 | } 149 | .selectize-input.full { 150 | background-color: #ffffff; 151 | } 152 | .selectize-input.disabled, 153 | .selectize-input.disabled * { 154 | cursor: default !important; 155 | } 156 | .selectize-input.focus { 157 | -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.15); 158 | box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.15); 159 | } 160 | .selectize-input.dropdown-active { 161 | -webkit-border-radius: 3px 3px 0 0; 162 | -moz-border-radius: 3px 3px 0 0; 163 | border-radius: 3px 3px 0 0; 164 | } 165 | .selectize-input > * { 166 | vertical-align: baseline; 167 | display: -moz-inline-stack; 168 | display: inline-block; 169 | zoom: 1; 170 | *display: inline; 171 | } 172 | .selectize-control.multi .selectize-input > div { 173 | cursor: pointer; 174 | margin: 0 3px 3px 0; 175 | padding: 2px 6px; 176 | background: #1da7ee; 177 | color: #ffffff; 178 | border: 1px solid #0073bb; 179 | } 180 | .selectize-control.multi .selectize-input > div.active { 181 | background: #92c836; 182 | color: #ffffff; 183 | border: 1px solid #00578d; 184 | } 185 | .selectize-control.multi .selectize-input.disabled > div, 186 | .selectize-control.multi .selectize-input.disabled > div.active { 187 | color: #ffffff; 188 | background: #d2d2d2; 189 | border: 1px solid #aaaaaa; 190 | } 191 | .selectize-input > input { 192 | display: inline-block !important; 193 | padding: 0 !important; 194 | min-height: 0 !important; 195 | max-height: none !important; 196 | max-width: 100% !important; 197 | margin: 0 1px !important; 198 | text-indent: 0 !important; 199 | border: 0 none !important; 200 | background: none !important; 201 | line-height: inherit !important; 202 | -webkit-user-select: auto !important; 203 | -webkit-box-shadow: none !important; 204 | box-shadow: none !important; 205 | } 206 | .selectize-input > input::-ms-clear { 207 | display: none; 208 | } 209 | .selectize-input > input:focus { 210 | outline: none !important; 211 | } 212 | .selectize-input::after { 213 | content: ' '; 214 | display: block; 215 | clear: left; 216 | } 217 | .selectize-input.dropdown-active::before { 218 | content: ' '; 219 | display: block; 220 | position: absolute; 221 | background: #f0f0f0; 222 | height: 1px; 223 | bottom: 0; 224 | left: 0; 225 | right: 0; 226 | } 227 | .selectize-dropdown { 228 | position: absolute; 229 | z-index: 10; 230 | border: 1px solid #d0d0d0; 231 | background: #ffffff; 232 | margin: -1px 0 0 0; 233 | border-top: 0 none; 234 | -webkit-box-sizing: border-box; 235 | -moz-box-sizing: border-box; 236 | box-sizing: border-box; 237 | -webkit-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); 238 | box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); 239 | -webkit-border-radius: 0 0 3px 3px; 240 | -moz-border-radius: 0 0 3px 3px; 241 | border-radius: 0 0 3px 3px; 242 | } 243 | .selectize-dropdown [data-selectable] { 244 | cursor: pointer; 245 | overflow: hidden; 246 | } 247 | .selectize-dropdown [data-selectable] .highlight { 248 | background: rgba(125, 168, 208, 0.2); 249 | -webkit-border-radius: 1px; 250 | -moz-border-radius: 1px; 251 | border-radius: 1px; 252 | } 253 | .selectize-dropdown [data-selectable], 254 | .selectize-dropdown .optgroup-header { 255 | padding: 5px 8px; 256 | } 257 | .selectize-dropdown .optgroup:first-child .optgroup-header { 258 | border-top: 0 none; 259 | } 260 | .selectize-dropdown .optgroup-header { 261 | color: #303030; 262 | background: #ffffff; 263 | cursor: default; 264 | } 265 | .selectize-dropdown .active { 266 | background-color: #f5fafd; 267 | color: #495c68; 268 | } 269 | .selectize-dropdown .active.create { 270 | color: #495c68; 271 | } 272 | .selectize-dropdown .create { 273 | color: rgba(48, 48, 48, 0.5); 274 | } 275 | .selectize-dropdown-content { 276 | overflow-y: auto; 277 | overflow-x: hidden; 278 | max-height: 200px; 279 | } 280 | .selectize-control.single .selectize-input, 281 | .selectize-control.single .selectize-input input { 282 | cursor: pointer; 283 | } 284 | .selectize-control.single .selectize-input.input-active, 285 | .selectize-control.single .selectize-input.input-active input { 286 | cursor: text; 287 | } 288 | .selectize-control.single .selectize-input:after { 289 | content: ' '; 290 | display: block; 291 | position: absolute; 292 | top: 50%; 293 | right: 15px; 294 | margin-top: -3px; 295 | width: 0; 296 | height: 0; 297 | border-style: solid; 298 | border-width: 5px 5px 0 5px; 299 | border-color: #808080 transparent transparent transparent; 300 | } 301 | .selectize-control.single .selectize-input.dropdown-active:after { 302 | margin-top: -4px; 303 | border-width: 0 5px 5px 5px; 304 | border-color: transparent transparent #808080 transparent; 305 | } 306 | .selectize-control.rtl.single .selectize-input:after { 307 | left: 15px; 308 | right: auto; 309 | } 310 | .selectize-control.rtl .selectize-input > input { 311 | margin: 0 4px 0 -2px !important; 312 | } 313 | .selectize-control .selectize-input.disabled { 314 | opacity: 0.5; 315 | background-color: #fafafa; 316 | } 317 | .selectize-control.multi .selectize-input.has-items { 318 | padding-left: 5px; 319 | padding-right: 5px; 320 | } 321 | .selectize-control.multi .selectize-input.disabled [data-value] { 322 | color: #999; 323 | text-shadow: none; 324 | background: none; 325 | -webkit-box-shadow: none; 326 | box-shadow: none; 327 | } 328 | .selectize-control.multi .selectize-input.disabled [data-value], 329 | .selectize-control.multi .selectize-input.disabled [data-value] .remove { 330 | border-color: #e6e6e6; 331 | } 332 | .selectize-control.multi .selectize-input.disabled [data-value] .remove { 333 | background: none; 334 | } 335 | .selectize-control.multi .selectize-input [data-value] { 336 | text-shadow: 0 1px 0 rgba(0, 51, 83, 0.3); 337 | -webkit-border-radius: 3px; 338 | -moz-border-radius: 3px; 339 | border-radius: 3px; 340 | background-color: #1b9dec; 341 | background-image: -moz-linear-gradient(top, #1da7ee, #178ee9); 342 | background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#1da7ee), to(#178ee9)); 343 | background-image: -webkit-linear-gradient(top, #1da7ee, #178ee9); 344 | background-image: -o-linear-gradient(top, #1da7ee, #178ee9); 345 | background-image: linear-gradient(to bottom, #1da7ee, #178ee9); 346 | background-repeat: repeat-x; 347 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff1da7ee', endColorstr='#ff178ee9', GradientType=0); 348 | -webkit-box-shadow: 0 1px 0 rgba(0,0,0,0.2),inset 0 1px rgba(255,255,255,0.03); 349 | box-shadow: 0 1px 0 rgba(0,0,0,0.2),inset 0 1px rgba(255,255,255,0.03); 350 | } 351 | .selectize-control.multi .selectize-input [data-value].active { 352 | background-color: #0085d4; 353 | background-image: -moz-linear-gradient(top, #008fd8, #0075cf); 354 | background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#008fd8), to(#0075cf)); 355 | background-image: -webkit-linear-gradient(top, #008fd8, #0075cf); 356 | background-image: -o-linear-gradient(top, #008fd8, #0075cf); 357 | background-image: linear-gradient(to bottom, #008fd8, #0075cf); 358 | background-repeat: repeat-x; 359 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff008fd8', endColorstr='#ff0075cf', GradientType=0); 360 | } 361 | .selectize-control.single .selectize-input { 362 | -webkit-box-shadow: 0 1px 0 rgba(0,0,0,0.05), inset 0 1px 0 rgba(255,255,255,0.8); 363 | box-shadow: 0 1px 0 rgba(0,0,0,0.05), inset 0 1px 0 rgba(255,255,255,0.8); 364 | background-color: #f9f9f9; 365 | background-image: -moz-linear-gradient(top, #fefefe, #f2f2f2); 366 | background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#fefefe), to(#f2f2f2)); 367 | background-image: -webkit-linear-gradient(top, #fefefe, #f2f2f2); 368 | background-image: -o-linear-gradient(top, #fefefe, #f2f2f2); 369 | background-image: linear-gradient(to bottom, #fefefe, #f2f2f2); 370 | background-repeat: repeat-x; 371 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffefefe', endColorstr='#fff2f2f2', GradientType=0); 372 | } 373 | .selectize-control.single .selectize-input, 374 | .selectize-dropdown.single { 375 | border-color: #b8b8b8; 376 | } 377 | .selectize-dropdown .optgroup-header { 378 | padding-top: 7px; 379 | font-weight: bold; 380 | font-size: 0.85em; 381 | } 382 | .selectize-dropdown .optgroup { 383 | border-top: 1px solid #f0f0f0; 384 | } 385 | .selectize-dropdown .optgroup:first-child { 386 | border-top: 0 none; 387 | } 388 | -------------------------------------------------------------------------------- /view/src/sass/selectize/selectize.css: -------------------------------------------------------------------------------- 1 | /** 2 | * selectize.css (v0.12.1) 3 | * Copyright (c) 2013–2015 Brian Reavis & contributors 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this 6 | * file except in compliance with the License. You may obtain a copy of the License at: 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under 10 | * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 11 | * ANY KIND, either express or implied. See the License for the specific language 12 | * governing permissions and limitations under the License. 13 | * 14 | * @author Brian Reavis 15 | */ 16 | 17 | .selectize-control.plugin-drag_drop.multi > .selectize-input > div.ui-sortable-placeholder { 18 | visibility: visible !important; 19 | background: #f2f2f2 !important; 20 | background: rgba(0, 0, 0, 0.06) !important; 21 | border: 0 none !important; 22 | -webkit-box-shadow: inset 0 0 12px 4px #ffffff; 23 | box-shadow: inset 0 0 12px 4px #ffffff; 24 | } 25 | .selectize-control.plugin-drag_drop .ui-sortable-placeholder::after { 26 | content: '!'; 27 | visibility: hidden; 28 | } 29 | .selectize-control.plugin-drag_drop .ui-sortable-helper { 30 | -webkit-box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2); 31 | box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2); 32 | } 33 | .selectize-dropdown-header { 34 | position: relative; 35 | padding: 5px 8px; 36 | border-bottom: 1px solid #d0d0d0; 37 | background: #f8f8f8; 38 | -webkit-border-radius: 3px 3px 0 0; 39 | -moz-border-radius: 3px 3px 0 0; 40 | border-radius: 3px 3px 0 0; 41 | } 42 | .selectize-dropdown-header-close { 43 | position: absolute; 44 | right: 8px; 45 | top: 50%; 46 | color: #303030; 47 | opacity: 0.4; 48 | margin-top: -12px; 49 | line-height: 20px; 50 | font-size: 20px !important; 51 | } 52 | .selectize-dropdown-header-close:hover { 53 | color: #000000; 54 | } 55 | .selectize-dropdown.plugin-optgroup_columns .optgroup { 56 | border-right: 1px solid #f2f2f2; 57 | border-top: 0 none; 58 | float: left; 59 | -webkit-box-sizing: border-box; 60 | -moz-box-sizing: border-box; 61 | box-sizing: border-box; 62 | } 63 | .selectize-dropdown.plugin-optgroup_columns .optgroup:last-child { 64 | border-right: 0 none; 65 | } 66 | .selectize-dropdown.plugin-optgroup_columns .optgroup:before { 67 | display: none; 68 | } 69 | .selectize-dropdown.plugin-optgroup_columns .optgroup-header { 70 | border-top: 0 none; 71 | } 72 | .selectize-control.plugin-remove_button [data-value] { 73 | position: relative; 74 | padding-right: 24px !important; 75 | } 76 | .selectize-control.plugin-remove_button [data-value] .remove { 77 | z-index: 1; 78 | /* fixes ie bug (see #392) */ 79 | position: absolute; 80 | top: 0; 81 | right: 0; 82 | bottom: 0; 83 | width: 17px; 84 | text-align: center; 85 | font-weight: bold; 86 | font-size: 12px; 87 | color: inherit; 88 | text-decoration: none; 89 | vertical-align: middle; 90 | display: inline-block; 91 | padding: 2px 0 0 0; 92 | border-left: 1px solid #d0d0d0; 93 | -webkit-border-radius: 0 2px 2px 0; 94 | -moz-border-radius: 0 2px 2px 0; 95 | border-radius: 0 2px 2px 0; 96 | -webkit-box-sizing: border-box; 97 | -moz-box-sizing: border-box; 98 | box-sizing: border-box; 99 | } 100 | .selectize-control.plugin-remove_button [data-value] .remove:hover { 101 | background: rgba(0, 0, 0, 0.05); 102 | } 103 | .selectize-control.plugin-remove_button [data-value].active .remove { 104 | border-left-color: #cacaca; 105 | } 106 | .selectize-control.plugin-remove_button .disabled [data-value] .remove:hover { 107 | background: none; 108 | } 109 | .selectize-control.plugin-remove_button .disabled [data-value] .remove { 110 | border-left-color: #ffffff; 111 | } 112 | .selectize-control { 113 | position: relative; 114 | } 115 | .selectize-dropdown, 116 | .selectize-input, 117 | .selectize-input input { 118 | color: #303030; 119 | font-family: inherit; 120 | font-size: 13px; 121 | line-height: 18px; 122 | -webkit-font-smoothing: inherit; 123 | } 124 | .selectize-input, 125 | .selectize-control.single .selectize-input.input-active { 126 | background: #ffffff; 127 | cursor: text; 128 | display: inline-block; 129 | } 130 | .selectize-input { 131 | border: 1px solid #d0d0d0; 132 | padding: 8px 8px; 133 | display: inline-block; 134 | width: 100%; 135 | overflow: hidden; 136 | position: relative; 137 | z-index: 1; 138 | -webkit-box-sizing: border-box; 139 | -moz-box-sizing: border-box; 140 | box-sizing: border-box; 141 | -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.1); 142 | box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.1); 143 | -webkit-border-radius: 3px; 144 | -moz-border-radius: 3px; 145 | border-radius: 3px; 146 | } 147 | .selectize-control.multi .selectize-input.has-items { 148 | padding: 6px 8px 3px; 149 | } 150 | .selectize-input.full { 151 | background-color: #ffffff; 152 | } 153 | .selectize-input.disabled, 154 | .selectize-input.disabled * { 155 | cursor: default !important; 156 | } 157 | .selectize-input.focus { 158 | -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.15); 159 | box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.15); 160 | } 161 | .selectize-input.dropdown-active { 162 | -webkit-border-radius: 3px 3px 0 0; 163 | -moz-border-radius: 3px 3px 0 0; 164 | border-radius: 3px 3px 0 0; 165 | } 166 | .selectize-input > * { 167 | vertical-align: baseline; 168 | display: -moz-inline-stack; 169 | display: inline-block; 170 | zoom: 1; 171 | *display: inline; 172 | } 173 | .selectize-control.multi .selectize-input > div { 174 | cursor: pointer; 175 | margin: 0 3px 3px 0; 176 | padding: 2px 6px; 177 | background: #f2f2f2; 178 | color: #303030; 179 | border: 0 solid #d0d0d0; 180 | } 181 | .selectize-control.multi .selectize-input > div.active { 182 | background: #e8e8e8; 183 | color: #303030; 184 | border: 0 solid #cacaca; 185 | } 186 | .selectize-control.multi .selectize-input.disabled > div, 187 | .selectize-control.multi .selectize-input.disabled > div.active { 188 | color: #7d7d7d; 189 | background: #ffffff; 190 | border: 0 solid #ffffff; 191 | } 192 | .selectize-input > input { 193 | display: inline-block !important; 194 | padding: 0 !important; 195 | min-height: 0 !important; 196 | max-height: none !important; 197 | max-width: 100% !important; 198 | margin: 0 2px 0 0 !important; 199 | text-indent: 0 !important; 200 | border: 0 none !important; 201 | background: none !important; 202 | line-height: inherit !important; 203 | -webkit-user-select: auto !important; 204 | -webkit-box-shadow: none !important; 205 | box-shadow: none !important; 206 | } 207 | .selectize-input > input::-ms-clear { 208 | display: none; 209 | } 210 | .selectize-input > input:focus { 211 | outline: none !important; 212 | } 213 | .selectize-input::after { 214 | content: ' '; 215 | display: block; 216 | clear: left; 217 | } 218 | .selectize-input.dropdown-active::before { 219 | content: ' '; 220 | display: block; 221 | position: absolute; 222 | background: #f0f0f0; 223 | height: 1px; 224 | bottom: 0; 225 | left: 0; 226 | right: 0; 227 | } 228 | .selectize-dropdown { 229 | position: absolute; 230 | z-index: 10; 231 | border: 1px solid #d0d0d0; 232 | background: #ffffff; 233 | margin: -1px 0 0 0; 234 | border-top: 0 none; 235 | -webkit-box-sizing: border-box; 236 | -moz-box-sizing: border-box; 237 | box-sizing: border-box; 238 | -webkit-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); 239 | box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); 240 | -webkit-border-radius: 0 0 3px 3px; 241 | -moz-border-radius: 0 0 3px 3px; 242 | border-radius: 0 0 3px 3px; 243 | } 244 | .selectize-dropdown [data-selectable] { 245 | cursor: pointer; 246 | overflow: hidden; 247 | } 248 | .selectize-dropdown [data-selectable] .highlight { 249 | background: rgba(125, 168, 208, 0.2); 250 | -webkit-border-radius: 1px; 251 | -moz-border-radius: 1px; 252 | border-radius: 1px; 253 | } 254 | .selectize-dropdown [data-selectable], 255 | .selectize-dropdown .optgroup-header { 256 | padding: 5px 8px; 257 | } 258 | .selectize-dropdown .optgroup:first-child .optgroup-header { 259 | border-top: 0 none; 260 | } 261 | .selectize-dropdown .optgroup-header { 262 | color: #303030; 263 | background: #ffffff; 264 | cursor: default; 265 | } 266 | .selectize-dropdown .active { 267 | background-color: #f5fafd; 268 | color: #495c68; 269 | } 270 | .selectize-dropdown .active.create { 271 | color: #495c68; 272 | } 273 | .selectize-dropdown .create { 274 | color: rgba(48, 48, 48, 0.5); 275 | } 276 | .selectize-dropdown-content { 277 | overflow-y: auto; 278 | overflow-x: hidden; 279 | max-height: 200px; 280 | } 281 | .selectize-control.single .selectize-input, 282 | .selectize-control.single .selectize-input input { 283 | cursor: pointer; 284 | } 285 | .selectize-control.single .selectize-input.input-active, 286 | .selectize-control.single .selectize-input.input-active input { 287 | cursor: text; 288 | } 289 | .selectize-control.single .selectize-input:after { 290 | content: ' '; 291 | display: block; 292 | position: absolute; 293 | top: 50%; 294 | right: 15px; 295 | margin-top: -3px; 296 | width: 0; 297 | height: 0; 298 | border-style: solid; 299 | border-width: 5px 5px 0 5px; 300 | border-color: #808080 transparent transparent transparent; 301 | } 302 | .selectize-control.single .selectize-input.dropdown-active:after { 303 | margin-top: -4px; 304 | border-width: 0 5px 5px 5px; 305 | border-color: transparent transparent #808080 transparent; 306 | } 307 | .selectize-control.rtl.single .selectize-input:after { 308 | left: 15px; 309 | right: auto; 310 | } 311 | .selectize-control.rtl .selectize-input > input { 312 | margin: 0 4px 0 -2px !important; 313 | } 314 | .selectize-control .selectize-input.disabled { 315 | opacity: 0.5; 316 | background-color: #fafafa; 317 | } 318 | -------------------------------------------------------------------------------- /view/src/sass/selectize/selectize.legacy.css: -------------------------------------------------------------------------------- 1 | /** 2 | * selectize.legacy.css (v0.12.1) - Default Theme 3 | * Copyright (c) 2013–2015 Brian Reavis & contributors 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this 6 | * file except in compliance with the License. You may obtain a copy of the License at: 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under 10 | * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 11 | * ANY KIND, either express or implied. See the License for the specific language 12 | * governing permissions and limitations under the License. 13 | * 14 | * @author Brian Reavis 15 | */ 16 | .selectize-control.plugin-drag_drop.multi > .selectize-input > div.ui-sortable-placeholder { 17 | visibility: visible !important; 18 | background: #f2f2f2 !important; 19 | background: rgba(0, 0, 0, 0.06) !important; 20 | border: 0 none !important; 21 | -webkit-box-shadow: inset 0 0 12px 4px #ffffff; 22 | box-shadow: inset 0 0 12px 4px #ffffff; 23 | } 24 | .selectize-control.plugin-drag_drop .ui-sortable-placeholder::after { 25 | content: '!'; 26 | visibility: hidden; 27 | } 28 | .selectize-control.plugin-drag_drop .ui-sortable-helper { 29 | -webkit-box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2); 30 | box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2); 31 | } 32 | .selectize-dropdown-header { 33 | position: relative; 34 | padding: 7px 10px; 35 | border-bottom: 1px solid #d0d0d0; 36 | background: #f8f8f8; 37 | -webkit-border-radius: 3px 3px 0 0; 38 | -moz-border-radius: 3px 3px 0 0; 39 | border-radius: 3px 3px 0 0; 40 | } 41 | .selectize-dropdown-header-close { 42 | position: absolute; 43 | right: 10px; 44 | top: 50%; 45 | color: #303030; 46 | opacity: 0.4; 47 | margin-top: -12px; 48 | line-height: 20px; 49 | font-size: 20px !important; 50 | } 51 | .selectize-dropdown-header-close:hover { 52 | color: #000000; 53 | } 54 | .selectize-dropdown.plugin-optgroup_columns .optgroup { 55 | border-right: 1px solid #f2f2f2; 56 | border-top: 0 none; 57 | float: left; 58 | -webkit-box-sizing: border-box; 59 | -moz-box-sizing: border-box; 60 | box-sizing: border-box; 61 | } 62 | .selectize-dropdown.plugin-optgroup_columns .optgroup:last-child { 63 | border-right: 0 none; 64 | } 65 | .selectize-dropdown.plugin-optgroup_columns .optgroup:before { 66 | display: none; 67 | } 68 | .selectize-dropdown.plugin-optgroup_columns .optgroup-header { 69 | border-top: 0 none; 70 | } 71 | .selectize-control.plugin-remove_button [data-value] { 72 | position: relative; 73 | padding-right: 24px !important; 74 | } 75 | .selectize-control.plugin-remove_button [data-value] .remove { 76 | z-index: 1; 77 | /* fixes ie bug (see #392) */ 78 | position: absolute; 79 | top: 0; 80 | right: 0; 81 | bottom: 0; 82 | width: 17px; 83 | text-align: center; 84 | font-weight: bold; 85 | font-size: 12px; 86 | color: inherit; 87 | text-decoration: none; 88 | vertical-align: middle; 89 | display: inline-block; 90 | padding: 1px 0 0 0; 91 | border-left: 1px solid #74b21e; 92 | -webkit-border-radius: 0 2px 2px 0; 93 | -moz-border-radius: 0 2px 2px 0; 94 | border-radius: 0 2px 2px 0; 95 | -webkit-box-sizing: border-box; 96 | -moz-box-sizing: border-box; 97 | box-sizing: border-box; 98 | } 99 | .selectize-control.plugin-remove_button [data-value] .remove:hover { 100 | background: rgba(0, 0, 0, 0.05); 101 | } 102 | .selectize-control.plugin-remove_button [data-value].active .remove { 103 | border-left-color: #6f9839; 104 | } 105 | .selectize-control.plugin-remove_button .disabled [data-value] .remove:hover { 106 | background: none; 107 | } 108 | .selectize-control.plugin-remove_button .disabled [data-value] .remove { 109 | border-left-color: #b4b4b4; 110 | } 111 | .selectize-control { 112 | position: relative; 113 | } 114 | .selectize-dropdown, 115 | .selectize-input, 116 | .selectize-input input { 117 | color: #303030; 118 | font-family: inherit; 119 | font-size: 13px; 120 | line-height: 20px; 121 | -webkit-font-smoothing: inherit; 122 | } 123 | .selectize-input, 124 | .selectize-control.single .selectize-input.input-active { 125 | background: #ffffff; 126 | cursor: text; 127 | display: inline-block; 128 | } 129 | .selectize-input { 130 | border: 1px solid #d0d0d0; 131 | padding: 10px 10px; 132 | display: inline-block; 133 | width: 100%; 134 | overflow: hidden; 135 | position: relative; 136 | z-index: 1; 137 | -webkit-box-sizing: border-box; 138 | -moz-box-sizing: border-box; 139 | box-sizing: border-box; 140 | -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.1); 141 | box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.1); 142 | -webkit-border-radius: 3px; 143 | -moz-border-radius: 3px; 144 | border-radius: 3px; 145 | } 146 | .selectize-control.multi .selectize-input.has-items { 147 | padding: 8px 10px 4px; 148 | } 149 | .selectize-input.full { 150 | background-color: #f2f2f2; 151 | } 152 | .selectize-input.disabled, 153 | .selectize-input.disabled * { 154 | cursor: default !important; 155 | } 156 | .selectize-input.focus { 157 | -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.15); 158 | box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.15); 159 | } 160 | .selectize-input.dropdown-active { 161 | -webkit-border-radius: 3px 3px 0 0; 162 | -moz-border-radius: 3px 3px 0 0; 163 | border-radius: 3px 3px 0 0; 164 | } 165 | .selectize-input > * { 166 | vertical-align: baseline; 167 | display: -moz-inline-stack; 168 | display: inline-block; 169 | zoom: 1; 170 | *display: inline; 171 | } 172 | .selectize-control.multi .selectize-input > div { 173 | cursor: pointer; 174 | margin: 0 4px 4px 0; 175 | padding: 1px 5px; 176 | background: #b8e76f; 177 | color: #3d5d18; 178 | border: 1px solid #74b21e; 179 | } 180 | .selectize-control.multi .selectize-input > div.active { 181 | background: #92c836; 182 | color: #303030; 183 | border: 1px solid #6f9839; 184 | } 185 | .selectize-control.multi .selectize-input.disabled > div, 186 | .selectize-control.multi .selectize-input.disabled > div.active { 187 | color: #878787; 188 | background: #f8f8f8; 189 | border: 1px solid #b4b4b4; 190 | } 191 | .selectize-input > input { 192 | display: inline-block !important; 193 | padding: 0 !important; 194 | min-height: 0 !important; 195 | max-height: none !important; 196 | max-width: 100% !important; 197 | margin: 0 2px 0 0 !important; 198 | text-indent: 0 !important; 199 | border: 0 none !important; 200 | background: none !important; 201 | line-height: inherit !important; 202 | -webkit-user-select: auto !important; 203 | -webkit-box-shadow: none !important; 204 | box-shadow: none !important; 205 | } 206 | .selectize-input > input::-ms-clear { 207 | display: none; 208 | } 209 | .selectize-input > input:focus { 210 | outline: none !important; 211 | } 212 | .selectize-input::after { 213 | content: ' '; 214 | display: block; 215 | clear: left; 216 | } 217 | .selectize-input.dropdown-active::before { 218 | content: ' '; 219 | display: block; 220 | position: absolute; 221 | background: #f0f0f0; 222 | height: 1px; 223 | bottom: 0; 224 | left: 0; 225 | right: 0; 226 | } 227 | .selectize-dropdown { 228 | position: absolute; 229 | z-index: 10; 230 | border: 1px solid #d0d0d0; 231 | background: #ffffff; 232 | margin: -1px 0 0 0; 233 | border-top: 0 none; 234 | -webkit-box-sizing: border-box; 235 | -moz-box-sizing: border-box; 236 | box-sizing: border-box; 237 | -webkit-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); 238 | box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); 239 | -webkit-border-radius: 0 0 3px 3px; 240 | -moz-border-radius: 0 0 3px 3px; 241 | border-radius: 0 0 3px 3px; 242 | } 243 | .selectize-dropdown [data-selectable] { 244 | cursor: pointer; 245 | overflow: hidden; 246 | } 247 | .selectize-dropdown [data-selectable] .highlight { 248 | background: rgba(255, 237, 40, 0.4); 249 | -webkit-border-radius: 1px; 250 | -moz-border-radius: 1px; 251 | border-radius: 1px; 252 | } 253 | .selectize-dropdown [data-selectable], 254 | .selectize-dropdown .optgroup-header { 255 | padding: 7px 10px; 256 | } 257 | .selectize-dropdown .optgroup:first-child .optgroup-header { 258 | border-top: 0 none; 259 | } 260 | .selectize-dropdown .optgroup-header { 261 | color: #303030; 262 | background: #f8f8f8; 263 | cursor: default; 264 | } 265 | .selectize-dropdown .active { 266 | background-color: #fffceb; 267 | color: #303030; 268 | } 269 | .selectize-dropdown .active.create { 270 | color: #303030; 271 | } 272 | .selectize-dropdown .create { 273 | color: rgba(48, 48, 48, 0.5); 274 | } 275 | .selectize-dropdown-content { 276 | overflow-y: auto; 277 | overflow-x: hidden; 278 | max-height: 200px; 279 | } 280 | .selectize-control.single .selectize-input, 281 | .selectize-control.single .selectize-input input { 282 | cursor: pointer; 283 | } 284 | .selectize-control.single .selectize-input.input-active, 285 | .selectize-control.single .selectize-input.input-active input { 286 | cursor: text; 287 | } 288 | .selectize-control.single .selectize-input:after { 289 | content: ' '; 290 | display: block; 291 | position: absolute; 292 | top: 50%; 293 | right: 15px; 294 | margin-top: -3px; 295 | width: 0; 296 | height: 0; 297 | border-style: solid; 298 | border-width: 5px 5px 0 5px; 299 | border-color: #808080 transparent transparent transparent; 300 | } 301 | .selectize-control.single .selectize-input.dropdown-active:after { 302 | margin-top: -4px; 303 | border-width: 0 5px 5px 5px; 304 | border-color: transparent transparent #808080 transparent; 305 | } 306 | .selectize-control.rtl.single .selectize-input:after { 307 | left: 15px; 308 | right: auto; 309 | } 310 | .selectize-control.rtl .selectize-input > input { 311 | margin: 0 4px 0 -2px !important; 312 | } 313 | .selectize-control .selectize-input.disabled { 314 | opacity: 0.5; 315 | background-color: #fafafa; 316 | } 317 | .selectize-control.multi .selectize-input [data-value] { 318 | text-shadow: 0 1px 0 rgba(255, 255, 255, 0.1); 319 | -webkit-border-radius: 3px; 320 | -moz-border-radius: 3px; 321 | border-radius: 3px; 322 | background-color: #b2e567; 323 | background-image: -moz-linear-gradient(top, #b8e76f, #a9e25c); 324 | background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#b8e76f), to(#a9e25c)); 325 | background-image: -webkit-linear-gradient(top, #b8e76f, #a9e25c); 326 | background-image: -o-linear-gradient(top, #b8e76f, #a9e25c); 327 | background-image: linear-gradient(to bottom, #b8e76f, #a9e25c); 328 | background-repeat: repeat-x; 329 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffb8e76f', endColorstr='#ffa9e25c', GradientType=0); 330 | -webkit-box-shadow: 0 1px 1px rgba(0, 0, 0, 0.1); 331 | box-shadow: 0 1px 1px rgba(0, 0, 0, 0.1); 332 | } 333 | .selectize-control.multi .selectize-input [data-value].active { 334 | background-color: #88c332; 335 | background-image: -moz-linear-gradient(top, #92c836, #7abc2c); 336 | background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#92c836), to(#7abc2c)); 337 | background-image: -webkit-linear-gradient(top, #92c836, #7abc2c); 338 | background-image: -o-linear-gradient(top, #92c836, #7abc2c); 339 | background-image: linear-gradient(to bottom, #92c836, #7abc2c); 340 | background-repeat: repeat-x; 341 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff92c836', endColorstr='#ff7abc2c', GradientType=0); 342 | } 343 | .selectize-control.single .selectize-input { 344 | -webkit-box-shadow: inset 0 1px 0 rgba(255,255,255,0.8), 0 2px 0 #e0e0e0, 0 3px 0 #c8c8c8, 0 4px 1px rgba(0,0,0,0.1); 345 | box-shadow: inset 0 1px 0 rgba(255,255,255,0.8), 0 2px 0 #e0e0e0, 0 3px 0 #c8c8c8, 0 4px 1px rgba(0,0,0,0.1); 346 | background-color: #f3f3f3; 347 | background-image: -moz-linear-gradient(top, #f5f5f5, #efefef); 348 | background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#f5f5f5), to(#efefef)); 349 | background-image: -webkit-linear-gradient(top, #f5f5f5, #efefef); 350 | background-image: -o-linear-gradient(top, #f5f5f5, #efefef); 351 | background-image: linear-gradient(to bottom, #f5f5f5, #efefef); 352 | background-repeat: repeat-x; 353 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffefefef', GradientType=0); 354 | } 355 | .selectize-control.single .selectize-input, 356 | .selectize-dropdown.single { 357 | border-color: #b8b8b8; 358 | } 359 | .selectize-dropdown .optgroup-header { 360 | font-weight: bold; 361 | font-size: 0.8em; 362 | border-bottom: 1px solid #f0f0f0; 363 | border-top: 1px solid #f0f0f0; 364 | } 365 | --------------------------------------------------------------------------------